@angular/router 11.0.4 → 11.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/router-testing.umd.js +1 -1
- package/bundles/router-testing.umd.min.js +1 -1
- package/bundles/router-testing.umd.min.js.map +1 -1
- package/bundles/router-upgrade.umd.js +1 -1
- package/bundles/router-upgrade.umd.min.js +1 -1
- package/bundles/router-upgrade.umd.min.js.map +1 -1
- package/bundles/router.umd.js +465 -340
- package/bundles/router.umd.js.map +1 -1
- package/bundles/router.umd.min.js +18 -33
- package/bundles/router.umd.min.js.map +1 -1
- package/esm2015/src/apply_redirects.js +90 -117
- package/esm2015/src/operators/activate_routes.js +13 -11
- package/esm2015/src/recognize.js +126 -122
- package/esm2015/src/router.js +2 -2
- package/esm2015/src/router_module.js +1 -1
- package/esm2015/src/router_state.js +20 -2
- package/esm2015/src/url_tree.js +1 -1
- package/esm2015/src/utils/collection.js +1 -25
- package/esm2015/src/utils/config.js +10 -14
- package/esm2015/src/utils/config_matching.js +145 -0
- package/esm2015/src/version.js +1 -1
- package/fesm2015/router.js +396 -285
- package/fesm2015/router.js.map +1 -1
- package/fesm2015/testing.js +1 -1
- package/fesm2015/upgrade.js +1 -1
- package/package.json +4 -4
- package/router.d.ts +29 -3
- package/router.metadata.json +1 -1
- package/testing/testing.d.ts +1 -1
- package/testing.d.ts +1 -1
- package/upgrade/upgrade.d.ts +1 -1
- package/upgrade.d.ts +1 -1
package/fesm2015/router.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v11.0.
|
|
2
|
+
* @license Angular v11.0.8
|
|
3
3
|
* (c) 2010-2020 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Location, LocationStrategy, ViewportScroller, PlatformLocation, APP_BASE_HREF, HashLocationStrategy, PathLocationStrategy, ɵgetDOM, LOCATION_INITIALIZED } from '@angular/common';
|
|
8
8
|
import { ɵisObservable, ɵisPromise, Component, NgModuleRef, InjectionToken, NgModuleFactory, ɵConsole, NgZone, Injectable, Type, Injector, NgModuleFactoryLoader, Compiler, Directive, Attribute, Renderer2, ElementRef, Input, HostListener, HostBinding, ChangeDetectorRef, Optional, ContentChildren, EventEmitter, ViewContainerRef, ComponentFactoryResolver, Output, SystemJsNgModuleLoader, NgProbeToken, ANALYZE_FOR_ENTRY_COMPONENTS, SkipSelf, Inject, APP_INITIALIZER, APP_BOOTSTRAP_LISTENER, NgModule, ApplicationRef, Version } from '@angular/core';
|
|
9
|
-
import {
|
|
10
|
-
import { map,
|
|
9
|
+
import { from, of, BehaviorSubject, combineLatest, Observable, EmptyError, defer, EMPTY, Subject } from 'rxjs';
|
|
10
|
+
import { map, switchMap, take, startWith, scan, filter, catchError, concatMap, last as last$1, first, mergeMap, tap, concatAll, takeLast, finalize, mergeAll } from 'rxjs/operators';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @license
|
|
@@ -571,28 +571,6 @@ function forEach(map, callback) {
|
|
|
571
571
|
}
|
|
572
572
|
}
|
|
573
573
|
}
|
|
574
|
-
function waitForMap(obj, fn) {
|
|
575
|
-
if (Object.keys(obj).length === 0) {
|
|
576
|
-
return of({});
|
|
577
|
-
}
|
|
578
|
-
const waitHead = [];
|
|
579
|
-
const waitTail = [];
|
|
580
|
-
const res = {};
|
|
581
|
-
forEach(obj, (a, k) => {
|
|
582
|
-
const mapped = fn(k, a).pipe(map((r) => res[k] = r));
|
|
583
|
-
if (k === PRIMARY_OUTLET) {
|
|
584
|
-
waitHead.push(mapped);
|
|
585
|
-
}
|
|
586
|
-
else {
|
|
587
|
-
waitTail.push(mapped);
|
|
588
|
-
}
|
|
589
|
-
});
|
|
590
|
-
// Closure compiler has problem with using spread operator here. So we use "Array.concat".
|
|
591
|
-
// Note that we also need to cast the new promise because TypeScript cannot infer the type
|
|
592
|
-
// when calling the "of" function through "Function.apply"
|
|
593
|
-
return of.apply(null, waitHead.concat(waitTail))
|
|
594
|
-
.pipe(concatAll(), last$1(), map(() => res));
|
|
595
|
-
}
|
|
596
574
|
function wrapIntoObservable(value) {
|
|
597
575
|
if (ɵisObservable(value)) {
|
|
598
576
|
return value;
|
|
@@ -1480,7 +1458,25 @@ class ActivatedRouteSnapshot {
|
|
|
1480
1458
|
constructor(
|
|
1481
1459
|
/** The URL segments matched by this route */
|
|
1482
1460
|
url,
|
|
1483
|
-
/**
|
|
1461
|
+
/**
|
|
1462
|
+
* The matrix parameters scoped to this route.
|
|
1463
|
+
*
|
|
1464
|
+
* You can compute all params (or data) in the router state or to get params outside
|
|
1465
|
+
* of an activated component by traversing the `RouterState` tree as in the following
|
|
1466
|
+
* example:
|
|
1467
|
+
* ```
|
|
1468
|
+
* collectRouteParams(router: Router) {
|
|
1469
|
+
* let params = {};
|
|
1470
|
+
* let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
|
|
1471
|
+
* while (stack.length > 0) {
|
|
1472
|
+
* const route = stack.pop()!;
|
|
1473
|
+
* params = {...params, ...route.params};
|
|
1474
|
+
* stack.push(...route.children);
|
|
1475
|
+
* }
|
|
1476
|
+
* return params;
|
|
1477
|
+
* }
|
|
1478
|
+
* ```
|
|
1479
|
+
*/
|
|
1484
1480
|
params,
|
|
1485
1481
|
/** The query parameters shared by all the routes */
|
|
1486
1482
|
queryParams,
|
|
@@ -2064,16 +2060,18 @@ class ActivateRoutes {
|
|
|
2064
2060
|
}
|
|
2065
2061
|
deactivateRouteAndOutlet(route, parentContexts) {
|
|
2066
2062
|
const context = parentContexts.getContext(route.value.outlet);
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2063
|
+
// The context could be `null` if we are on a componentless route but there may still be
|
|
2064
|
+
// children that need deactivating.
|
|
2065
|
+
const contexts = context && route.value.component ? context.children : parentContexts;
|
|
2066
|
+
const children = nodeChildrenAsMap(route);
|
|
2067
|
+
for (const childOutlet of Object.keys(children)) {
|
|
2068
|
+
this.deactivateRouteAndItsChildren(children[childOutlet], contexts);
|
|
2069
|
+
}
|
|
2070
|
+
if (context && context.outlet) {
|
|
2071
|
+
// Destroy the component
|
|
2072
|
+
context.outlet.deactivate();
|
|
2073
|
+
// Destroy the contexts for all the outlets that were in the component
|
|
2074
|
+
context.children.onOutletDeactivated();
|
|
2077
2075
|
}
|
|
2078
2076
|
}
|
|
2079
2077
|
activateChildRoutes(futureNode, currNode, contexts) {
|
|
@@ -2373,23 +2371,160 @@ function standardizeConfig(r) {
|
|
|
2373
2371
|
}
|
|
2374
2372
|
return c;
|
|
2375
2373
|
}
|
|
2376
|
-
/** Returns of `Map` of outlet names to the `Route`s for that outlet. */
|
|
2377
|
-
function groupRoutesByOutlet(routes) {
|
|
2378
|
-
return routes.reduce((map, route) => {
|
|
2379
|
-
const routeOutlet = getOutlet(route);
|
|
2380
|
-
if (map.has(routeOutlet)) {
|
|
2381
|
-
map.get(routeOutlet).push(route);
|
|
2382
|
-
}
|
|
2383
|
-
else {
|
|
2384
|
-
map.set(routeOutlet, [route]);
|
|
2385
|
-
}
|
|
2386
|
-
return map;
|
|
2387
|
-
}, new Map());
|
|
2388
|
-
}
|
|
2389
2374
|
/** Returns the `route.outlet` or PRIMARY_OUTLET if none exists. */
|
|
2390
2375
|
function getOutlet(route) {
|
|
2391
2376
|
return route.outlet || PRIMARY_OUTLET;
|
|
2392
2377
|
}
|
|
2378
|
+
/**
|
|
2379
|
+
* Sorts the `routes` such that the ones with an outlet matching `outletName` come first.
|
|
2380
|
+
* The order of the configs is otherwise preserved.
|
|
2381
|
+
*/
|
|
2382
|
+
function sortByMatchingOutlets(routes, outletName) {
|
|
2383
|
+
const sortedConfig = routes.filter(r => getOutlet(r) === outletName);
|
|
2384
|
+
sortedConfig.push(...routes.filter(r => getOutlet(r) !== outletName));
|
|
2385
|
+
return sortedConfig;
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
/**
|
|
2389
|
+
* @license
|
|
2390
|
+
* Copyright Google LLC All Rights Reserved.
|
|
2391
|
+
*
|
|
2392
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
2393
|
+
* found in the LICENSE file at https://angular.io/license
|
|
2394
|
+
*/
|
|
2395
|
+
const noMatch = {
|
|
2396
|
+
matched: false,
|
|
2397
|
+
consumedSegments: [],
|
|
2398
|
+
lastChild: 0,
|
|
2399
|
+
parameters: {},
|
|
2400
|
+
positionalParamSegments: {}
|
|
2401
|
+
};
|
|
2402
|
+
function match(segmentGroup, route, segments) {
|
|
2403
|
+
var _a;
|
|
2404
|
+
if (route.path === '') {
|
|
2405
|
+
if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) {
|
|
2406
|
+
return Object.assign({}, noMatch);
|
|
2407
|
+
}
|
|
2408
|
+
return {
|
|
2409
|
+
matched: true,
|
|
2410
|
+
consumedSegments: [],
|
|
2411
|
+
lastChild: 0,
|
|
2412
|
+
parameters: {},
|
|
2413
|
+
positionalParamSegments: {}
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
const matcher = route.matcher || defaultUrlMatcher;
|
|
2417
|
+
const res = matcher(segments, segmentGroup, route);
|
|
2418
|
+
if (!res)
|
|
2419
|
+
return Object.assign({}, noMatch);
|
|
2420
|
+
const posParams = {};
|
|
2421
|
+
forEach(res.posParams, (v, k) => {
|
|
2422
|
+
posParams[k] = v.path;
|
|
2423
|
+
});
|
|
2424
|
+
const parameters = res.consumed.length > 0 ? Object.assign(Object.assign({}, posParams), res.consumed[res.consumed.length - 1].parameters) :
|
|
2425
|
+
posParams;
|
|
2426
|
+
return {
|
|
2427
|
+
matched: true,
|
|
2428
|
+
consumedSegments: res.consumed,
|
|
2429
|
+
lastChild: res.consumed.length,
|
|
2430
|
+
// TODO(atscott): investigate combining parameters and positionalParamSegments
|
|
2431
|
+
parameters,
|
|
2432
|
+
positionalParamSegments: (_a = res.posParams) !== null && _a !== void 0 ? _a : {}
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
function split(segmentGroup, consumedSegments, slicedSegments, config, relativeLinkResolution = 'corrected') {
|
|
2436
|
+
if (slicedSegments.length > 0 &&
|
|
2437
|
+
containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, config)) {
|
|
2438
|
+
const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptyPaths(segmentGroup, consumedSegments, config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
|
|
2439
|
+
s._sourceSegment = segmentGroup;
|
|
2440
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2441
|
+
return { segmentGroup: s, slicedSegments: [] };
|
|
2442
|
+
}
|
|
2443
|
+
if (slicedSegments.length === 0 &&
|
|
2444
|
+
containsEmptyPathMatches(segmentGroup, slicedSegments, config)) {
|
|
2445
|
+
const s = new UrlSegmentGroup(segmentGroup.segments, addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, config, segmentGroup.children, relativeLinkResolution));
|
|
2446
|
+
s._sourceSegment = segmentGroup;
|
|
2447
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2448
|
+
return { segmentGroup: s, slicedSegments };
|
|
2449
|
+
}
|
|
2450
|
+
const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);
|
|
2451
|
+
s._sourceSegment = segmentGroup;
|
|
2452
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2453
|
+
return { segmentGroup: s, slicedSegments };
|
|
2454
|
+
}
|
|
2455
|
+
function addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, routes, children, relativeLinkResolution) {
|
|
2456
|
+
const res = {};
|
|
2457
|
+
for (const r of routes) {
|
|
2458
|
+
if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) {
|
|
2459
|
+
const s = new UrlSegmentGroup([], {});
|
|
2460
|
+
s._sourceSegment = segmentGroup;
|
|
2461
|
+
if (relativeLinkResolution === 'legacy') {
|
|
2462
|
+
s._segmentIndexShift = segmentGroup.segments.length;
|
|
2463
|
+
}
|
|
2464
|
+
else {
|
|
2465
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2466
|
+
}
|
|
2467
|
+
res[getOutlet(r)] = s;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
return Object.assign(Object.assign({}, children), res);
|
|
2471
|
+
}
|
|
2472
|
+
function createChildrenForEmptyPaths(segmentGroup, consumedSegments, routes, primarySegment) {
|
|
2473
|
+
const res = {};
|
|
2474
|
+
res[PRIMARY_OUTLET] = primarySegment;
|
|
2475
|
+
primarySegment._sourceSegment = segmentGroup;
|
|
2476
|
+
primarySegment._segmentIndexShift = consumedSegments.length;
|
|
2477
|
+
for (const r of routes) {
|
|
2478
|
+
if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {
|
|
2479
|
+
const s = new UrlSegmentGroup([], {});
|
|
2480
|
+
s._sourceSegment = segmentGroup;
|
|
2481
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2482
|
+
res[getOutlet(r)] = s;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
return res;
|
|
2486
|
+
}
|
|
2487
|
+
function containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, routes) {
|
|
2488
|
+
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r) && getOutlet(r) !== PRIMARY_OUTLET);
|
|
2489
|
+
}
|
|
2490
|
+
function containsEmptyPathMatches(segmentGroup, slicedSegments, routes) {
|
|
2491
|
+
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r));
|
|
2492
|
+
}
|
|
2493
|
+
function emptyPathMatch(segmentGroup, slicedSegments, r) {
|
|
2494
|
+
if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full') {
|
|
2495
|
+
return false;
|
|
2496
|
+
}
|
|
2497
|
+
return r.path === '';
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* Determines if `route` is a path match for the `rawSegment`, `segments`, and `outlet` without
|
|
2501
|
+
* verifying that its children are a full match for the remainder of the `rawSegment` children as
|
|
2502
|
+
* well.
|
|
2503
|
+
*/
|
|
2504
|
+
function isImmediateMatch(route, rawSegment, segments, outlet) {
|
|
2505
|
+
// We allow matches to empty paths when the outlets differ so we can match a url like `/(b:b)` to
|
|
2506
|
+
// a config like
|
|
2507
|
+
// * `{path: '', children: [{path: 'b', outlet: 'b'}]}`
|
|
2508
|
+
// or even
|
|
2509
|
+
// * `{path: '', outlet: 'a', children: [{path: 'b', outlet: 'b'}]`
|
|
2510
|
+
//
|
|
2511
|
+
// The exception here is when the segment outlet is for the primary outlet. This would
|
|
2512
|
+
// result in a match inside the named outlet because all children there are written as primary
|
|
2513
|
+
// outlets. So we need to prevent child named outlet matches in a url like `/b` in a config like
|
|
2514
|
+
// * `{path: '', outlet: 'x' children: [{path: 'b'}]}`
|
|
2515
|
+
// This should only match if the url is `/(x:b)`.
|
|
2516
|
+
if (getOutlet(route) !== outlet &&
|
|
2517
|
+
(outlet === PRIMARY_OUTLET || !emptyPathMatch(rawSegment, segments, route))) {
|
|
2518
|
+
return false;
|
|
2519
|
+
}
|
|
2520
|
+
if (route.path === '**') {
|
|
2521
|
+
return true;
|
|
2522
|
+
}
|
|
2523
|
+
return match(rawSegment, route, segments).matched;
|
|
2524
|
+
}
|
|
2525
|
+
function noLeftoversInUrl(segmentGroup, segments, outlet) {
|
|
2526
|
+
return segments.length === 0 && !segmentGroup.children[outlet];
|
|
2527
|
+
}
|
|
2393
2528
|
|
|
2394
2529
|
/**
|
|
2395
2530
|
* @license
|
|
@@ -2408,7 +2543,7 @@ class AbsoluteRedirect {
|
|
|
2408
2543
|
this.urlTree = urlTree;
|
|
2409
2544
|
}
|
|
2410
2545
|
}
|
|
2411
|
-
function noMatch(segmentGroup) {
|
|
2546
|
+
function noMatch$1(segmentGroup) {
|
|
2412
2547
|
return new Observable((obs) => obs.error(new NoMatch(segmentGroup)));
|
|
2413
2548
|
}
|
|
2414
2549
|
function absoluteRedirect(newTree) {
|
|
@@ -2438,8 +2573,18 @@ class ApplyRedirects {
|
|
|
2438
2573
|
this.ngModule = moduleInjector.get(NgModuleRef);
|
|
2439
2574
|
}
|
|
2440
2575
|
apply() {
|
|
2441
|
-
const
|
|
2442
|
-
|
|
2576
|
+
const splitGroup = split(this.urlTree.root, [], [], this.config).segmentGroup;
|
|
2577
|
+
// TODO(atscott): creating a new segment removes the _sourceSegment _segmentIndexShift, which is
|
|
2578
|
+
// only necessary to prevent failures in tests which assert exact object matches. The `split` is
|
|
2579
|
+
// now shared between `applyRedirects` and `recognize` but only the `recognize` step needs these
|
|
2580
|
+
// properties. Before the implementations were merged, the `applyRedirects` would not assign
|
|
2581
|
+
// them. We should be able to remove this logic as a "breaking change" but should do some more
|
|
2582
|
+
// investigation into the failures first.
|
|
2583
|
+
const rootSegmentGroup = new UrlSegmentGroup(splitGroup.segments, splitGroup.children);
|
|
2584
|
+
const expanded$ = this.expandSegmentGroup(this.ngModule, this.config, rootSegmentGroup, PRIMARY_OUTLET);
|
|
2585
|
+
const urlTrees$ = expanded$.pipe(map((rootSegmentGroup) => {
|
|
2586
|
+
return this.createUrlTree(squashSegmentGroup(rootSegmentGroup), this.urlTree.queryParams, this.urlTree.fragment);
|
|
2587
|
+
}));
|
|
2443
2588
|
return urlTrees$.pipe(catchError((e) => {
|
|
2444
2589
|
if (e instanceof AbsoluteRedirect) {
|
|
2445
2590
|
// after an absolute redirect we do not apply any more redirects!
|
|
@@ -2455,7 +2600,9 @@ class ApplyRedirects {
|
|
|
2455
2600
|
}
|
|
2456
2601
|
match(tree) {
|
|
2457
2602
|
const expanded$ = this.expandSegmentGroup(this.ngModule, this.config, tree.root, PRIMARY_OUTLET);
|
|
2458
|
-
const mapped$ = expanded$.pipe(map((rootSegmentGroup) =>
|
|
2603
|
+
const mapped$ = expanded$.pipe(map((rootSegmentGroup) => {
|
|
2604
|
+
return this.createUrlTree(squashSegmentGroup(rootSegmentGroup), tree.queryParams, tree.fragment);
|
|
2605
|
+
}));
|
|
2459
2606
|
return mapped$.pipe(catchError((e) => {
|
|
2460
2607
|
if (e instanceof NoMatch) {
|
|
2461
2608
|
throw this.noMatchError(e);
|
|
@@ -2481,62 +2628,61 @@ class ApplyRedirects {
|
|
|
2481
2628
|
}
|
|
2482
2629
|
// Recursively expand segment groups for all the child outlets
|
|
2483
2630
|
expandChildren(ngModule, routes, segmentGroup) {
|
|
2484
|
-
|
|
2631
|
+
// Expand outlets one at a time, starting with the primary outlet. We need to do it this way
|
|
2632
|
+
// because an absolute redirect from the primary outlet takes precedence.
|
|
2633
|
+
const childOutlets = [];
|
|
2634
|
+
for (const child of Object.keys(segmentGroup.children)) {
|
|
2635
|
+
if (child === 'primary') {
|
|
2636
|
+
childOutlets.unshift(child);
|
|
2637
|
+
}
|
|
2638
|
+
else {
|
|
2639
|
+
childOutlets.push(child);
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
return from(childOutlets)
|
|
2643
|
+
.pipe(concatMap(childOutlet => {
|
|
2644
|
+
const child = segmentGroup.children[childOutlet];
|
|
2645
|
+
// Sort the routes so routes with outlets that match the the segment appear
|
|
2646
|
+
// first, followed by routes for other outlets, which might match if they have an
|
|
2647
|
+
// empty path.
|
|
2648
|
+
const sortedRoutes = sortByMatchingOutlets(routes, childOutlet);
|
|
2649
|
+
return this.expandSegmentGroup(ngModule, sortedRoutes, child, childOutlet)
|
|
2650
|
+
.pipe(map(s => ({ segment: s, outlet: childOutlet })));
|
|
2651
|
+
}), scan((children, expandedChild) => {
|
|
2652
|
+
children[expandedChild.outlet] = expandedChild.segment;
|
|
2653
|
+
return children;
|
|
2654
|
+
}, {}), last$1());
|
|
2485
2655
|
}
|
|
2486
2656
|
expandSegment(ngModule, segmentGroup, routes, segments, outlet, allowRedirects) {
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
routesByOutlet.set(outlet, []);
|
|
2493
|
-
}
|
|
2494
|
-
const expandRoutes = (routes) => {
|
|
2495
|
-
return from(routes).pipe(concatMap((r) => {
|
|
2496
|
-
const expanded$ = this.expandSegmentAgainstRoute(ngModule, segmentGroup, routes, r, segments, outlet, allowRedirects);
|
|
2497
|
-
return expanded$.pipe(catchError(e => {
|
|
2498
|
-
if (e instanceof NoMatch) {
|
|
2499
|
-
return of(null);
|
|
2500
|
-
}
|
|
2501
|
-
throw e;
|
|
2502
|
-
}));
|
|
2503
|
-
}), first((s) => s !== null), catchError(e => {
|
|
2504
|
-
if (e instanceof EmptyError || e.name === 'EmptyError') {
|
|
2505
|
-
if (this.noLeftoversInUrl(segmentGroup, segments, outlet)) {
|
|
2506
|
-
return of(new UrlSegmentGroup([], {}));
|
|
2507
|
-
}
|
|
2508
|
-
throw new NoMatch(segmentGroup);
|
|
2657
|
+
return from(routes).pipe(concatMap((r) => {
|
|
2658
|
+
const expanded$ = this.expandSegmentAgainstRoute(ngModule, segmentGroup, routes, r, segments, outlet, allowRedirects);
|
|
2659
|
+
return expanded$.pipe(catchError((e) => {
|
|
2660
|
+
if (e instanceof NoMatch) {
|
|
2661
|
+
return of(null);
|
|
2509
2662
|
}
|
|
2510
2663
|
throw e;
|
|
2511
2664
|
}));
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
// Return only the expansion for the route outlet we are trying to activate.
|
|
2522
|
-
map(results => results.find(result => result !== null)));
|
|
2523
|
-
}
|
|
2524
|
-
noLeftoversInUrl(segmentGroup, segments, outlet) {
|
|
2525
|
-
return segments.length === 0 && !segmentGroup.children[outlet];
|
|
2665
|
+
}), first((s) => !!s), catchError((e, _) => {
|
|
2666
|
+
if (e instanceof EmptyError || e.name === 'EmptyError') {
|
|
2667
|
+
if (noLeftoversInUrl(segmentGroup, segments, outlet)) {
|
|
2668
|
+
return of(new UrlSegmentGroup([], {}));
|
|
2669
|
+
}
|
|
2670
|
+
throw new NoMatch(segmentGroup);
|
|
2671
|
+
}
|
|
2672
|
+
throw e;
|
|
2673
|
+
}));
|
|
2526
2674
|
}
|
|
2527
2675
|
expandSegmentAgainstRoute(ngModule, segmentGroup, routes, route, paths, outlet, allowRedirects) {
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
if (getOutlet(route) !== outlet && route.path !== '') {
|
|
2531
|
-
return noMatch(segmentGroup);
|
|
2676
|
+
if (!isImmediateMatch(route, segmentGroup, paths, outlet)) {
|
|
2677
|
+
return noMatch$1(segmentGroup);
|
|
2532
2678
|
}
|
|
2533
2679
|
if (route.redirectTo === undefined) {
|
|
2534
|
-
return this.matchSegmentAgainstRoute(ngModule, segmentGroup, route, paths);
|
|
2680
|
+
return this.matchSegmentAgainstRoute(ngModule, segmentGroup, route, paths, outlet);
|
|
2535
2681
|
}
|
|
2536
2682
|
if (allowRedirects && this.allowRedirects) {
|
|
2537
2683
|
return this.expandSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, paths, outlet);
|
|
2538
2684
|
}
|
|
2539
|
-
return noMatch(segmentGroup);
|
|
2685
|
+
return noMatch$1(segmentGroup);
|
|
2540
2686
|
}
|
|
2541
2687
|
expandSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet) {
|
|
2542
2688
|
if (route.path === '**') {
|
|
@@ -2557,7 +2703,7 @@ class ApplyRedirects {
|
|
|
2557
2703
|
expandRegularSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet) {
|
|
2558
2704
|
const { matched, consumedSegments, lastChild, positionalParamSegments } = match(segmentGroup, route, segments);
|
|
2559
2705
|
if (!matched)
|
|
2560
|
-
return noMatch(segmentGroup);
|
|
2706
|
+
return noMatch$1(segmentGroup);
|
|
2561
2707
|
const newTree = this.applyRedirectCommands(consumedSegments, route.redirectTo, positionalParamSegments);
|
|
2562
2708
|
if (route.redirectTo.startsWith('/')) {
|
|
2563
2709
|
return absoluteRedirect(newTree);
|
|
@@ -2566,7 +2712,7 @@ class ApplyRedirects {
|
|
|
2566
2712
|
return this.expandSegment(ngModule, segmentGroup, routes, newSegments.concat(segments.slice(lastChild)), outlet, false);
|
|
2567
2713
|
}));
|
|
2568
2714
|
}
|
|
2569
|
-
matchSegmentAgainstRoute(ngModule, rawSegmentGroup, route, segments) {
|
|
2715
|
+
matchSegmentAgainstRoute(ngModule, rawSegmentGroup, route, segments, outlet) {
|
|
2570
2716
|
if (route.path === '**') {
|
|
2571
2717
|
if (route.loadChildren) {
|
|
2572
2718
|
return this.configLoader.load(ngModule.injector, route)
|
|
@@ -2579,13 +2725,15 @@ class ApplyRedirects {
|
|
|
2579
2725
|
}
|
|
2580
2726
|
const { matched, consumedSegments, lastChild } = match(rawSegmentGroup, route, segments);
|
|
2581
2727
|
if (!matched)
|
|
2582
|
-
return noMatch(rawSegmentGroup);
|
|
2728
|
+
return noMatch$1(rawSegmentGroup);
|
|
2583
2729
|
const rawSlicedSegments = segments.slice(lastChild);
|
|
2584
2730
|
const childConfig$ = this.getChildConfig(ngModule, route, segments);
|
|
2585
2731
|
return childConfig$.pipe(mergeMap((routerConfig) => {
|
|
2586
2732
|
const childModule = routerConfig.module;
|
|
2587
2733
|
const childConfig = routerConfig.routes;
|
|
2588
|
-
const { segmentGroup, slicedSegments } = split(rawSegmentGroup, consumedSegments, rawSlicedSegments, childConfig);
|
|
2734
|
+
const { segmentGroup: splitSegmentGroup, slicedSegments } = split(rawSegmentGroup, consumedSegments, rawSlicedSegments, childConfig);
|
|
2735
|
+
// See comment on the other call to `split` about why this is necessary.
|
|
2736
|
+
const segmentGroup = new UrlSegmentGroup(splitSegmentGroup.segments, splitSegmentGroup.children);
|
|
2589
2737
|
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
|
|
2590
2738
|
const expanded$ = this.expandChildren(childModule, childConfig, segmentGroup);
|
|
2591
2739
|
return expanded$.pipe(map((children) => new UrlSegmentGroup(consumedSegments, children)));
|
|
@@ -2593,7 +2741,8 @@ class ApplyRedirects {
|
|
|
2593
2741
|
if (childConfig.length === 0 && slicedSegments.length === 0) {
|
|
2594
2742
|
return of(new UrlSegmentGroup(consumedSegments, {}));
|
|
2595
2743
|
}
|
|
2596
|
-
const
|
|
2744
|
+
const matchedOnOutlet = getOutlet(route) === outlet;
|
|
2745
|
+
const expanded$ = this.expandSegment(childModule, segmentGroup, childConfig, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet, true);
|
|
2597
2746
|
return expanded$.pipe(map((cs) => new UrlSegmentGroup(consumedSegments.concat(cs.segments), cs.children)));
|
|
2598
2747
|
}));
|
|
2599
2748
|
}
|
|
@@ -2713,43 +2862,14 @@ class ApplyRedirects {
|
|
|
2713
2862
|
return redirectToUrlSegment;
|
|
2714
2863
|
}
|
|
2715
2864
|
}
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
const res = matcher(segments, segmentGroup, route);
|
|
2725
|
-
if (!res) {
|
|
2726
|
-
return {
|
|
2727
|
-
matched: false,
|
|
2728
|
-
consumedSegments: [],
|
|
2729
|
-
lastChild: 0,
|
|
2730
|
-
positionalParamSegments: {},
|
|
2731
|
-
};
|
|
2732
|
-
}
|
|
2733
|
-
return {
|
|
2734
|
-
matched: true,
|
|
2735
|
-
consumedSegments: res.consumed,
|
|
2736
|
-
lastChild: res.consumed.length,
|
|
2737
|
-
positionalParamSegments: res.posParams,
|
|
2738
|
-
};
|
|
2739
|
-
}
|
|
2740
|
-
function split(segmentGroup, consumedSegments, slicedSegments, config) {
|
|
2741
|
-
if (slicedSegments.length > 0 &&
|
|
2742
|
-
containsEmptyPathRedirectsWithNamedOutlets(segmentGroup, slicedSegments, config)) {
|
|
2743
|
-
const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptySegments(config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
|
|
2744
|
-
return { segmentGroup: mergeTrivialChildren(s), slicedSegments: [] };
|
|
2745
|
-
}
|
|
2746
|
-
if (slicedSegments.length === 0 &&
|
|
2747
|
-
containsEmptyPathRedirects(segmentGroup, slicedSegments, config)) {
|
|
2748
|
-
const s = new UrlSegmentGroup(segmentGroup.segments, addEmptySegmentsToChildrenIfNeeded(segmentGroup, slicedSegments, config, segmentGroup.children));
|
|
2749
|
-
return { segmentGroup: mergeTrivialChildren(s), slicedSegments };
|
|
2750
|
-
}
|
|
2751
|
-
return { segmentGroup, slicedSegments };
|
|
2752
|
-
}
|
|
2865
|
+
/**
|
|
2866
|
+
* When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
|
|
2867
|
+
*
|
|
2868
|
+
* When a segment group has only one child which is a primary outlet, merges that child into the
|
|
2869
|
+
* parent. That is, the child segment group's segments are merged into the `s` and the child's
|
|
2870
|
+
* children become the children of `s`. Think of this like a 'squash', merging the child segment
|
|
2871
|
+
* group into the parent.
|
|
2872
|
+
*/
|
|
2753
2873
|
function mergeTrivialChildren(s) {
|
|
2754
2874
|
if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
|
|
2755
2875
|
const c = s.children[PRIMARY_OUTLET];
|
|
@@ -2757,36 +2877,23 @@ function mergeTrivialChildren(s) {
|
|
|
2757
2877
|
}
|
|
2758
2878
|
return s;
|
|
2759
2879
|
}
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {
|
|
2774
|
-
res[getOutlet(r)] = new UrlSegmentGroup([], {});
|
|
2880
|
+
/**
|
|
2881
|
+
* Recursively merges primary segment children into their parents and also drops empty children
|
|
2882
|
+
* (those which have no segments and no children themselves). The latter prevents serializing a
|
|
2883
|
+
* group into something like `/a(aux:)`, where `aux` is an empty child segment.
|
|
2884
|
+
*/
|
|
2885
|
+
function squashSegmentGroup(segmentGroup) {
|
|
2886
|
+
const newChildren = {};
|
|
2887
|
+
for (const childOutlet of Object.keys(segmentGroup.children)) {
|
|
2888
|
+
const child = segmentGroup.children[childOutlet];
|
|
2889
|
+
const childCandidate = squashSegmentGroup(child);
|
|
2890
|
+
// don't add empty children
|
|
2891
|
+
if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
|
|
2892
|
+
newChildren[childOutlet] = childCandidate;
|
|
2775
2893
|
}
|
|
2776
2894
|
}
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
function containsEmptyPathRedirectsWithNamedOutlets(segmentGroup, segments, routes) {
|
|
2780
|
-
return routes.some(r => isEmptyPathRedirect(segmentGroup, segments, r) && getOutlet(r) !== PRIMARY_OUTLET);
|
|
2781
|
-
}
|
|
2782
|
-
function containsEmptyPathRedirects(segmentGroup, segments, routes) {
|
|
2783
|
-
return routes.some(r => isEmptyPathRedirect(segmentGroup, segments, r));
|
|
2784
|
-
}
|
|
2785
|
-
function isEmptyPathRedirect(segmentGroup, segments, r) {
|
|
2786
|
-
if ((segmentGroup.hasChildren() || segments.length > 0) && r.pathMatch === 'full') {
|
|
2787
|
-
return false;
|
|
2788
|
-
}
|
|
2789
|
-
return r.path === '' && r.redirectTo !== undefined;
|
|
2895
|
+
const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
|
|
2896
|
+
return mergeTrivialChildren(s);
|
|
2790
2897
|
}
|
|
2791
2898
|
|
|
2792
2899
|
/**
|
|
@@ -3103,9 +3210,26 @@ function runCanDeactivate(component, currARS, currRSS, futureRSS, moduleInjector
|
|
|
3103
3210
|
*/
|
|
3104
3211
|
class NoMatch$1 {
|
|
3105
3212
|
}
|
|
3213
|
+
function newObservableError(e) {
|
|
3214
|
+
// TODO(atscott): This pattern is used throughout the router code and can be `throwError` instead.
|
|
3215
|
+
return new Observable((obs) => obs.error(e));
|
|
3216
|
+
}
|
|
3106
3217
|
function recognize(rootComponentType, config, urlTree, url, paramsInheritanceStrategy = 'emptyOnly', relativeLinkResolution = 'legacy') {
|
|
3107
|
-
|
|
3108
|
-
|
|
3218
|
+
try {
|
|
3219
|
+
const result = new Recognizer(rootComponentType, config, urlTree, url, paramsInheritanceStrategy, relativeLinkResolution)
|
|
3220
|
+
.recognize();
|
|
3221
|
+
if (result === null) {
|
|
3222
|
+
return newObservableError(new NoMatch$1());
|
|
3223
|
+
}
|
|
3224
|
+
else {
|
|
3225
|
+
return of(result);
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
catch (e) {
|
|
3229
|
+
// Catch the potential error from recognize due to duplicate outlet matches and return as an
|
|
3230
|
+
// `Observable` error instead.
|
|
3231
|
+
return newObservableError(e);
|
|
3232
|
+
}
|
|
3109
3233
|
}
|
|
3110
3234
|
class Recognizer {
|
|
3111
3235
|
constructor(rootComponentType, config, urlTree, url, paramsInheritanceStrategy, relativeLinkResolution) {
|
|
@@ -3117,18 +3241,19 @@ class Recognizer {
|
|
|
3117
3241
|
this.relativeLinkResolution = relativeLinkResolution;
|
|
3118
3242
|
}
|
|
3119
3243
|
recognize() {
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
const routeState = new RouterStateSnapshot(this.url, rootNode);
|
|
3126
|
-
this.inheritParamsAndData(routeState._root);
|
|
3127
|
-
return of(routeState);
|
|
3128
|
-
}
|
|
3129
|
-
catch (e) {
|
|
3130
|
-
return new Observable((obs) => obs.error(e));
|
|
3244
|
+
const rootSegmentGroup = split(this.urlTree.root, [], [], this.config.filter(c => c.redirectTo === undefined), this.relativeLinkResolution)
|
|
3245
|
+
.segmentGroup;
|
|
3246
|
+
const children = this.processSegmentGroup(this.config, rootSegmentGroup, PRIMARY_OUTLET);
|
|
3247
|
+
if (children === null) {
|
|
3248
|
+
return null;
|
|
3131
3249
|
}
|
|
3250
|
+
// Use Object.freeze to prevent readers of the Router state from modifying it outside of a
|
|
3251
|
+
// navigation, resulting in the router being out of sync with the browser.
|
|
3252
|
+
const root = new ActivatedRouteSnapshot([], Object.freeze({}), Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, {}, PRIMARY_OUTLET, this.rootComponentType, null, this.urlTree.root, -1, {});
|
|
3253
|
+
const rootNode = new TreeNode(root, children);
|
|
3254
|
+
const routeState = new RouterStateSnapshot(this.url, rootNode);
|
|
3255
|
+
this.inheritParamsAndData(routeState._root);
|
|
3256
|
+
return routeState;
|
|
3132
3257
|
}
|
|
3133
3258
|
inheritParamsAndData(routeNode) {
|
|
3134
3259
|
const route = routeNode.value;
|
|
@@ -3143,58 +3268,101 @@ class Recognizer {
|
|
|
3143
3268
|
}
|
|
3144
3269
|
return this.processSegment(config, segmentGroup, segmentGroup.segments, outlet);
|
|
3145
3270
|
}
|
|
3271
|
+
/**
|
|
3272
|
+
* Matches every child outlet in the `segmentGroup` to a `Route` in the config. Returns `null` if
|
|
3273
|
+
* we cannot find a match for _any_ of the children.
|
|
3274
|
+
*
|
|
3275
|
+
* @param config - The `Routes` to match against
|
|
3276
|
+
* @param segmentGroup - The `UrlSegmentGroup` whose children need to be matched against the
|
|
3277
|
+
* config.
|
|
3278
|
+
*/
|
|
3146
3279
|
processChildren(config, segmentGroup) {
|
|
3147
|
-
const children =
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3280
|
+
const children = [];
|
|
3281
|
+
for (const childOutlet of Object.keys(segmentGroup.children)) {
|
|
3282
|
+
const child = segmentGroup.children[childOutlet];
|
|
3283
|
+
// Sort the config so that routes with outlets that match the one being activated appear
|
|
3284
|
+
// first, followed by routes for other outlets, which might match if they have an empty path.
|
|
3285
|
+
const sortedConfig = sortByMatchingOutlets(config, childOutlet);
|
|
3286
|
+
const outletChildren = this.processSegmentGroup(sortedConfig, child, childOutlet);
|
|
3287
|
+
if (outletChildren === null) {
|
|
3288
|
+
// Configs must match all segment children so because we did not find a match for this
|
|
3289
|
+
// outlet, return `null`.
|
|
3290
|
+
return null;
|
|
3291
|
+
}
|
|
3292
|
+
children.push(...outletChildren);
|
|
3293
|
+
}
|
|
3294
|
+
// Because we may have matched two outlets to the same empty path segment, we can have multiple
|
|
3295
|
+
// activated results for the same outlet. We should merge the children of these results so the
|
|
3296
|
+
// final return value is only one `TreeNode` per outlet.
|
|
3297
|
+
const mergedChildren = mergeEmptyPathMatches(children);
|
|
3298
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
3299
|
+
// This should really never happen - we are only taking the first match for each outlet and
|
|
3300
|
+
// merge the empty path matches.
|
|
3301
|
+
checkOutletNameUniqueness(mergedChildren);
|
|
3302
|
+
}
|
|
3303
|
+
sortActivatedRouteSnapshots(mergedChildren);
|
|
3304
|
+
return mergedChildren;
|
|
3151
3305
|
}
|
|
3152
3306
|
processSegment(config, segmentGroup, segments, outlet) {
|
|
3153
3307
|
for (const r of config) {
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
catch (e) {
|
|
3158
|
-
if (!(e instanceof NoMatch$1))
|
|
3159
|
-
throw e;
|
|
3308
|
+
const children = this.processSegmentAgainstRoute(r, segmentGroup, segments, outlet);
|
|
3309
|
+
if (children !== null) {
|
|
3310
|
+
return children;
|
|
3160
3311
|
}
|
|
3161
3312
|
}
|
|
3162
|
-
if (
|
|
3313
|
+
if (noLeftoversInUrl(segmentGroup, segments, outlet)) {
|
|
3163
3314
|
return [];
|
|
3164
3315
|
}
|
|
3165
|
-
|
|
3166
|
-
}
|
|
3167
|
-
noLeftoversInUrl(segmentGroup, segments, outlet) {
|
|
3168
|
-
return segments.length === 0 && !segmentGroup.children[outlet];
|
|
3316
|
+
return null;
|
|
3169
3317
|
}
|
|
3170
3318
|
processSegmentAgainstRoute(route, rawSegment, segments, outlet) {
|
|
3171
|
-
if (route.redirectTo)
|
|
3172
|
-
|
|
3173
|
-
if ((route.outlet || PRIMARY_OUTLET) !== outlet)
|
|
3174
|
-
throw new NoMatch$1();
|
|
3319
|
+
if (route.redirectTo || !isImmediateMatch(route, rawSegment, segments, outlet))
|
|
3320
|
+
return null;
|
|
3175
3321
|
let snapshot;
|
|
3176
3322
|
let consumedSegments = [];
|
|
3177
3323
|
let rawSlicedSegments = [];
|
|
3178
3324
|
if (route.path === '**') {
|
|
3179
3325
|
const params = segments.length > 0 ? last(segments).parameters : {};
|
|
3180
|
-
snapshot = new ActivatedRouteSnapshot(segments, params, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route),
|
|
3326
|
+
snapshot = new ActivatedRouteSnapshot(segments, params, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route), getOutlet(route), route.component, route, getSourceSegmentGroup(rawSegment), getPathIndexShift(rawSegment) + segments.length, getResolve(route));
|
|
3181
3327
|
}
|
|
3182
3328
|
else {
|
|
3183
|
-
const result = match
|
|
3329
|
+
const result = match(rawSegment, route, segments);
|
|
3330
|
+
if (!result.matched) {
|
|
3331
|
+
return null;
|
|
3332
|
+
}
|
|
3184
3333
|
consumedSegments = result.consumedSegments;
|
|
3185
3334
|
rawSlicedSegments = segments.slice(result.lastChild);
|
|
3186
|
-
snapshot = new ActivatedRouteSnapshot(consumedSegments, result.parameters, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route),
|
|
3335
|
+
snapshot = new ActivatedRouteSnapshot(consumedSegments, result.parameters, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route), getOutlet(route), route.component, route, getSourceSegmentGroup(rawSegment), getPathIndexShift(rawSegment) + consumedSegments.length, getResolve(route));
|
|
3187
3336
|
}
|
|
3188
3337
|
const childConfig = getChildConfig(route);
|
|
3189
|
-
const { segmentGroup, slicedSegments } = split
|
|
3338
|
+
const { segmentGroup, slicedSegments } = split(rawSegment, consumedSegments, rawSlicedSegments,
|
|
3339
|
+
// Filter out routes with redirectTo because we are trying to create activated route
|
|
3340
|
+
// snapshots and don't handle redirects here. That should have been done in
|
|
3341
|
+
// `applyRedirects`.
|
|
3342
|
+
childConfig.filter(c => c.redirectTo === undefined), this.relativeLinkResolution);
|
|
3190
3343
|
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
|
|
3191
3344
|
const children = this.processChildren(childConfig, segmentGroup);
|
|
3345
|
+
if (children === null) {
|
|
3346
|
+
return null;
|
|
3347
|
+
}
|
|
3192
3348
|
return [new TreeNode(snapshot, children)];
|
|
3193
3349
|
}
|
|
3194
3350
|
if (childConfig.length === 0 && slicedSegments.length === 0) {
|
|
3195
3351
|
return [new TreeNode(snapshot, [])];
|
|
3196
3352
|
}
|
|
3197
|
-
const
|
|
3353
|
+
const matchedOnOutlet = getOutlet(route) === outlet;
|
|
3354
|
+
// If we matched a config due to empty path match on a different outlet, we need to continue
|
|
3355
|
+
// passing the current outlet for the segment rather than switch to PRIMARY.
|
|
3356
|
+
// Note that we switch to primary when we have a match because outlet configs look like this:
|
|
3357
|
+
// {path: 'a', outlet: 'a', children: [
|
|
3358
|
+
// {path: 'b', component: B},
|
|
3359
|
+
// {path: 'c', component: C},
|
|
3360
|
+
// ]}
|
|
3361
|
+
// Notice that the children of the named outlet are configured with the primary outlet
|
|
3362
|
+
const children = this.processSegment(childConfig, segmentGroup, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet);
|
|
3363
|
+
if (children === null) {
|
|
3364
|
+
return null;
|
|
3365
|
+
}
|
|
3198
3366
|
return [new TreeNode(snapshot, children)];
|
|
3199
3367
|
}
|
|
3200
3368
|
}
|
|
@@ -3216,24 +3384,31 @@ function getChildConfig(route) {
|
|
|
3216
3384
|
}
|
|
3217
3385
|
return [];
|
|
3218
3386
|
}
|
|
3219
|
-
function
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3387
|
+
function hasEmptyPathConfig(node) {
|
|
3388
|
+
const config = node.value.routeConfig;
|
|
3389
|
+
return config && config.path === '' && config.redirectTo === undefined;
|
|
3390
|
+
}
|
|
3391
|
+
/**
|
|
3392
|
+
* Finds `TreeNode`s with matching empty path route configs and merges them into `TreeNode` with the
|
|
3393
|
+
* children from each duplicate. This is necessary because different outlets can match a single
|
|
3394
|
+
* empty path route config and the results need to then be merged.
|
|
3395
|
+
*/
|
|
3396
|
+
function mergeEmptyPathMatches(nodes) {
|
|
3397
|
+
const result = [];
|
|
3398
|
+
for (const node of nodes) {
|
|
3399
|
+
if (!hasEmptyPathConfig(node)) {
|
|
3400
|
+
result.push(node);
|
|
3401
|
+
continue;
|
|
3402
|
+
}
|
|
3403
|
+
const duplicateEmptyPathNode = result.find(resultNode => node.value.routeConfig === resultNode.value.routeConfig);
|
|
3404
|
+
if (duplicateEmptyPathNode !== undefined) {
|
|
3405
|
+
duplicateEmptyPathNode.children.push(...node.children);
|
|
3406
|
+
}
|
|
3407
|
+
else {
|
|
3408
|
+
result.push(node);
|
|
3223
3409
|
}
|
|
3224
|
-
return { consumedSegments: [], lastChild: 0, parameters: {} };
|
|
3225
3410
|
}
|
|
3226
|
-
|
|
3227
|
-
const res = matcher(segments, segmentGroup, route);
|
|
3228
|
-
if (!res)
|
|
3229
|
-
throw new NoMatch$1();
|
|
3230
|
-
const posParams = {};
|
|
3231
|
-
forEach(res.posParams, (v, k) => {
|
|
3232
|
-
posParams[k] = v.path;
|
|
3233
|
-
});
|
|
3234
|
-
const parameters = res.consumed.length > 0 ? Object.assign(Object.assign({}, posParams), res.consumed[res.consumed.length - 1].parameters) :
|
|
3235
|
-
posParams;
|
|
3236
|
-
return { consumedSegments: res.consumed, lastChild: res.consumed.length, parameters };
|
|
3411
|
+
return result;
|
|
3237
3412
|
}
|
|
3238
3413
|
function checkOutletNameUniqueness(nodes) {
|
|
3239
3414
|
const names = {};
|
|
@@ -3263,70 +3438,6 @@ function getPathIndexShift(segmentGroup) {
|
|
|
3263
3438
|
}
|
|
3264
3439
|
return res - 1;
|
|
3265
3440
|
}
|
|
3266
|
-
function split$1(segmentGroup, consumedSegments, slicedSegments, config, relativeLinkResolution) {
|
|
3267
|
-
if (slicedSegments.length > 0 &&
|
|
3268
|
-
containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, config)) {
|
|
3269
|
-
const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptyPaths(segmentGroup, consumedSegments, config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
|
|
3270
|
-
s._sourceSegment = segmentGroup;
|
|
3271
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3272
|
-
return { segmentGroup: s, slicedSegments: [] };
|
|
3273
|
-
}
|
|
3274
|
-
if (slicedSegments.length === 0 &&
|
|
3275
|
-
containsEmptyPathMatches(segmentGroup, slicedSegments, config)) {
|
|
3276
|
-
const s = new UrlSegmentGroup(segmentGroup.segments, addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, config, segmentGroup.children, relativeLinkResolution));
|
|
3277
|
-
s._sourceSegment = segmentGroup;
|
|
3278
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3279
|
-
return { segmentGroup: s, slicedSegments };
|
|
3280
|
-
}
|
|
3281
|
-
const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);
|
|
3282
|
-
s._sourceSegment = segmentGroup;
|
|
3283
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3284
|
-
return { segmentGroup: s, slicedSegments };
|
|
3285
|
-
}
|
|
3286
|
-
function addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, routes, children, relativeLinkResolution) {
|
|
3287
|
-
const res = {};
|
|
3288
|
-
for (const r of routes) {
|
|
3289
|
-
if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) {
|
|
3290
|
-
const s = new UrlSegmentGroup([], {});
|
|
3291
|
-
s._sourceSegment = segmentGroup;
|
|
3292
|
-
if (relativeLinkResolution === 'legacy') {
|
|
3293
|
-
s._segmentIndexShift = segmentGroup.segments.length;
|
|
3294
|
-
}
|
|
3295
|
-
else {
|
|
3296
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3297
|
-
}
|
|
3298
|
-
res[getOutlet(r)] = s;
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3301
|
-
return Object.assign(Object.assign({}, children), res);
|
|
3302
|
-
}
|
|
3303
|
-
function createChildrenForEmptyPaths(segmentGroup, consumedSegments, routes, primarySegment) {
|
|
3304
|
-
const res = {};
|
|
3305
|
-
res[PRIMARY_OUTLET] = primarySegment;
|
|
3306
|
-
primarySegment._sourceSegment = segmentGroup;
|
|
3307
|
-
primarySegment._segmentIndexShift = consumedSegments.length;
|
|
3308
|
-
for (const r of routes) {
|
|
3309
|
-
if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {
|
|
3310
|
-
const s = new UrlSegmentGroup([], {});
|
|
3311
|
-
s._sourceSegment = segmentGroup;
|
|
3312
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3313
|
-
res[getOutlet(r)] = s;
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3316
|
-
return res;
|
|
3317
|
-
}
|
|
3318
|
-
function containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, routes) {
|
|
3319
|
-
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r) && getOutlet(r) !== PRIMARY_OUTLET);
|
|
3320
|
-
}
|
|
3321
|
-
function containsEmptyPathMatches(segmentGroup, slicedSegments, routes) {
|
|
3322
|
-
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r));
|
|
3323
|
-
}
|
|
3324
|
-
function emptyPathMatch(segmentGroup, slicedSegments, r) {
|
|
3325
|
-
if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full') {
|
|
3326
|
-
return false;
|
|
3327
|
-
}
|
|
3328
|
-
return r.path === '' && r.redirectTo === undefined;
|
|
3329
|
-
}
|
|
3330
3441
|
function getData(route) {
|
|
3331
3442
|
return route.data || {};
|
|
3332
3443
|
}
|
|
@@ -3997,7 +4108,7 @@ class Router {
|
|
|
3997
4108
|
// navigation completes, there will be nothing in
|
|
3998
4109
|
// history.state.navigationId. This can cause sync problems with AngularJS
|
|
3999
4110
|
// sync code which looks for a value here in order to determine whether or
|
|
4000
|
-
// not to handle a given popstate event or to leave it to the
|
|
4111
|
+
// not to handle a given popstate event or to leave it to the Angular
|
|
4001
4112
|
// router.
|
|
4002
4113
|
this.resetUrlToCurrentUrlTree();
|
|
4003
4114
|
const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), `Navigation ID ${t.id} is not equal to the current navigation id ${this.navigationId}`);
|
|
@@ -5677,7 +5788,7 @@ function provideRouterInitializer() {
|
|
|
5677
5788
|
/**
|
|
5678
5789
|
* @publicApi
|
|
5679
5790
|
*/
|
|
5680
|
-
const VERSION = new Version('11.0.
|
|
5791
|
+
const VERSION = new Version('11.0.8');
|
|
5681
5792
|
|
|
5682
5793
|
/**
|
|
5683
5794
|
* @license
|