Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/core/test/bundling/router/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,9 @@
{
"name": "createUrlTreeFromSegmentGroup"
},
{
"name": "createWildcardMatchResult"
},
{
"name": "deactivateRouteAndItsChildren"
},
Expand Down
26 changes: 21 additions & 5 deletions packages/router/src/recognize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';

Expand Down Expand Up @@ -161,7 +162,7 @@ export class Recognizer {
return children;
}),
defaultIfEmpty(null as TreeNode<ActivatedRouteSnapshot>[] | 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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -266,13 +268,17 @@ export class Recognizer {
matchSegmentAgainstRoute(
injector: EnvironmentInjector, rawSegment: UrlSegmentGroup, route: Route,
segments: UrlSegment[], outlet: string): Observable<TreeNode<ActivatedRouteSnapshot>> {
const matchResult = matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer);
let matchResult: Observable<MatchResult>;

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) => {
Expand Down Expand Up @@ -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: {},
};
}
18 changes: 3 additions & 15 deletions packages/router/src/utils/config_matching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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[]) {
Expand Down Expand Up @@ -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;
}

Expand Down
11 changes: 0 additions & 11 deletions packages/router/test/recognize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down