refactor(router): Move target RouterState creation before 'blocking' stage
Some checks are pending
DevInfra / assistant_to_the_branch_manager (push) Waiting to run
CI (push) / lint (push) Waiting to run
CI (push) / devtools (push) Waiting to run
CI (push) / test (push) Waiting to run
CI (push) / integration-tests (push) Waiting to run
CI (push) / adev (push) Waiting to run
CI (push) / vscode-ng-language-service (push) Waiting to run
CI (push) / publish-snapshots (push) Waiting to run
CI (push) / zone-js (push) Waiting to run
CI (push) / adev-deploy (push) Blocked by required conditions
Update ADEV Cross Repo Docs / Update Cross Repo ADEV Docs (push) Waiting to run
Performance Tracking / list (push) Waiting to run
Performance Tracking / workflow (push) Blocked by required conditions
OpenSSF Scorecard / Scorecards analysis (push) Waiting to run

This change moves `RouterState` creation to _before_ the `afterPreactivation` step,
which is the step that pauses until bootstrap listeners are complete. It is used for
'enabled blocking' initial navigation and destructive hydration. After this stage,
activation is expected to be (more or less) synchronous.

More importantly than above (since enabled blocking and destructive hydration are
essentially deprecated), this also oves the state creation before the view transition
creation.

These are done to accomodate features in the future that would depend on the RouterState
(e.g. ones which need to know which `ActivatedRoute` instances are new and which are reused).
These features may include additional async blocks/waits, which should not happen after view
transition creation (which freezes the UI until resolved).
This commit is contained in:
Andrew Scott 2026-05-06 12:37:02 -07:00 committed by Matthew Beck (Berry)
parent e661f4d255
commit 1f287b9ba7

View file

@ -443,6 +443,7 @@ export class NavigationTransitions {
// Using switchMap so we cancel executing navigations when a new one comes in
switchMap((overallTransitionState) => {
let abortable = true;
let completedOrAborted = false;
const abortController = new AbortController();
const shouldContinueNavigation = () => {
@ -738,6 +739,20 @@ export class NavigationTransitions {
return loaders.length === 0 ? of(t) : from(Promise.all(loaders).then(() => t));
}),
switchMap((t: NavigationTransition) => {
const targetRouterState = createRouterState(
router.routeReuseStrategy,
t.targetSnapshot!,
t.currentRouterState,
);
this.currentTransition = overallTransitionState = t = {...t, targetRouterState};
this.currentNavigation.update((nav) => {
nav!.targetRouterState = targetRouterState;
return nav;
});
return of(t);
}),
switchTap(() => this.afterPreactivation()),
// TODO(atscott): Move this into the last block below.
@ -762,17 +777,7 @@ export class NavigationTransitions {
take(1),
switchMap((t: NavigationTransition) => {
const targetRouterState = createRouterState(
router.routeReuseStrategy,
t.targetSnapshot!,
t.currentRouterState,
);
this.currentTransition = overallTransitionState = t = {...t, targetRouterState};
this.currentNavigation.update((nav) => {
nav!.targetRouterState = targetRouterState;
return nav;
});
abortable = false;
this.events.next(new BeforeActivateRoutes());
const deferred = overallTransitionState.beforeActivateHandler.deferredHandle;
return deferred ? from(deferred.then(() => t)) : of(t);
@ -811,7 +816,7 @@ export class NavigationTransitions {
takeUntil(
abortSignalToObservable(abortController.signal).pipe(
// Ignore aborts if we are already completed, canceled, or are in the activation stage (we have targetRouterState)
filter(() => !completedOrAborted && !overallTransitionState.targetRouterState),
filter(() => !completedOrAborted && abortable),
tap(() => {
this.cancelNavigationTransition(
overallTransitionState,