diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 5e48e7f1e548..6dc7f0c90aed 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -1106,6 +1106,9 @@ { "name": "createUrlTreeFromSegmentGroup" }, + { + "name": "createWildcardMatchResult" + }, { "name": "deactivateRouteAndItsChildren" }, diff --git a/packages/router/src/recognize.ts b/packages/router/src/recognize.ts index c5a3854edd90..0a8bdfc87ae8 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, map, mergeMap, scan, switchMap, tap} from 'rxjs/operators'; +import {catchError, concatMap, defaultIfEmpty, first, last as rxjsLast, map, mergeMap, scan, switchMap, tap} from 'rxjs/operators'; import {AbsoluteRedirect, ApplyRedirects, canLoadFails, noMatch, NoMatch} from './apply_redirects'; import {createUrlTreeFromSnapshot} from './create_url_tree'; @@ -19,8 +19,9 @@ 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, matchWithChecks, noLeftoversInUrl, split} from './utils/config_matching'; +import {isImmediateMatch, match, MatchResult, matchWithChecks, noLeftoversInUrl, split} from './utils/config_matching'; import {TreeNode} from './utils/tree'; import {isEmptyError} from './utils/type_guards'; @@ -161,7 +162,7 @@ export class Recognizer { return children; }), defaultIfEmpty(null as TreeNode[] | null), - last(), + rxjsLast(), mergeMap(children => { if (children === null) return noMatch(segmentGroup); // Because we may have matched two outlets to the same empty path segment, we can have @@ -234,7 +235,8 @@ export class Recognizer { consumedSegments, positionalParamSegments, remainingSegments, - } = match(segmentGroup, route, segments); + } = route.path === '**' ? createWildcardMatchResult(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 @@ -266,13 +268,17 @@ export class Recognizer { matchSegmentAgainstRoute( injector: EnvironmentInjector, rawSegment: UrlSegmentGroup, route: Route, segments: UrlSegment[], outlet: string): Observable> { - const matchResult = matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer); + let matchResult: Observable; + 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) => { @@ -431,3 +437,13 @@ 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 d9543de8d9e6..338d782738f7 100644 --- a/packages/router/src/utils/config_matching.ts +++ b/packages/router/src/utils/config_matching.ts @@ -15,7 +15,6 @@ 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 { @@ -53,10 +52,6 @@ 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}; @@ -93,16 +88,6 @@ 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[]) { @@ -198,6 +183,9 @@ 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 1bf24593125d..5ff0be3093d6 100644 --- a/packages/router/test/recognize.spec.ts +++ b/packages/router/test/recognize.spec.ts @@ -653,17 +653,6 @@ 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(