Skip to content

Commit 1940280

Browse files
atscottdylhunn
authored andcommitted
fix(router): Ensure canMatch guards run on wildcard routes (#53239)
This commit makes sure that wildcard routes still run the `canMatch` guards. Fixes #49949 PR Close #53239
1 parent cb500c7 commit 1940280

File tree

4 files changed

+31
-27
lines changed

4 files changed

+31
-27
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
@@ -1106,9 +1106,6 @@
11061106
{
11071107
"name": "createUrlTreeFromSegmentGroup"
11081108
},
1109-
{
1110-
"name": "createWildcardMatchResult"
1111-
},
11121109
{
11131110
"name": "deactivateRouteAndItsChildren"
11141111
},

packages/router/src/recognize.ts

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {EnvironmentInjector, Type, ɵRuntimeError as RuntimeError} from '@angular/core';
1010
import {from, Observable, of} from 'rxjs';
11-
import {catchError, concatMap, defaultIfEmpty, first, last as rxjsLast, map, mergeMap, scan, switchMap, tap} from 'rxjs/operators';
11+
import {catchError, concatMap, defaultIfEmpty, first, last, map, mergeMap, scan, switchMap, tap} from 'rxjs/operators';
1212

1313
import {AbsoluteRedirect, ApplyRedirects, canLoadFails, noMatch, NoMatch} from './apply_redirects';
1414
import {createUrlTreeFromSnapshot} from './create_url_tree';
@@ -19,9 +19,8 @@ import {RouterConfigLoader} from './router_config_loader';
1919
import {ActivatedRouteSnapshot, getInherited, ParamsInheritanceStrategy, RouterStateSnapshot} from './router_state';
2020
import {PRIMARY_OUTLET} from './shared';
2121
import {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
22-
import {last} from './utils/collection';
2322
import {getOutlet, sortByMatchingOutlets} from './utils/config';
24-
import {isImmediateMatch, match, MatchResult, matchWithChecks, noLeftoversInUrl, split} from './utils/config_matching';
23+
import {isImmediateMatch, match, matchWithChecks, noLeftoversInUrl, split} from './utils/config_matching';
2524
import {TreeNode} from './utils/tree';
2625
import {isEmptyError} from './utils/type_guards';
2726

@@ -162,7 +161,7 @@ export class Recognizer {
162161
return children;
163162
}),
164163
defaultIfEmpty(null as TreeNode<ActivatedRouteSnapshot>[] | null),
165-
rxjsLast(),
164+
last(),
166165
mergeMap(children => {
167166
if (children === null) return noMatch(segmentGroup);
168167
// Because we may have matched two outlets to the same empty path segment, we can have
@@ -235,8 +234,7 @@ export class Recognizer {
235234
consumedSegments,
236235
positionalParamSegments,
237236
remainingSegments,
238-
} = route.path === '**' ? createWildcardMatchResult(segments) :
239-
match(segmentGroup, route, segments);
237+
} = match(segmentGroup, route, segments);
240238
if (!matched) return noMatch(segmentGroup);
241239

242240
// TODO(atscott): Move all of this under an if(ngDevMode) as a breaking change and allow stack
@@ -268,17 +266,13 @@ export class Recognizer {
268266
matchSegmentAgainstRoute(
269267
injector: EnvironmentInjector, rawSegment: UrlSegmentGroup, route: Route,
270268
segments: UrlSegment[], outlet: string): Observable<TreeNode<ActivatedRouteSnapshot>> {
271-
let matchResult: Observable<MatchResult>;
272-
269+
const matchResult = matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer);
273270
if (route.path === '**') {
274-
matchResult = of(createWildcardMatchResult(segments));
275271
// Prior versions of the route matching algorithm would stop matching at the wildcard route.
276272
// We should investigate a better strategy for any existing children. Otherwise, these
277273
// child segments are silently dropped from the navigation.
278274
// https://github.com/angular/angular/issues/40089
279275
rawSegment.children = {};
280-
} else {
281-
matchResult = matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer);
282276
}
283277

284278
return matchResult.pipe(switchMap((result) => {
@@ -437,13 +431,3 @@ function getData(route: Route): Data {
437431
function getResolve(route: Route): ResolveData {
438432
return route.resolve || {};
439433
}
440-
441-
function createWildcardMatchResult(segments: UrlSegment[]): MatchResult {
442-
return {
443-
matched: true,
444-
parameters: segments.length > 0 ? last(segments)!.parameters : {},
445-
consumedSegments: segments,
446-
remainingSegments: [],
447-
positionalParamSegments: {},
448-
};
449-
}

packages/router/src/utils/config_matching.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {runCanMatchGuards} from '../operators/check_guards';
1515
import {defaultUrlMatcher, PRIMARY_OUTLET} from '../shared';
1616
import {UrlSegment, UrlSegmentGroup, UrlSerializer} from '../url_tree';
1717

18+
import {last} from './collection';
1819
import {getOrCreateRouteInjectorIfNeeded, getOutlet} from './config';
1920

2021
export interface MatchResult {
@@ -52,6 +53,10 @@ export function matchWithChecks(
5253

5354
export function match(
5455
segmentGroup: UrlSegmentGroup, route: Route, segments: UrlSegment[]): MatchResult {
56+
if (route.path === '**') {
57+
return createWildcardMatchResult(segments);
58+
}
59+
5560
if (route.path === '') {
5661
if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) {
5762
return {...noMatch};
@@ -88,6 +93,16 @@ export function match(
8893
};
8994
}
9095

96+
function createWildcardMatchResult(segments: UrlSegment[]): MatchResult {
97+
return {
98+
matched: true,
99+
parameters: segments.length > 0 ? last(segments)!.parameters : {},
100+
consumedSegments: segments,
101+
remainingSegments: [],
102+
positionalParamSegments: {},
103+
};
104+
}
105+
91106
export function split(
92107
segmentGroup: UrlSegmentGroup, consumedSegments: UrlSegment[], slicedSegments: UrlSegment[],
93108
config: Route[]) {
@@ -183,9 +198,6 @@ export function isImmediateMatch(
183198
(outlet === PRIMARY_OUTLET || !emptyPathMatch(rawSegment, segments, route))) {
184199
return false;
185200
}
186-
if (route.path === '**') {
187-
return true;
188-
}
189201
return match(rawSegment, route, segments).matched;
190202
}
191203

packages/router/test/recognize.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,17 @@ describe('recognize', async () => {
653653
expect(s.root.fragment).toEqual('f1');
654654
});
655655
});
656+
657+
describe('guards', () => {
658+
it('should run canMatch guards on wildcard routes', async () => {
659+
const config = [
660+
{path: '**', component: ComponentA, data: {id: 'a'}, canMatch: [() => false]},
661+
{path: '**', component: ComponentB, data: {id: 'b'}}
662+
];
663+
const s = await recognize(config, 'a');
664+
expect(s.root.firstChild!.data['id']).toEqual('b');
665+
});
666+
});
656667
});
657668

658669
async function recognize(

0 commit comments

Comments
 (0)