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

Function createTransactionalSnapshot

packages/router/src/router_resource.ts:101–175  ·  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

99 * (freezing during navigation and rollback recovery).
100 */
101function createTransactionalSnapshot<T>(
102 source: Resource<T>,
103 router: Router,
104 injector: Injector,
105): {
106 snapshot: Signal<ResourceSnapshot<T>>;
107 frozenSnapshot: Signal<ResourceSnapshot<T> | null>;
108} {
109 // Holds a snapshot of the resource to keep the UI masked (frozen) during pending navigations
110 // or while recovering from a cancelled navigation.
111 const frozenSnapshot = signal<ResourceSnapshot<T> | null>(null);
112
113 // Tracks whether we are in a recovery phase after a cancelled navigation.
114 // The intended behavior is that on cancellation, the router reverts to the previous state.
115 // This reversion might trigger a new load of the previous state because the signal dependencies
116 // changed. If we were to release the frozen resource state immediately, the user would see a loading state
117 // for data they were just looking at. To avoid this "loading flash", we retain the frozen
118 // value (via frozenSnapshot) during this recovery load/reload until the resource settles.
119 const isRollbackRecoveryPending = signal(false);
120
121 const sub = router.events.subscribe((e) => {
122 if (e instanceof NavigationStart) {
123 isRollbackRecoveryPending.set(false);
124
125 if (frozenSnapshot() === null) {
126 // Freeze the snapshot at the start of navigation to keep the UI stable.
127 frozenSnapshot.set(source.snapshot());
128 }
129 } else if (e instanceof NavigationEnd) {
130 // Navigation succeeded, so we can unfreeze and use the live state.
131 frozenSnapshot.set(null);
132 isRollbackRecoveryPending.set(false);
133 } else if (e instanceof NavigationSkipped) {
134 // If a navigation is skipped while we have a frozen snapshot (e.g. navigating to the
135 // current URL to cancel an in-flight navigation), the in-flight navigation is aborted
136 // and parameter rollback begins. We must maintain the frozen snapshot until the rollback
137 // recovery load completes to prevent flashing a loading state.
138 if (frozenSnapshot() !== null) {
139 isRollbackRecoveryPending.set(true);
140 }
141 } else if (e instanceof NavigationCancel || e instanceof NavigationError) {
142 const isRollback =
143 e instanceof NavigationError ||
144 (e instanceof NavigationCancel &&
145 e.code !== NavigationCancellationCode.SupersededByNewNavigation &&
146 e.code !== NavigationCancellationCode.Redirect);
147
148 if (!isRollback) return;
149
150 isRollbackRecoveryPending.set(true);
151 }
152 });
153
154 injector.get(DestroyRef).onDestroy(() => sub.unsubscribe());
155
156 effect(
157 () => {
158 if (

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