Skip to content

Commit 91486aa

Browse files
leonelvscAndrewKushnir
authored andcommitted
fix(router): Resolvers in different parts of the route tree should be able to execute together (#52934)
The following commit accidentally broken execution of resolvers when two resolvers appear in different parts of the tree and do not share a 3278966 This happens when there are secondary routes. This test ensures that all routes with resolves are run. fixes #52892 PR Close #52934
1 parent 07920d9 commit 91486aa

File tree

3 files changed

+54
-15
lines changed

3 files changed

+54
-15
lines changed

packages/core/test/bundling/router/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,9 +1877,6 @@
18771877
{
18781878
"name": "resetPreOrderHookFlags"
18791879
},
1880-
{
1881-
"name": "resolveData"
1882-
},
18831880
{
18841881
"name": "resolveForwardRef"
18851882
},

packages/router/src/operators/resolve_data.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,25 @@ export function resolveData(
2828
if (!canActivateChecks.length) {
2929
return of(t);
3030
}
31-
const routesWithResolversToRun = canActivateChecks.map(check => check.route);
32-
const routesWithResolversSet = new Set(routesWithResolversToRun);
33-
const routesNeedingDataUpdates =
34-
// List all ActivatedRoutes in an array, starting from the parent of the first route to run
35-
// resolvers. We go from the parent because the first route might have siblings that also
36-
// run resolvers.
37-
flattenRouteTree(routesWithResolversToRun[0].parent!)
38-
// Remove the parent from the list -- we do not need to recompute its inherited data
39-
// because no resolvers at or above it run.
40-
.slice(1);
31+
// Iterating a Set in javascript happens in insertion order so it is safe to use a `Set` to
32+
// preserve the correct order that the resolvers should run in.
33+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#description
34+
const routesWithResolversToRun = new Set(canActivateChecks.map(check => check.route));
35+
const routesNeedingDataUpdates = new Set<ActivatedRouteSnapshot>();
36+
for (const route of routesWithResolversToRun) {
37+
if (routesNeedingDataUpdates.has(route)) {
38+
continue;
39+
}
40+
// All children under the route with a resolver to run need to recompute inherited data.
41+
for (const newRoute of flattenRouteTree(route)) {
42+
routesNeedingDataUpdates.add(newRoute);
43+
}
44+
}
4145
let routesProcessed = 0;
4246
return from(routesNeedingDataUpdates)
4347
.pipe(
4448
concatMap(route => {
45-
if (routesWithResolversSet.has(route)) {
49+
if (routesWithResolversToRun.has(route)) {
4650
return runResolve(route, targetSnapshot!, paramsInheritanceStrategy, injector);
4751
} else {
4852
route.data = getInherited(route, route.parent, paramsInheritanceStrategy).resolve;
@@ -51,7 +55,7 @@ export function resolveData(
5155
}),
5256
tap(() => routesProcessed++),
5357
takeLast(1),
54-
mergeMap(_ => routesProcessed === routesNeedingDataUpdates.length ? of(t) : EMPTY),
58+
mergeMap(_ => routesProcessed === routesNeedingDataUpdates.size ? of(t) : EMPTY),
5559
);
5660
});
5761
}

packages/router/test/operators/resolve_data.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,44 @@ describe('resolveData operator', () => {
4646
expect(TestBed.inject(Router).url).toEqual('/');
4747
});
4848

49+
it('should run resolvers in different parts of the tree', async () => {
50+
let value = 0;
51+
let bValue = 0;
52+
TestBed.configureTestingModule({
53+
providers: [provideRouter([
54+
{
55+
path: 'a',
56+
runGuardsAndResolvers: () => false,
57+
children: [{
58+
path: '',
59+
resolve: {d0: () => ++value},
60+
runGuardsAndResolvers: 'always',
61+
children: [],
62+
}],
63+
},
64+
{
65+
path: 'b',
66+
outlet: 'aux',
67+
runGuardsAndResolvers: () => false,
68+
children: [{
69+
path: '',
70+
resolve: {d1: () => ++bValue},
71+
runGuardsAndResolvers: 'always',
72+
children: [],
73+
}]
74+
},
75+
])]
76+
});
77+
const router = TestBed.inject(Router);
78+
const harness = await RouterTestingHarness.create('/a(aux:b)');
79+
expect(router.routerState.root.children[0]?.firstChild?.snapshot.data).toEqual({d0: 1});
80+
expect(router.routerState.root.children[1]?.firstChild?.snapshot.data).toEqual({d1: 1});
81+
82+
await harness.navigateByUrl('/a(aux:b)#new');
83+
expect(router.routerState.root.children[0]?.firstChild?.snapshot.data).toEqual({d0: 2});
84+
expect(router.routerState.root.children[1]?.firstChild?.snapshot.data).toEqual({d1: 2});
85+
});
86+
4987
it('should update children inherited data when resolvers run', async () => {
5088
let value = 0;
5189
TestBed.configureTestingModule({

0 commit comments

Comments
 (0)