MCPcopy Create free account
hub / github.com/angular/angular / createTransactionalSnapshot

Function createTransactionalSnapshot

packages/router/src/router_resource.ts:74–135  ·  view source on GitHub ↗

* Creates a signal that tracks the resource snapshot and handles transactional behavior * (freezing during navigation and rollback recovery).

(
  source: Resource<T>,
  router: Router,
  injector: Injector,
)

Source from the content-addressed store, hash-verified

72 * (freezing during navigation and rollback recovery).
73 */
74function createTransactionalSnapshot<T>(
75 source: Resource<T>,
76 router: Router,
77 injector: Injector,
78): {
79 snapshot: Signal<ResourceSnapshot<T>>;
80 frozenSnapshot: Signal<ResourceSnapshot<T> | null>;
81} {
82 // Holds a snapshot of the resource to keep the UI masked (frozen) during pending navigations
83 // or while recovering from a cancelled navigation.
84 const frozenSnapshot = signal<ResourceSnapshot<T> | null>(null);
85
86 // Tracks whether we are in a recovery phase after a cancelled navigation.
87 // The intended behavior is that on cancellation, the router reverts to the previous state.
88 // This reversion might trigger a new load of the previous state because the signal dependencies
89 // changed. If we were to release the frozen resource state immediately, the user would see a loading state
90 // for data they were just looking at. To avoid this "loading flash", we retain the frozen
91 // value (via frozenSnapshot) during this recovery load/reload until the resource settles.
92 const isRollbackRecoveryPending = signal(false);
93
94 const sub = router.events.subscribe((e) => {
95 if (e instanceof NavigationStart) {
96 isRollbackRecoveryPending.set(false);
97
98 if (frozenSnapshot() === null) {
99 // Freeze the snapshot at the start of navigation to keep the UI stable.
100 frozenSnapshot.set(source.snapshot());
101 }
102 } else if (e instanceof NavigationEnd || e instanceof NavigationSkipped) {
103 // Navigation succeeded or was skipped, so we can unfreeze and use the live state.
104 frozenSnapshot.set(null);
105 isRollbackRecoveryPending.set(false);
106 } else if (e instanceof NavigationCancel || e instanceof NavigationError) {
107 const isRollback =
108 e instanceof NavigationError ||
109 (e instanceof NavigationCancel &&
110 e.code !== NavigationCancellationCode.SupersededByNewNavigation &&
111 e.code !== NavigationCancellationCode.Redirect);
112
113 if (!isRollback) return;
114
115 isRollbackRecoveryPending.set(true);
116 }
117 });
118
119 injector.get(DestroyRef).onDestroy(() => sub.unsubscribe());
120
121 effect(
122 () => {
123 if (isRollbackRecoveryPending() && !source.isLoading()) {
124 isRollbackRecoveryPending.set(false);
125 frozenSnapshot.set(null);
126 }
127 },
128 {injector},
129 );
130
131 return {

Callers 1

routerResourceFunction · 0.85

Calls 9

signalFunction · 0.90
effectFunction · 0.90
computedFunction · 0.90
snapshotMethod · 0.80
subscribeMethod · 0.65
setMethod · 0.65
onDestroyMethod · 0.65
getMethod · 0.65
unsubscribeMethod · 0.65

Tested by

no test coverage detected