diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 6dc7f0c90ae..5e48e7f1e54 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -1106,9 +1106,6 @@ { "name": "createUrlTreeFromSegmentGroup" }, - { - "name": "createWildcardMatchResult" - }, { "name": "deactivateRouteAndItsChildren" }, diff --git a/packages/router/src/recognize.ts b/packages/router/src/recognize.ts index 0a8bdfc87ae..c5a3854edd9 100644 --- a/packages/router/src/recognize.ts +++ b/packages/router/src/recognize.ts @@ -8,7 +8,7 @@ import {EnvironmentInjector, Type, ɵRuntimeError as RuntimeError} from '@angular/core'; import {from, Observable, of} from 'rxjs'; -import {catchError, concatMap, defaultIfEmpty, first, last as rxjsLast, map, mergeMap, scan, switchMap, tap} from 'rxjs/operators'; +import {catchError, concatMap, defaultIfEmpty, first, last, map, mergeMap, scan, switchMap, tap} from 'rxjs/operators'; import {AbsoluteRedirect, ApplyRedirects, canLoadFails, noMatch, NoMatch} from './apply_redirects'; import {createUrlTreeFromSnapshot} from './create_url_tree'; @@ -19,9 +19,8 @@ import {RouterConfigLoader} from './router_config_loader'; import {ActivatedRouteSnapshot, getInherited, ParamsInheritanceStrategy, RouterStateSnapshot} from './router_state'; import {PRIMARY_OUTLET} from './shared'; import {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree'; -import {last} from './utils/collection'; import {getOutlet, sortByMatchingOutlets} from './utils/config'; -import {isImmediateMatch, match, MatchResult, matchWithChecks, noLeftoversInUrl, split} from './utils/config_matching'; +import {isImmediateMatch, match, matchWithChecks, noLeftoversInUrl, split} from './utils/config_matching'; import {TreeNode} from './utils/tree'; import {isEmptyError} from './utils/type_guards'; @@ -162,7 +161,7 @@ export class Recognizer { return children; }), defaultIfEmpty(null as TreeNode[] | null), - rxjsLast(), + last(), mergeMap(children => { if (children === null) return noMatch(segmentGroup); // Because we may have matched two outlets to the same empty path segment, we can have @@ -235,8 +234,7 @@ export class Recognizer { consumedSegments, positionalParamSegments, remainingSegments, - } = route.path === '**' ? createWildcardMatchResult(segments) : - match(segmentGroup, route, segments); + } = match(segmentGroup, route, segments); if (!matched) return noMatch(segmentGroup); // TODO(atscott): Move all of this under an if(ngDevMode) as a breaking change and allow stack @@ -268,17 +266,13 @@ export class Recognizer { matchSegmentAgainstRoute( injector: EnvironmentInjector, rawSegment: UrlSegmentGroup, route: Route, segments: UrlSegment[], outlet: string): Observable> { - let matchResult: Observable; - + const matchResult = matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer); if (route.path === '**') { - matchResult = of(createWildcardMatchResult(segments)); // Prior versions of the route matching algorithm would stop matching at the wildcard route. // We should investigate a better strategy for any existing children. Otherwise, these // child segments are silently dropped from the navigation. // https://github.com/angular/angular/issues/40089 rawSegment.children = {}; - } else { - matchResult = matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer); } return matchResult.pipe(switchMap((result) => { @@ -437,13 +431,3 @@ function getData(route: Route): Data { function getResolve(route: Route): ResolveData { return route.resolve || {}; } - -function createWildcardMatchResult(segments: UrlSegment[]): MatchResult { - return { - matched: true, - parameters: segments.length > 0 ? last(segments)!.parameters : {}, - consumedSegments: segments, - remainingSegments: [], - positionalParamSegments: {}, - }; -} diff --git a/packages/router/src/utils/config_matching.ts b/packages/router/src/utils/config_matching.ts index 338d782738f..d9543de8d9e 100644 --- a/packages/router/src/utils/config_matching.ts +++ b/packages/router/src/utils/config_matching.ts @@ -15,6 +15,7 @@ import {runCanMatchGuards} from '../operators/check_guards'; import {defaultUrlMatcher, PRIMARY_OUTLET} from '../shared'; import {UrlSegment, UrlSegmentGroup, UrlSerializer} from '../url_tree'; +import {last} from './collection'; import {getOrCreateRouteInjectorIfNeeded, getOutlet} from './config'; export interface MatchResult { @@ -52,6 +53,10 @@ export function matchWithChecks( export function match( segmentGroup: UrlSegmentGroup, route: Route, segments: UrlSegment[]): MatchResult { + if (route.path === '**') { + return createWildcardMatchResult(segments); + } + if (route.path === '') { if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) { return {...noMatch}; @@ -88,6 +93,16 @@ export function match( }; } +function createWildcardMatchResult(segments: UrlSegment[]): MatchResult { + return { + matched: true, + parameters: segments.length > 0 ? last(segments)!.parameters : {}, + consumedSegments: segments, + remainingSegments: [], + positionalParamSegments: {}, + }; +} + export function split( segmentGroup: UrlSegmentGroup, consumedSegments: UrlSegment[], slicedSegments: UrlSegment[], config: Route[]) { @@ -183,9 +198,6 @@ export function isImmediateMatch( (outlet === PRIMARY_OUTLET || !emptyPathMatch(rawSegment, segments, route))) { return false; } - if (route.path === '**') { - return true; - } return match(rawSegment, route, segments).matched; } diff --git a/packages/router/test/recognize.spec.ts b/packages/router/test/recognize.spec.ts index 5ff0be3093d..1bf24593125 100644 --- a/packages/router/test/recognize.spec.ts +++ b/packages/router/test/recognize.spec.ts @@ -653,6 +653,17 @@ describe('recognize', async () => { expect(s.root.fragment).toEqual('f1'); }); }); + + describe('guards', () => { + it('should run canMatch guards on wildcard routes', async () => { + const config = [ + {path: '**', component: ComponentA, data: {id: 'a'}, canMatch: [() => false]}, + {path: '**', component: ComponentB, data: {id: 'b'}} + ]; + const s = await recognize(config, 'a'); + expect(s.root.firstChild!.data['id']).toEqual('b'); + }); + }); }); async function recognize(