@angular/router 14.0.1 → 14.1.0-next.0
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/esm2020/src/apply_redirects.mjs +3 -38
- package/esm2020/src/components/empty_outlet.mjs +3 -3
- package/esm2020/src/create_url_tree.mjs +127 -4
- package/esm2020/src/directives/router_link.mjs +6 -6
- package/esm2020/src/directives/router_link_active.mjs +3 -3
- package/esm2020/src/directives/router_outlet.mjs +3 -3
- package/esm2020/src/index.mjs +2 -1
- package/esm2020/src/page_title_strategy.mjs +3 -3
- package/esm2020/src/router.mjs +3 -3
- package/esm2020/src/router_config_loader.mjs +3 -3
- package/esm2020/src/router_module.mjs +7 -7
- package/esm2020/src/router_preloader.mjs +3 -3
- package/esm2020/src/router_scroller.mjs +3 -3
- package/esm2020/src/url_tree.mjs +39 -1
- package/esm2020/src/version.mjs +1 -1
- package/esm2020/testing/src/router_testing_module.mjs +4 -4
- package/fesm2015/router.mjs +1931 -1805
- package/fesm2015/router.mjs.map +1 -1
- package/fesm2015/testing.mjs +5 -5
- package/fesm2015/upgrade.mjs +1 -1
- package/fesm2020/router.mjs +1926 -1800
- package/fesm2020/router.mjs.map +1 -1
- package/fesm2020/testing.mjs +5 -5
- package/fesm2020/upgrade.mjs +1 -1
- package/index.d.ts +54 -1
- package/package.json +4 -4
- package/testing/index.d.ts +1 -1
- package/upgrade/index.d.ts +1 -1
package/fesm2020/router.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v14.0.
|
|
2
|
+
* @license Angular v14.1.0-next.0
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import * as i3 from '@angular/common';
|
|
8
|
-
import { Location, LocationStrategy, PlatformLocation, APP_BASE_HREF, ViewportScroller, HashLocationStrategy, PathLocationStrategy, LOCATION_INITIALIZED } from '@angular/common';
|
|
9
7
|
import * as i0 from '@angular/core';
|
|
10
8
|
import { ɵisObservable, ɵisPromise, EventEmitter, Directive, Attribute, Output, Component, createEnvironmentInjector, ɵisStandalone, ComponentFactoryResolver, InjectionToken, InjectFlags, NgModuleFactory, Injectable, NgModuleRef, ɵConsole, NgZone, ɵcoerceToBoolean, Input, HostListener, HostBinding, Optional, ContentChildren, Injector, Compiler, NgProbeToken, ANALYZE_FOR_ENTRY_COMPONENTS, SkipSelf, Inject, APP_INITIALIZER, APP_BOOTSTRAP_LISTENER, NgModule, ApplicationRef, Version } from '@angular/core';
|
|
11
9
|
import { from, of, BehaviorSubject, combineLatest, throwError, EmptyError, concat, defer, Observable, EMPTY, ConnectableObservable, Subject } from 'rxjs';
|
|
10
|
+
import * as i3 from '@angular/common';
|
|
11
|
+
import { Location, LocationStrategy, PlatformLocation, APP_BASE_HREF, ViewportScroller, HashLocationStrategy, PathLocationStrategy, LOCATION_INITIALIZED } from '@angular/common';
|
|
12
12
|
import { map, switchMap, take, startWith, scan, filter, catchError, concatMap, last as last$1, first, mergeMap, tap, takeLast, mapTo, finalize, refCount, defaultIfEmpty, mergeAll } from 'rxjs/operators';
|
|
13
13
|
import * as i1 from '@angular/platform-browser';
|
|
14
14
|
|
|
@@ -20,627 +20,782 @@ import * as i1 from '@angular/platform-browser';
|
|
|
20
20
|
* found in the LICENSE file at https://angular.io/license
|
|
21
21
|
*/
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
* route. Fired one time for any given navigation.
|
|
25
|
-
*
|
|
26
|
-
* The following code shows how a class subscribes to router events.
|
|
27
|
-
*
|
|
28
|
-
* ```ts
|
|
29
|
-
* import {Event, RouterEvent, Router} from '@angular/router';
|
|
30
|
-
*
|
|
31
|
-
* class MyService {
|
|
32
|
-
* constructor(public router: Router) {
|
|
33
|
-
* router.events.pipe(
|
|
34
|
-
* filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
|
|
35
|
-
* ).subscribe((e: RouterEvent) => {
|
|
36
|
-
* // Do something
|
|
37
|
-
* });
|
|
38
|
-
* }
|
|
39
|
-
* }
|
|
40
|
-
* ```
|
|
23
|
+
* The primary routing outlet.
|
|
41
24
|
*
|
|
42
|
-
* @see `Event`
|
|
43
|
-
* @see [Router events summary](guide/router-reference#router-events)
|
|
44
25
|
* @publicApi
|
|
45
26
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.
|
|
53
|
-
|
|
27
|
+
const PRIMARY_OUTLET = 'primary';
|
|
28
|
+
class ParamsAsMap {
|
|
29
|
+
constructor(params) {
|
|
30
|
+
this.params = params || {};
|
|
31
|
+
}
|
|
32
|
+
has(name) {
|
|
33
|
+
return Object.prototype.hasOwnProperty.call(this.params, name);
|
|
34
|
+
}
|
|
35
|
+
get(name) {
|
|
36
|
+
if (this.has(name)) {
|
|
37
|
+
const v = this.params[name];
|
|
38
|
+
return Array.isArray(v) ? v[0] : v;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
getAll(name) {
|
|
43
|
+
if (this.has(name)) {
|
|
44
|
+
const v = this.params[name];
|
|
45
|
+
return Array.isArray(v) ? v : [v];
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
get keys() {
|
|
50
|
+
return Object.keys(this.params);
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
53
|
/**
|
|
57
|
-
*
|
|
54
|
+
* Converts a `Params` instance to a `ParamMap`.
|
|
55
|
+
* @param params The instance to convert.
|
|
56
|
+
* @returns The new map instance.
|
|
58
57
|
*
|
|
59
58
|
* @publicApi
|
|
60
59
|
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
function convertToParamMap(params) {
|
|
61
|
+
return new ParamsAsMap(params);
|
|
62
|
+
}
|
|
63
|
+
const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
|
|
64
|
+
function navigationCancelingError(message) {
|
|
65
|
+
const error = Error('NavigationCancelingError: ' + message);
|
|
66
|
+
error[NAVIGATION_CANCELING_ERROR] = true;
|
|
67
|
+
return error;
|
|
68
|
+
}
|
|
69
|
+
function isNavigationCancelingError(error) {
|
|
70
|
+
return error && error[NAVIGATION_CANCELING_ERROR];
|
|
71
|
+
}
|
|
72
|
+
// Matches the route configuration (`route`) against the actual URL (`segments`).
|
|
73
|
+
function defaultUrlMatcher(segments, segmentGroup, route) {
|
|
74
|
+
const parts = route.path.split('/');
|
|
75
|
+
if (parts.length > segments.length) {
|
|
76
|
+
// The actual URL is shorter than the config, no match
|
|
77
|
+
return null;
|
|
75
78
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
if (route.pathMatch === 'full' &&
|
|
80
|
+
(segmentGroup.hasChildren() || parts.length < segments.length)) {
|
|
81
|
+
// The config is longer than the actual URL but we are looking for a full match, return null
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const posParams = {};
|
|
85
|
+
// Check each config part against the actual URL
|
|
86
|
+
for (let index = 0; index < parts.length; index++) {
|
|
87
|
+
const part = parts[index];
|
|
88
|
+
const segment = segments[index];
|
|
89
|
+
const isParameter = part.startsWith(':');
|
|
90
|
+
if (isParameter) {
|
|
91
|
+
posParams[part.substring(1)] = segment;
|
|
92
|
+
}
|
|
93
|
+
else if (part !== segment.path) {
|
|
94
|
+
// The actual URL part does not match the config, no match
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
79
97
|
}
|
|
98
|
+
return { consumed: segments.slice(0, parts.length), posParams };
|
|
80
99
|
}
|
|
100
|
+
|
|
81
101
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* @see `NavigationStart`
|
|
85
|
-
* @see `NavigationCancel`
|
|
86
|
-
* @see `NavigationError`
|
|
102
|
+
* @license
|
|
103
|
+
* Copyright Google LLC All Rights Reserved.
|
|
87
104
|
*
|
|
88
|
-
*
|
|
105
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
106
|
+
* found in the LICENSE file at https://angular.io/license
|
|
89
107
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
/** @docsNotRequired */
|
|
97
|
-
urlAfterRedirects) {
|
|
98
|
-
super(id, url);
|
|
99
|
-
this.urlAfterRedirects = urlAfterRedirects;
|
|
100
|
-
this.type = 1 /* EventType.NavigationEnd */;
|
|
108
|
+
function shallowEqualArrays(a, b) {
|
|
109
|
+
if (a.length !== b.length)
|
|
110
|
+
return false;
|
|
111
|
+
for (let i = 0; i < a.length; ++i) {
|
|
112
|
+
if (!shallowEqual(a[i], b[i]))
|
|
113
|
+
return false;
|
|
101
114
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
function shallowEqual(a, b) {
|
|
118
|
+
// While `undefined` should never be possible, it would sometimes be the case in IE 11
|
|
119
|
+
// and pre-chromium Edge. The check below accounts for this edge case.
|
|
120
|
+
const k1 = a ? Object.keys(a) : undefined;
|
|
121
|
+
const k2 = b ? Object.keys(b) : undefined;
|
|
122
|
+
if (!k1 || !k2 || k1.length != k2.length) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
let key;
|
|
126
|
+
for (let i = 0; i < k1.length; i++) {
|
|
127
|
+
key = k1[i];
|
|
128
|
+
if (!equalArraysOrString(a[key], b[key])) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
105
131
|
}
|
|
132
|
+
return true;
|
|
106
133
|
}
|
|
107
134
|
/**
|
|
108
|
-
*
|
|
109
|
-
* This can happen for several reasons including when a route guard
|
|
110
|
-
* returns `false` or initiates a redirect by returning a `UrlTree`.
|
|
111
|
-
*
|
|
112
|
-
* @see `NavigationStart`
|
|
113
|
-
* @see `NavigationEnd`
|
|
114
|
-
* @see `NavigationError`
|
|
115
|
-
*
|
|
116
|
-
* @publicApi
|
|
135
|
+
* Test equality for arrays of strings or a string.
|
|
117
136
|
*/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
reason) {
|
|
126
|
-
super(id, url);
|
|
127
|
-
this.reason = reason;
|
|
128
|
-
this.type = 2 /* EventType.NavigationCancel */;
|
|
137
|
+
function equalArraysOrString(a, b) {
|
|
138
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
139
|
+
if (a.length !== b.length)
|
|
140
|
+
return false;
|
|
141
|
+
const aSorted = [...a].sort();
|
|
142
|
+
const bSorted = [...b].sort();
|
|
143
|
+
return aSorted.every((val, index) => bSorted[index] === val);
|
|
129
144
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
|
|
145
|
+
else {
|
|
146
|
+
return a === b;
|
|
133
147
|
}
|
|
134
148
|
}
|
|
135
149
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* @see `NavigationStart`
|
|
139
|
-
* @see `NavigationEnd`
|
|
140
|
-
* @see `NavigationCancel`
|
|
141
|
-
*
|
|
142
|
-
* @publicApi
|
|
150
|
+
* Flattens single-level nested arrays.
|
|
143
151
|
*/
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
/** @docsNotRequired */
|
|
147
|
-
id,
|
|
148
|
-
/** @docsNotRequired */
|
|
149
|
-
url,
|
|
150
|
-
/** @docsNotRequired */
|
|
151
|
-
error) {
|
|
152
|
-
super(id, url);
|
|
153
|
-
this.error = error;
|
|
154
|
-
this.type = 3 /* EventType.NavigationError */;
|
|
155
|
-
}
|
|
156
|
-
/** @docsNotRequired */
|
|
157
|
-
toString() {
|
|
158
|
-
return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;
|
|
159
|
-
}
|
|
152
|
+
function flatten(arr) {
|
|
153
|
+
return Array.prototype.concat.apply([], arr);
|
|
160
154
|
}
|
|
161
155
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* @publicApi
|
|
156
|
+
* Return the last element of an array.
|
|
165
157
|
*/
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
/** @docsNotRequired */
|
|
169
|
-
id,
|
|
170
|
-
/** @docsNotRequired */
|
|
171
|
-
url,
|
|
172
|
-
/** @docsNotRequired */
|
|
173
|
-
urlAfterRedirects,
|
|
174
|
-
/** @docsNotRequired */
|
|
175
|
-
state) {
|
|
176
|
-
super(id, url);
|
|
177
|
-
this.urlAfterRedirects = urlAfterRedirects;
|
|
178
|
-
this.state = state;
|
|
179
|
-
this.type = 4 /* EventType.RoutesRecognized */;
|
|
180
|
-
}
|
|
181
|
-
/** @docsNotRequired */
|
|
182
|
-
toString() {
|
|
183
|
-
return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
184
|
-
}
|
|
158
|
+
function last(a) {
|
|
159
|
+
return a.length > 0 ? a[a.length - 1] : null;
|
|
185
160
|
}
|
|
186
161
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @see `GuardsCheckEnd`
|
|
190
|
-
*
|
|
191
|
-
* @publicApi
|
|
162
|
+
* Verifys all booleans in an array are `true`.
|
|
192
163
|
*/
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
/** @docsNotRequired */
|
|
202
|
-
state) {
|
|
203
|
-
super(id, url);
|
|
204
|
-
this.urlAfterRedirects = urlAfterRedirects;
|
|
205
|
-
this.state = state;
|
|
206
|
-
this.type = 7 /* EventType.GuardsCheckStart */;
|
|
164
|
+
function and(bools) {
|
|
165
|
+
return !bools.some(v => !v);
|
|
166
|
+
}
|
|
167
|
+
function forEach(map, callback) {
|
|
168
|
+
for (const prop in map) {
|
|
169
|
+
if (map.hasOwnProperty(prop)) {
|
|
170
|
+
callback(map[prop], prop);
|
|
171
|
+
}
|
|
207
172
|
}
|
|
208
|
-
|
|
209
|
-
|
|
173
|
+
}
|
|
174
|
+
function wrapIntoObservable(value) {
|
|
175
|
+
if (ɵisObservable(value)) {
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
if (ɵisPromise(value)) {
|
|
179
|
+
// Use `Promise.resolve()` to wrap promise-like instances.
|
|
180
|
+
// Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the
|
|
181
|
+
// change detection.
|
|
182
|
+
return from(Promise.resolve(value));
|
|
210
183
|
}
|
|
184
|
+
return of(value);
|
|
211
185
|
}
|
|
186
|
+
|
|
212
187
|
/**
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* @see `GuardsCheckStart`
|
|
188
|
+
* @license
|
|
189
|
+
* Copyright Google LLC All Rights Reserved.
|
|
216
190
|
*
|
|
217
|
-
*
|
|
191
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
192
|
+
* found in the LICENSE file at https://angular.io/license
|
|
218
193
|
*/
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
194
|
+
function createEmptyUrlTree() {
|
|
195
|
+
return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
|
|
196
|
+
}
|
|
197
|
+
const pathCompareMap = {
|
|
198
|
+
'exact': equalSegmentGroups,
|
|
199
|
+
'subset': containsSegmentGroup,
|
|
200
|
+
};
|
|
201
|
+
const paramCompareMap = {
|
|
202
|
+
'exact': equalParams,
|
|
203
|
+
'subset': containsParams,
|
|
204
|
+
'ignored': () => true,
|
|
205
|
+
};
|
|
206
|
+
function containsTree(container, containee, options) {
|
|
207
|
+
return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&
|
|
208
|
+
paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&
|
|
209
|
+
!(options.fragment === 'exact' && container.fragment !== containee.fragment);
|
|
210
|
+
}
|
|
211
|
+
function equalParams(container, containee) {
|
|
212
|
+
// TODO: This does not handle array params correctly.
|
|
213
|
+
return shallowEqual(container, containee);
|
|
214
|
+
}
|
|
215
|
+
function equalSegmentGroups(container, containee, matrixParams) {
|
|
216
|
+
if (!equalPath(container.segments, containee.segments))
|
|
217
|
+
return false;
|
|
218
|
+
if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {
|
|
219
|
+
return false;
|
|
236
220
|
}
|
|
237
|
-
|
|
238
|
-
return
|
|
221
|
+
if (container.numberOfChildren !== containee.numberOfChildren)
|
|
222
|
+
return false;
|
|
223
|
+
for (const c in containee.children) {
|
|
224
|
+
if (!container.children[c])
|
|
225
|
+
return false;
|
|
226
|
+
if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
function containsParams(container, containee) {
|
|
232
|
+
return Object.keys(containee).length <= Object.keys(container).length &&
|
|
233
|
+
Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
|
|
234
|
+
}
|
|
235
|
+
function containsSegmentGroup(container, containee, matrixParams) {
|
|
236
|
+
return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams);
|
|
237
|
+
}
|
|
238
|
+
function containsSegmentGroupHelper(container, containee, containeePaths, matrixParams) {
|
|
239
|
+
if (container.segments.length > containeePaths.length) {
|
|
240
|
+
const current = container.segments.slice(0, containeePaths.length);
|
|
241
|
+
if (!equalPath(current, containeePaths))
|
|
242
|
+
return false;
|
|
243
|
+
if (containee.hasChildren())
|
|
244
|
+
return false;
|
|
245
|
+
if (!matrixParamsMatch(current, containeePaths, matrixParams))
|
|
246
|
+
return false;
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
else if (container.segments.length === containeePaths.length) {
|
|
250
|
+
if (!equalPath(container.segments, containeePaths))
|
|
251
|
+
return false;
|
|
252
|
+
if (!matrixParamsMatch(container.segments, containeePaths, matrixParams))
|
|
253
|
+
return false;
|
|
254
|
+
for (const c in containee.children) {
|
|
255
|
+
if (!container.children[c])
|
|
256
|
+
return false;
|
|
257
|
+
if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
const current = containeePaths.slice(0, container.segments.length);
|
|
265
|
+
const next = containeePaths.slice(container.segments.length);
|
|
266
|
+
if (!equalPath(container.segments, current))
|
|
267
|
+
return false;
|
|
268
|
+
if (!matrixParamsMatch(container.segments, current, matrixParams))
|
|
269
|
+
return false;
|
|
270
|
+
if (!container.children[PRIMARY_OUTLET])
|
|
271
|
+
return false;
|
|
272
|
+
return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams);
|
|
239
273
|
}
|
|
240
274
|
}
|
|
275
|
+
function matrixParamsMatch(containerPaths, containeePaths, options) {
|
|
276
|
+
return containeePaths.every((containeeSegment, i) => {
|
|
277
|
+
return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
241
280
|
/**
|
|
242
|
-
*
|
|
281
|
+
* @description
|
|
243
282
|
*
|
|
244
|
-
*
|
|
245
|
-
* In future, may change to only run when there are things to be resolved.
|
|
283
|
+
* Represents the parsed URL.
|
|
246
284
|
*
|
|
247
|
-
*
|
|
285
|
+
* Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a
|
|
286
|
+
* serialized tree.
|
|
287
|
+
* UrlTree is a data structure that provides a lot of affordances in dealing with URLs
|
|
288
|
+
*
|
|
289
|
+
* @usageNotes
|
|
290
|
+
* ### Example
|
|
291
|
+
*
|
|
292
|
+
* ```
|
|
293
|
+
* @Component({templateUrl:'template.html'})
|
|
294
|
+
* class MyComponent {
|
|
295
|
+
* constructor(router: Router) {
|
|
296
|
+
* const tree: UrlTree =
|
|
297
|
+
* router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
|
|
298
|
+
* const f = tree.fragment; // return 'fragment'
|
|
299
|
+
* const q = tree.queryParams; // returns {debug: 'true'}
|
|
300
|
+
* const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
|
|
301
|
+
* const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
|
|
302
|
+
* g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
|
|
303
|
+
* g.children['support'].segments; // return 1 segment 'help'
|
|
304
|
+
* }
|
|
305
|
+
* }
|
|
306
|
+
* ```
|
|
248
307
|
*
|
|
249
308
|
* @publicApi
|
|
250
309
|
*/
|
|
251
|
-
class
|
|
310
|
+
class UrlTree {
|
|
311
|
+
/** @internal */
|
|
252
312
|
constructor(
|
|
253
|
-
/**
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
this.
|
|
313
|
+
/** The root segment group of the URL tree */
|
|
314
|
+
root,
|
|
315
|
+
/** The query params of the URL */
|
|
316
|
+
queryParams,
|
|
317
|
+
/** The fragment of the URL */
|
|
318
|
+
fragment) {
|
|
319
|
+
this.root = root;
|
|
320
|
+
this.queryParams = queryParams;
|
|
321
|
+
this.fragment = fragment;
|
|
322
|
+
}
|
|
323
|
+
get queryParamMap() {
|
|
324
|
+
if (!this._queryParamMap) {
|
|
325
|
+
this._queryParamMap = convertToParamMap(this.queryParams);
|
|
326
|
+
}
|
|
327
|
+
return this._queryParamMap;
|
|
265
328
|
}
|
|
329
|
+
/** @docsNotRequired */
|
|
266
330
|
toString() {
|
|
267
|
-
return
|
|
331
|
+
return DEFAULT_SERIALIZER.serialize(this);
|
|
268
332
|
}
|
|
269
333
|
}
|
|
270
334
|
/**
|
|
271
|
-
*
|
|
272
|
-
*
|
|
335
|
+
* @description
|
|
336
|
+
*
|
|
337
|
+
* Represents the parsed URL segment group.
|
|
338
|
+
*
|
|
339
|
+
* See `UrlTree` for more information.
|
|
273
340
|
*
|
|
274
341
|
* @publicApi
|
|
275
342
|
*/
|
|
276
|
-
class
|
|
343
|
+
class UrlSegmentGroup {
|
|
277
344
|
constructor(
|
|
278
|
-
/**
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
345
|
+
/** The URL segments of this group. See `UrlSegment` for more information */
|
|
346
|
+
segments,
|
|
347
|
+
/** The list of children of this group */
|
|
348
|
+
children) {
|
|
349
|
+
this.segments = segments;
|
|
350
|
+
this.children = children;
|
|
351
|
+
/** The parent node in the url tree */
|
|
352
|
+
this.parent = null;
|
|
353
|
+
forEach(children, (v, k) => v.parent = this);
|
|
354
|
+
}
|
|
355
|
+
/** Whether the segment has child segments */
|
|
356
|
+
hasChildren() {
|
|
357
|
+
return this.numberOfChildren > 0;
|
|
358
|
+
}
|
|
359
|
+
/** Number of child segments */
|
|
360
|
+
get numberOfChildren() {
|
|
361
|
+
return Object.keys(this.children).length;
|
|
290
362
|
}
|
|
363
|
+
/** @docsNotRequired */
|
|
291
364
|
toString() {
|
|
292
|
-
return
|
|
365
|
+
return serializePaths(this);
|
|
293
366
|
}
|
|
294
367
|
}
|
|
295
368
|
/**
|
|
296
|
-
*
|
|
369
|
+
* @description
|
|
297
370
|
*
|
|
298
|
-
*
|
|
371
|
+
* Represents a single URL segment.
|
|
299
372
|
*
|
|
300
|
-
*
|
|
301
|
-
|
|
302
|
-
class RouteConfigLoadStart {
|
|
303
|
-
constructor(
|
|
304
|
-
/** @docsNotRequired */
|
|
305
|
-
route) {
|
|
306
|
-
this.route = route;
|
|
307
|
-
this.type = 9 /* EventType.RouteConfigLoadStart */;
|
|
308
|
-
}
|
|
309
|
-
toString() {
|
|
310
|
-
return `RouteConfigLoadStart(path: ${this.route.path})`;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* An event triggered when a route has been lazy loaded.
|
|
373
|
+
* A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix
|
|
374
|
+
* parameters associated with the segment.
|
|
315
375
|
*
|
|
316
|
-
* @
|
|
376
|
+
* @usageNotes
|
|
377
|
+
* ### Example
|
|
378
|
+
*
|
|
379
|
+
* ```
|
|
380
|
+
* @Component({templateUrl:'template.html'})
|
|
381
|
+
* class MyComponent {
|
|
382
|
+
* constructor(router: Router) {
|
|
383
|
+
* const tree: UrlTree = router.parseUrl('/team;id=33');
|
|
384
|
+
* const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
|
|
385
|
+
* const s: UrlSegment[] = g.segments;
|
|
386
|
+
* s[0].path; // returns 'team'
|
|
387
|
+
* s[0].parameters; // returns {id: 33}
|
|
388
|
+
* }
|
|
389
|
+
* }
|
|
390
|
+
* ```
|
|
317
391
|
*
|
|
318
392
|
* @publicApi
|
|
319
393
|
*/
|
|
320
|
-
class
|
|
394
|
+
class UrlSegment {
|
|
321
395
|
constructor(
|
|
322
|
-
/**
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
396
|
+
/** The path part of a URL segment */
|
|
397
|
+
path,
|
|
398
|
+
/** The matrix parameters associated with a segment */
|
|
399
|
+
parameters) {
|
|
400
|
+
this.path = path;
|
|
401
|
+
this.parameters = parameters;
|
|
402
|
+
}
|
|
403
|
+
get parameterMap() {
|
|
404
|
+
if (!this._parameterMap) {
|
|
405
|
+
this._parameterMap = convertToParamMap(this.parameters);
|
|
406
|
+
}
|
|
407
|
+
return this._parameterMap;
|
|
326
408
|
}
|
|
409
|
+
/** @docsNotRequired */
|
|
327
410
|
toString() {
|
|
328
|
-
return
|
|
411
|
+
return serializePath(this);
|
|
329
412
|
}
|
|
330
413
|
}
|
|
414
|
+
function equalSegments(as, bs) {
|
|
415
|
+
return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters));
|
|
416
|
+
}
|
|
417
|
+
function equalPath(as, bs) {
|
|
418
|
+
if (as.length !== bs.length)
|
|
419
|
+
return false;
|
|
420
|
+
return as.every((a, i) => a.path === bs[i].path);
|
|
421
|
+
}
|
|
422
|
+
function mapChildrenIntoArray(segment, fn) {
|
|
423
|
+
let res = [];
|
|
424
|
+
forEach(segment.children, (child, childOutlet) => {
|
|
425
|
+
if (childOutlet === PRIMARY_OUTLET) {
|
|
426
|
+
res = res.concat(fn(child, childOutlet));
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
forEach(segment.children, (child, childOutlet) => {
|
|
430
|
+
if (childOutlet !== PRIMARY_OUTLET) {
|
|
431
|
+
res = res.concat(fn(child, childOutlet));
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
return res;
|
|
435
|
+
}
|
|
331
436
|
/**
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
437
|
+
* @description
|
|
438
|
+
*
|
|
439
|
+
* Serializes and deserializes a URL string into a URL tree.
|
|
440
|
+
*
|
|
441
|
+
* The url serialization strategy is customizable. You can
|
|
442
|
+
* make all URLs case insensitive by providing a custom UrlSerializer.
|
|
443
|
+
*
|
|
444
|
+
* See `DefaultUrlSerializer` for an example of a URL serializer.
|
|
336
445
|
*
|
|
337
446
|
* @publicApi
|
|
338
447
|
*/
|
|
339
|
-
class
|
|
340
|
-
constructor(
|
|
341
|
-
/** @docsNotRequired */
|
|
342
|
-
snapshot) {
|
|
343
|
-
this.snapshot = snapshot;
|
|
344
|
-
this.type = 11 /* EventType.ChildActivationStart */;
|
|
345
|
-
}
|
|
346
|
-
toString() {
|
|
347
|
-
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
348
|
-
return `ChildActivationStart(path: '${path}')`;
|
|
349
|
-
}
|
|
448
|
+
class UrlSerializer {
|
|
350
449
|
}
|
|
351
450
|
/**
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
451
|
+
* @description
|
|
452
|
+
*
|
|
453
|
+
* A default implementation of the `UrlSerializer`.
|
|
454
|
+
*
|
|
455
|
+
* Example URLs:
|
|
456
|
+
*
|
|
457
|
+
* ```
|
|
458
|
+
* /inbox/33(popup:compose)
|
|
459
|
+
* /inbox/33;open=true/messages/44
|
|
460
|
+
* ```
|
|
461
|
+
*
|
|
462
|
+
* DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the
|
|
463
|
+
* colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to
|
|
464
|
+
* specify route specific parameters.
|
|
465
|
+
*
|
|
356
466
|
* @publicApi
|
|
357
467
|
*/
|
|
358
|
-
class
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
this.type = 12 /* EventType.ChildActivationEnd */;
|
|
468
|
+
class DefaultUrlSerializer {
|
|
469
|
+
/** Parses a url into a `UrlTree` */
|
|
470
|
+
parse(url) {
|
|
471
|
+
const p = new UrlParser(url);
|
|
472
|
+
return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
|
|
364
473
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
474
|
+
/** Converts a `UrlTree` into a url */
|
|
475
|
+
serialize(tree) {
|
|
476
|
+
const segment = `/${serializeSegment(tree.root, true)}`;
|
|
477
|
+
const query = serializeQueryParams(tree.queryParams);
|
|
478
|
+
const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';
|
|
479
|
+
return `${segment}${query}${fragment}`;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const DEFAULT_SERIALIZER = new DefaultUrlSerializer();
|
|
483
|
+
function serializePaths(segment) {
|
|
484
|
+
return segment.segments.map(p => serializePath(p)).join('/');
|
|
485
|
+
}
|
|
486
|
+
function serializeSegment(segment, root) {
|
|
487
|
+
if (!segment.hasChildren()) {
|
|
488
|
+
return serializePaths(segment);
|
|
489
|
+
}
|
|
490
|
+
if (root) {
|
|
491
|
+
const primary = segment.children[PRIMARY_OUTLET] ?
|
|
492
|
+
serializeSegment(segment.children[PRIMARY_OUTLET], false) :
|
|
493
|
+
'';
|
|
494
|
+
const children = [];
|
|
495
|
+
forEach(segment.children, (v, k) => {
|
|
496
|
+
if (k !== PRIMARY_OUTLET) {
|
|
497
|
+
children.push(`${k}:${serializeSegment(v, false)}`);
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
return children.length > 0 ? `${primary}(${children.join('//')})` : primary;
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
const children = mapChildrenIntoArray(segment, (v, k) => {
|
|
504
|
+
if (k === PRIMARY_OUTLET) {
|
|
505
|
+
return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];
|
|
506
|
+
}
|
|
507
|
+
return [`${k}:${serializeSegment(v, false)}`];
|
|
508
|
+
});
|
|
509
|
+
// use no parenthesis if the only child is a primary outlet route
|
|
510
|
+
if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) {
|
|
511
|
+
return `${serializePaths(segment)}/${children[0]}`;
|
|
512
|
+
}
|
|
513
|
+
return `${serializePaths(segment)}/(${children.join('//')})`;
|
|
368
514
|
}
|
|
369
515
|
}
|
|
370
516
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
517
|
+
* Encodes a URI string with the default encoding. This function will only ever be called from
|
|
518
|
+
* `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need
|
|
519
|
+
* a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't
|
|
520
|
+
* have to be encoded per https://url.spec.whatwg.org.
|
|
521
|
+
*/
|
|
522
|
+
function encodeUriString(s) {
|
|
523
|
+
return encodeURIComponent(s)
|
|
524
|
+
.replace(/%40/g, '@')
|
|
525
|
+
.replace(/%3A/gi, ':')
|
|
526
|
+
.replace(/%24/g, '$')
|
|
527
|
+
.replace(/%2C/gi, ',');
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* This function should be used to encode both keys and values in a query string key/value. In
|
|
531
|
+
* the following URL, you need to call encodeUriQuery on "k" and "v":
|
|
375
532
|
*
|
|
376
|
-
*
|
|
533
|
+
* http://www.site.org/html;mk=mv?k=v#f
|
|
377
534
|
*/
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
/** @docsNotRequired */
|
|
381
|
-
snapshot) {
|
|
382
|
-
this.snapshot = snapshot;
|
|
383
|
-
this.type = 13 /* EventType.ActivationStart */;
|
|
384
|
-
}
|
|
385
|
-
toString() {
|
|
386
|
-
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
387
|
-
return `ActivationStart(path: '${path}')`;
|
|
388
|
-
}
|
|
535
|
+
function encodeUriQuery(s) {
|
|
536
|
+
return encodeUriString(s).replace(/%3B/gi, ';');
|
|
389
537
|
}
|
|
390
538
|
/**
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
* @see `ActivationStart`
|
|
394
|
-
* @see `ResolveStart`
|
|
539
|
+
* This function should be used to encode a URL fragment. In the following URL, you need to call
|
|
540
|
+
* encodeUriFragment on "f":
|
|
395
541
|
*
|
|
396
|
-
*
|
|
542
|
+
* http://www.site.org/html;mk=mv?k=v#f
|
|
397
543
|
*/
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
/** @docsNotRequired */
|
|
401
|
-
snapshot) {
|
|
402
|
-
this.snapshot = snapshot;
|
|
403
|
-
this.type = 14 /* EventType.ActivationEnd */;
|
|
404
|
-
}
|
|
405
|
-
toString() {
|
|
406
|
-
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
407
|
-
return `ActivationEnd(path: '${path}')`;
|
|
408
|
-
}
|
|
544
|
+
function encodeUriFragment(s) {
|
|
545
|
+
return encodeURI(s);
|
|
409
546
|
}
|
|
410
547
|
/**
|
|
411
|
-
*
|
|
548
|
+
* This function should be run on any URI segment as well as the key and value in a key/value
|
|
549
|
+
* pair for matrix params. In the following URL, you need to call encodeUriSegment on "html",
|
|
550
|
+
* "mk", and "mv":
|
|
412
551
|
*
|
|
413
|
-
*
|
|
552
|
+
* http://www.site.org/html;mk=mv?k=v#f
|
|
414
553
|
*/
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
/** @docsNotRequired */
|
|
418
|
-
routerEvent,
|
|
419
|
-
/** @docsNotRequired */
|
|
420
|
-
position,
|
|
421
|
-
/** @docsNotRequired */
|
|
422
|
-
anchor) {
|
|
423
|
-
this.routerEvent = routerEvent;
|
|
424
|
-
this.position = position;
|
|
425
|
-
this.anchor = anchor;
|
|
426
|
-
this.type = 15 /* EventType.Scroll */;
|
|
427
|
-
}
|
|
428
|
-
toString() {
|
|
429
|
-
const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
|
|
430
|
-
return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
|
|
431
|
-
}
|
|
554
|
+
function encodeUriSegment(s) {
|
|
555
|
+
return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&');
|
|
432
556
|
}
|
|
433
|
-
function
|
|
434
|
-
|
|
435
|
-
return `Unknown Router Event: ${routerEvent.constructor.name}`;
|
|
436
|
-
}
|
|
437
|
-
switch (routerEvent.type) {
|
|
438
|
-
case 14 /* EventType.ActivationEnd */:
|
|
439
|
-
return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
|
|
440
|
-
case 13 /* EventType.ActivationStart */:
|
|
441
|
-
return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
|
|
442
|
-
case 12 /* EventType.ChildActivationEnd */:
|
|
443
|
-
return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
|
|
444
|
-
case 11 /* EventType.ChildActivationStart */:
|
|
445
|
-
return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
|
|
446
|
-
case 8 /* EventType.GuardsCheckEnd */:
|
|
447
|
-
return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
|
|
448
|
-
case 7 /* EventType.GuardsCheckStart */:
|
|
449
|
-
return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
450
|
-
case 2 /* EventType.NavigationCancel */:
|
|
451
|
-
return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
|
|
452
|
-
case 1 /* EventType.NavigationEnd */:
|
|
453
|
-
return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
|
|
454
|
-
case 3 /* EventType.NavigationError */:
|
|
455
|
-
return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
|
|
456
|
-
case 0 /* EventType.NavigationStart */:
|
|
457
|
-
return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
|
|
458
|
-
case 6 /* EventType.ResolveEnd */:
|
|
459
|
-
return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
460
|
-
case 5 /* EventType.ResolveStart */:
|
|
461
|
-
return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
462
|
-
case 10 /* EventType.RouteConfigLoadEnd */:
|
|
463
|
-
return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
|
|
464
|
-
case 9 /* EventType.RouteConfigLoadStart */:
|
|
465
|
-
return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
|
|
466
|
-
case 4 /* EventType.RoutesRecognized */:
|
|
467
|
-
return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
468
|
-
case 15 /* EventType.Scroll */:
|
|
469
|
-
const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
|
|
470
|
-
return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
|
|
471
|
-
}
|
|
557
|
+
function decode(s) {
|
|
558
|
+
return decodeURIComponent(s);
|
|
472
559
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
*
|
|
478
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
479
|
-
* found in the LICENSE file at https://angular.io/license
|
|
480
|
-
*/
|
|
481
|
-
/**
|
|
482
|
-
* The primary routing outlet.
|
|
483
|
-
*
|
|
484
|
-
* @publicApi
|
|
485
|
-
*/
|
|
486
|
-
const PRIMARY_OUTLET = 'primary';
|
|
487
|
-
class ParamsAsMap {
|
|
488
|
-
constructor(params) {
|
|
489
|
-
this.params = params || {};
|
|
490
|
-
}
|
|
491
|
-
has(name) {
|
|
492
|
-
return Object.prototype.hasOwnProperty.call(this.params, name);
|
|
493
|
-
}
|
|
494
|
-
get(name) {
|
|
495
|
-
if (this.has(name)) {
|
|
496
|
-
const v = this.params[name];
|
|
497
|
-
return Array.isArray(v) ? v[0] : v;
|
|
498
|
-
}
|
|
499
|
-
return null;
|
|
500
|
-
}
|
|
501
|
-
getAll(name) {
|
|
502
|
-
if (this.has(name)) {
|
|
503
|
-
const v = this.params[name];
|
|
504
|
-
return Array.isArray(v) ? v : [v];
|
|
505
|
-
}
|
|
506
|
-
return [];
|
|
507
|
-
}
|
|
508
|
-
get keys() {
|
|
509
|
-
return Object.keys(this.params);
|
|
510
|
-
}
|
|
560
|
+
// Query keys/values should have the "+" replaced first, as "+" in a query string is " ".
|
|
561
|
+
// decodeURIComponent function will not decode "+" as a space.
|
|
562
|
+
function decodeQuery(s) {
|
|
563
|
+
return decode(s.replace(/\+/g, '%20'));
|
|
511
564
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
* @param params The instance to convert.
|
|
515
|
-
* @returns The new map instance.
|
|
516
|
-
*
|
|
517
|
-
* @publicApi
|
|
518
|
-
*/
|
|
519
|
-
function convertToParamMap(params) {
|
|
520
|
-
return new ParamsAsMap(params);
|
|
565
|
+
function serializePath(path) {
|
|
566
|
+
return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`;
|
|
521
567
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
return error;
|
|
568
|
+
function serializeMatrixParams(params) {
|
|
569
|
+
return Object.keys(params)
|
|
570
|
+
.map(key => `;${encodeUriSegment(key)}=${encodeUriSegment(params[key])}`)
|
|
571
|
+
.join('');
|
|
527
572
|
}
|
|
528
|
-
function
|
|
529
|
-
|
|
573
|
+
function serializeQueryParams(params) {
|
|
574
|
+
const strParams = Object.keys(params)
|
|
575
|
+
.map((name) => {
|
|
576
|
+
const value = params[name];
|
|
577
|
+
return Array.isArray(value) ?
|
|
578
|
+
value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') :
|
|
579
|
+
`${encodeUriQuery(name)}=${encodeUriQuery(value)}`;
|
|
580
|
+
})
|
|
581
|
+
.filter(s => !!s);
|
|
582
|
+
return strParams.length ? `?${strParams.join('&')}` : '';
|
|
530
583
|
}
|
|
531
|
-
|
|
532
|
-
function
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
584
|
+
const SEGMENT_RE = /^[^\/()?;=#]+/;
|
|
585
|
+
function matchSegments(str) {
|
|
586
|
+
const match = str.match(SEGMENT_RE);
|
|
587
|
+
return match ? match[0] : '';
|
|
588
|
+
}
|
|
589
|
+
const QUERY_PARAM_RE = /^[^=?&#]+/;
|
|
590
|
+
// Return the name of the query param at the start of the string or an empty string
|
|
591
|
+
function matchQueryParams(str) {
|
|
592
|
+
const match = str.match(QUERY_PARAM_RE);
|
|
593
|
+
return match ? match[0] : '';
|
|
594
|
+
}
|
|
595
|
+
const QUERY_PARAM_VALUE_RE = /^[^&#]+/;
|
|
596
|
+
// Return the value of the query param at the start of the string or an empty string
|
|
597
|
+
function matchUrlQueryParamValue(str) {
|
|
598
|
+
const match = str.match(QUERY_PARAM_VALUE_RE);
|
|
599
|
+
return match ? match[0] : '';
|
|
600
|
+
}
|
|
601
|
+
class UrlParser {
|
|
602
|
+
constructor(url) {
|
|
603
|
+
this.url = url;
|
|
604
|
+
this.remaining = url;
|
|
542
605
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const segment = segments[index];
|
|
548
|
-
const isParameter = part.startsWith(':');
|
|
549
|
-
if (isParameter) {
|
|
550
|
-
posParams[part.substring(1)] = segment;
|
|
551
|
-
}
|
|
552
|
-
else if (part !== segment.path) {
|
|
553
|
-
// The actual URL part does not match the config, no match
|
|
554
|
-
return null;
|
|
606
|
+
parseRootSegment() {
|
|
607
|
+
this.consumeOptional('/');
|
|
608
|
+
if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
|
|
609
|
+
return new UrlSegmentGroup([], {});
|
|
555
610
|
}
|
|
611
|
+
// The root segment group never has segments
|
|
612
|
+
return new UrlSegmentGroup([], this.parseChildren());
|
|
556
613
|
}
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
* found in the LICENSE file at https://angular.io/license
|
|
566
|
-
*/
|
|
567
|
-
function shallowEqualArrays(a, b) {
|
|
568
|
-
if (a.length !== b.length)
|
|
569
|
-
return false;
|
|
570
|
-
for (let i = 0; i < a.length; ++i) {
|
|
571
|
-
if (!shallowEqual(a[i], b[i]))
|
|
572
|
-
return false;
|
|
614
|
+
parseQueryParams() {
|
|
615
|
+
const params = {};
|
|
616
|
+
if (this.consumeOptional('?')) {
|
|
617
|
+
do {
|
|
618
|
+
this.parseQueryParam(params);
|
|
619
|
+
} while (this.consumeOptional('&'));
|
|
620
|
+
}
|
|
621
|
+
return params;
|
|
573
622
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
function shallowEqual(a, b) {
|
|
577
|
-
// While `undefined` should never be possible, it would sometimes be the case in IE 11
|
|
578
|
-
// and pre-chromium Edge. The check below accounts for this edge case.
|
|
579
|
-
const k1 = a ? Object.keys(a) : undefined;
|
|
580
|
-
const k2 = b ? Object.keys(b) : undefined;
|
|
581
|
-
if (!k1 || !k2 || k1.length != k2.length) {
|
|
582
|
-
return false;
|
|
623
|
+
parseFragment() {
|
|
624
|
+
return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;
|
|
583
625
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
626
|
+
parseChildren() {
|
|
627
|
+
if (this.remaining === '') {
|
|
628
|
+
return {};
|
|
629
|
+
}
|
|
630
|
+
this.consumeOptional('/');
|
|
631
|
+
const segments = [];
|
|
632
|
+
if (!this.peekStartsWith('(')) {
|
|
633
|
+
segments.push(this.parseSegment());
|
|
634
|
+
}
|
|
635
|
+
while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {
|
|
636
|
+
this.capture('/');
|
|
637
|
+
segments.push(this.parseSegment());
|
|
638
|
+
}
|
|
639
|
+
let children = {};
|
|
640
|
+
if (this.peekStartsWith('/(')) {
|
|
641
|
+
this.capture('/');
|
|
642
|
+
children = this.parseParens(true);
|
|
643
|
+
}
|
|
644
|
+
let res = {};
|
|
645
|
+
if (this.peekStartsWith('(')) {
|
|
646
|
+
res = this.parseParens(false);
|
|
589
647
|
}
|
|
648
|
+
if (segments.length > 0 || Object.keys(children).length > 0) {
|
|
649
|
+
res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
|
|
650
|
+
}
|
|
651
|
+
return res;
|
|
590
652
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const aSorted = [...a].sort();
|
|
601
|
-
const bSorted = [...b].sort();
|
|
602
|
-
return aSorted.every((val, index) => bSorted[index] === val);
|
|
653
|
+
// parse a segment with its matrix parameters
|
|
654
|
+
// ie `name;k1=v1;k2`
|
|
655
|
+
parseSegment() {
|
|
656
|
+
const path = matchSegments(this.remaining);
|
|
657
|
+
if (path === '' && this.peekStartsWith(';')) {
|
|
658
|
+
throw new Error(`Empty path url segment cannot have parameters: '${this.remaining}'.`);
|
|
659
|
+
}
|
|
660
|
+
this.capture(path);
|
|
661
|
+
return new UrlSegment(decode(path), this.parseMatrixParams());
|
|
603
662
|
}
|
|
604
|
-
|
|
605
|
-
|
|
663
|
+
parseMatrixParams() {
|
|
664
|
+
const params = {};
|
|
665
|
+
while (this.consumeOptional(';')) {
|
|
666
|
+
this.parseParam(params);
|
|
667
|
+
}
|
|
668
|
+
return params;
|
|
669
|
+
}
|
|
670
|
+
parseParam(params) {
|
|
671
|
+
const key = matchSegments(this.remaining);
|
|
672
|
+
if (!key) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
this.capture(key);
|
|
676
|
+
let value = '';
|
|
677
|
+
if (this.consumeOptional('=')) {
|
|
678
|
+
const valueMatch = matchSegments(this.remaining);
|
|
679
|
+
if (valueMatch) {
|
|
680
|
+
value = valueMatch;
|
|
681
|
+
this.capture(value);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
params[decode(key)] = decode(value);
|
|
685
|
+
}
|
|
686
|
+
// Parse a single query parameter `name[=value]`
|
|
687
|
+
parseQueryParam(params) {
|
|
688
|
+
const key = matchQueryParams(this.remaining);
|
|
689
|
+
if (!key) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
this.capture(key);
|
|
693
|
+
let value = '';
|
|
694
|
+
if (this.consumeOptional('=')) {
|
|
695
|
+
const valueMatch = matchUrlQueryParamValue(this.remaining);
|
|
696
|
+
if (valueMatch) {
|
|
697
|
+
value = valueMatch;
|
|
698
|
+
this.capture(value);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const decodedKey = decodeQuery(key);
|
|
702
|
+
const decodedVal = decodeQuery(value);
|
|
703
|
+
if (params.hasOwnProperty(decodedKey)) {
|
|
704
|
+
// Append to existing values
|
|
705
|
+
let currentVal = params[decodedKey];
|
|
706
|
+
if (!Array.isArray(currentVal)) {
|
|
707
|
+
currentVal = [currentVal];
|
|
708
|
+
params[decodedKey] = currentVal;
|
|
709
|
+
}
|
|
710
|
+
currentVal.push(decodedVal);
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
// Create a new value
|
|
714
|
+
params[decodedKey] = decodedVal;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
// parse `(a/b//outlet_name:c/d)`
|
|
718
|
+
parseParens(allowPrimary) {
|
|
719
|
+
const segments = {};
|
|
720
|
+
this.capture('(');
|
|
721
|
+
while (!this.consumeOptional(')') && this.remaining.length > 0) {
|
|
722
|
+
const path = matchSegments(this.remaining);
|
|
723
|
+
const next = this.remaining[path.length];
|
|
724
|
+
// if is is not one of these characters, then the segment was unescaped
|
|
725
|
+
// or the group was not closed
|
|
726
|
+
if (next !== '/' && next !== ')' && next !== ';') {
|
|
727
|
+
throw new Error(`Cannot parse url '${this.url}'`);
|
|
728
|
+
}
|
|
729
|
+
let outletName = undefined;
|
|
730
|
+
if (path.indexOf(':') > -1) {
|
|
731
|
+
outletName = path.slice(0, path.indexOf(':'));
|
|
732
|
+
this.capture(outletName);
|
|
733
|
+
this.capture(':');
|
|
734
|
+
}
|
|
735
|
+
else if (allowPrimary) {
|
|
736
|
+
outletName = PRIMARY_OUTLET;
|
|
737
|
+
}
|
|
738
|
+
const children = this.parseChildren();
|
|
739
|
+
segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] :
|
|
740
|
+
new UrlSegmentGroup([], children);
|
|
741
|
+
this.consumeOptional('//');
|
|
742
|
+
}
|
|
743
|
+
return segments;
|
|
744
|
+
}
|
|
745
|
+
peekStartsWith(str) {
|
|
746
|
+
return this.remaining.startsWith(str);
|
|
747
|
+
}
|
|
748
|
+
// Consumes the prefix when it is present and returns whether it has been consumed
|
|
749
|
+
consumeOptional(str) {
|
|
750
|
+
if (this.peekStartsWith(str)) {
|
|
751
|
+
this.remaining = this.remaining.substring(str.length);
|
|
752
|
+
return true;
|
|
753
|
+
}
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
capture(str) {
|
|
757
|
+
if (!this.consumeOptional(str)) {
|
|
758
|
+
throw new Error(`Expected "${str}".`);
|
|
759
|
+
}
|
|
606
760
|
}
|
|
607
761
|
}
|
|
762
|
+
function createRoot(rootCandidate) {
|
|
763
|
+
return rootCandidate.segments.length > 0 ?
|
|
764
|
+
new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
|
|
765
|
+
rootCandidate;
|
|
766
|
+
}
|
|
608
767
|
/**
|
|
609
|
-
*
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
return Array.prototype.concat.apply([], arr);
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* Return the last element of an array.
|
|
616
|
-
*/
|
|
617
|
-
function last(a) {
|
|
618
|
-
return a.length > 0 ? a[a.length - 1] : null;
|
|
619
|
-
}
|
|
620
|
-
/**
|
|
621
|
-
* Verifys all booleans in an array are `true`.
|
|
768
|
+
* Recursively merges primary segment children into their parents and also drops empty children
|
|
769
|
+
* (those which have no segments and no children themselves). The latter prevents serializing a
|
|
770
|
+
* group into something like `/a(aux:)`, where `aux` is an empty child segment.
|
|
622
771
|
*/
|
|
623
|
-
function
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
772
|
+
function squashSegmentGroup(segmentGroup) {
|
|
773
|
+
const newChildren = {};
|
|
774
|
+
for (const childOutlet of Object.keys(segmentGroup.children)) {
|
|
775
|
+
const child = segmentGroup.children[childOutlet];
|
|
776
|
+
const childCandidate = squashSegmentGroup(child);
|
|
777
|
+
// don't add empty children
|
|
778
|
+
if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
|
|
779
|
+
newChildren[childOutlet] = childCandidate;
|
|
630
780
|
}
|
|
631
781
|
}
|
|
782
|
+
const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
|
|
783
|
+
return mergeTrivialChildren(s);
|
|
632
784
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
785
|
+
/**
|
|
786
|
+
* When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
|
|
787
|
+
*
|
|
788
|
+
* When a segment group has only one child which is a primary outlet, merges that child into the
|
|
789
|
+
* parent. That is, the child segment group's segments are merged into the `s` and the child's
|
|
790
|
+
* children become the children of `s`. Think of this like a 'squash', merging the child segment
|
|
791
|
+
* group into the parent.
|
|
792
|
+
*/
|
|
793
|
+
function mergeTrivialChildren(s) {
|
|
794
|
+
if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
|
|
795
|
+
const c = s.children[PRIMARY_OUTLET];
|
|
796
|
+
return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
|
|
642
797
|
}
|
|
643
|
-
return
|
|
798
|
+
return s;
|
|
644
799
|
}
|
|
645
800
|
|
|
646
801
|
/**
|
|
@@ -650,998 +805,1009 @@ function wrapIntoObservable(value) {
|
|
|
650
805
|
* Use of this source code is governed by an MIT-style license that can be
|
|
651
806
|
* found in the LICENSE file at https://angular.io/license
|
|
652
807
|
*/
|
|
653
|
-
function createEmptyUrlTree() {
|
|
654
|
-
return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
|
|
655
|
-
}
|
|
656
|
-
const pathCompareMap = {
|
|
657
|
-
'exact': equalSegmentGroups,
|
|
658
|
-
'subset': containsSegmentGroup,
|
|
659
|
-
};
|
|
660
|
-
const paramCompareMap = {
|
|
661
|
-
'exact': equalParams,
|
|
662
|
-
'subset': containsParams,
|
|
663
|
-
'ignored': () => true,
|
|
664
|
-
};
|
|
665
|
-
function containsTree(container, containee, options) {
|
|
666
|
-
return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&
|
|
667
|
-
paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&
|
|
668
|
-
!(options.fragment === 'exact' && container.fragment !== containee.fragment);
|
|
669
|
-
}
|
|
670
|
-
function equalParams(container, containee) {
|
|
671
|
-
// TODO: This does not handle array params correctly.
|
|
672
|
-
return shallowEqual(container, containee);
|
|
673
|
-
}
|
|
674
|
-
function equalSegmentGroups(container, containee, matrixParams) {
|
|
675
|
-
if (!equalPath(container.segments, containee.segments))
|
|
676
|
-
return false;
|
|
677
|
-
if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {
|
|
678
|
-
return false;
|
|
679
|
-
}
|
|
680
|
-
if (container.numberOfChildren !== containee.numberOfChildren)
|
|
681
|
-
return false;
|
|
682
|
-
for (const c in containee.children) {
|
|
683
|
-
if (!container.children[c])
|
|
684
|
-
return false;
|
|
685
|
-
if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))
|
|
686
|
-
return false;
|
|
687
|
-
}
|
|
688
|
-
return true;
|
|
689
|
-
}
|
|
690
|
-
function containsParams(container, containee) {
|
|
691
|
-
return Object.keys(containee).length <= Object.keys(container).length &&
|
|
692
|
-
Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
|
|
693
|
-
}
|
|
694
|
-
function containsSegmentGroup(container, containee, matrixParams) {
|
|
695
|
-
return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams);
|
|
696
|
-
}
|
|
697
|
-
function containsSegmentGroupHelper(container, containee, containeePaths, matrixParams) {
|
|
698
|
-
if (container.segments.length > containeePaths.length) {
|
|
699
|
-
const current = container.segments.slice(0, containeePaths.length);
|
|
700
|
-
if (!equalPath(current, containeePaths))
|
|
701
|
-
return false;
|
|
702
|
-
if (containee.hasChildren())
|
|
703
|
-
return false;
|
|
704
|
-
if (!matrixParamsMatch(current, containeePaths, matrixParams))
|
|
705
|
-
return false;
|
|
706
|
-
return true;
|
|
707
|
-
}
|
|
708
|
-
else if (container.segments.length === containeePaths.length) {
|
|
709
|
-
if (!equalPath(container.segments, containeePaths))
|
|
710
|
-
return false;
|
|
711
|
-
if (!matrixParamsMatch(container.segments, containeePaths, matrixParams))
|
|
712
|
-
return false;
|
|
713
|
-
for (const c in containee.children) {
|
|
714
|
-
if (!container.children[c])
|
|
715
|
-
return false;
|
|
716
|
-
if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {
|
|
717
|
-
return false;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
return true;
|
|
721
|
-
}
|
|
722
|
-
else {
|
|
723
|
-
const current = containeePaths.slice(0, container.segments.length);
|
|
724
|
-
const next = containeePaths.slice(container.segments.length);
|
|
725
|
-
if (!equalPath(container.segments, current))
|
|
726
|
-
return false;
|
|
727
|
-
if (!matrixParamsMatch(container.segments, current, matrixParams))
|
|
728
|
-
return false;
|
|
729
|
-
if (!container.children[PRIMARY_OUTLET])
|
|
730
|
-
return false;
|
|
731
|
-
return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
function matrixParamsMatch(containerPaths, containeePaths, options) {
|
|
735
|
-
return containeePaths.every((containeeSegment, i) => {
|
|
736
|
-
return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters);
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
808
|
/**
|
|
740
|
-
*
|
|
809
|
+
* Creates a `UrlTree` relative to an `ActivatedRouteSnapshot`.
|
|
741
810
|
*
|
|
742
|
-
*
|
|
811
|
+
* @publicApi
|
|
743
812
|
*
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
813
|
+
*
|
|
814
|
+
* @param relativeTo The `ActivatedRouteSnapshot` to apply the commands to
|
|
815
|
+
* @param commands An array of URL fragments with which to construct the new URL tree.
|
|
816
|
+
* If the path is static, can be the literal URL string. For a dynamic path, pass an array of path
|
|
817
|
+
* segments, followed by the parameters for each segment.
|
|
818
|
+
* The fragments are applied to the one provided in the `relativeTo` parameter.
|
|
819
|
+
* @param queryParams The query parameters for the `UrlTree`. `null` if the `UrlTree` does not have
|
|
820
|
+
* any query parameters.
|
|
821
|
+
* @param fragment The fragment for the `UrlTree`. `null` if the `UrlTree` does not have a fragment.
|
|
747
822
|
*
|
|
748
823
|
* @usageNotes
|
|
749
|
-
* ### Example
|
|
750
824
|
*
|
|
751
825
|
* ```
|
|
752
|
-
*
|
|
753
|
-
*
|
|
754
|
-
* constructor(router: Router) {
|
|
755
|
-
* const tree: UrlTree =
|
|
756
|
-
* router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
|
|
757
|
-
* const f = tree.fragment; // return 'fragment'
|
|
758
|
-
* const q = tree.queryParams; // returns {debug: 'true'}
|
|
759
|
-
* const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
|
|
760
|
-
* const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
|
|
761
|
-
* g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
|
|
762
|
-
* g.children['support'].segments; // return 1 segment 'help'
|
|
763
|
-
* }
|
|
764
|
-
* }
|
|
765
|
-
* ```
|
|
826
|
+
* // create /team/33/user/11
|
|
827
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team', 33, 'user', 11]);
|
|
766
828
|
*
|
|
767
|
-
*
|
|
768
|
-
|
|
769
|
-
class UrlTree {
|
|
770
|
-
/** @internal */
|
|
771
|
-
constructor(
|
|
772
|
-
/** The root segment group of the URL tree */
|
|
773
|
-
root,
|
|
774
|
-
/** The query params of the URL */
|
|
775
|
-
queryParams,
|
|
776
|
-
/** The fragment of the URL */
|
|
777
|
-
fragment) {
|
|
778
|
-
this.root = root;
|
|
779
|
-
this.queryParams = queryParams;
|
|
780
|
-
this.fragment = fragment;
|
|
781
|
-
}
|
|
782
|
-
get queryParamMap() {
|
|
783
|
-
if (!this._queryParamMap) {
|
|
784
|
-
this._queryParamMap = convertToParamMap(this.queryParams);
|
|
785
|
-
}
|
|
786
|
-
return this._queryParamMap;
|
|
787
|
-
}
|
|
788
|
-
/** @docsNotRequired */
|
|
789
|
-
toString() {
|
|
790
|
-
return DEFAULT_SERIALIZER.serialize(this);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
/**
|
|
794
|
-
* @description
|
|
829
|
+
* // create /team/33;expand=true/user/11
|
|
830
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team', 33, {expand: true}, 'user', 11]);
|
|
795
831
|
*
|
|
796
|
-
*
|
|
832
|
+
* // you can collapse static segments like this (this works only with the first passed-in value):
|
|
833
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team/33/user', userId]);
|
|
797
834
|
*
|
|
798
|
-
*
|
|
835
|
+
* // If the first segment can contain slashes, and you do not want the router to split it,
|
|
836
|
+
* // you can do the following:
|
|
837
|
+
* createUrlTreeFromSnapshot(snapshot, [{segmentPath: '/one/two'}]);
|
|
799
838
|
*
|
|
800
|
-
*
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
constructor(
|
|
804
|
-
/** The URL segments of this group. See `UrlSegment` for more information */
|
|
805
|
-
segments,
|
|
806
|
-
/** The list of children of this group */
|
|
807
|
-
children) {
|
|
808
|
-
this.segments = segments;
|
|
809
|
-
this.children = children;
|
|
810
|
-
/** The parent node in the url tree */
|
|
811
|
-
this.parent = null;
|
|
812
|
-
forEach(children, (v, k) => v.parent = this);
|
|
813
|
-
}
|
|
814
|
-
/** Whether the segment has child segments */
|
|
815
|
-
hasChildren() {
|
|
816
|
-
return this.numberOfChildren > 0;
|
|
817
|
-
}
|
|
818
|
-
/** Number of child segments */
|
|
819
|
-
get numberOfChildren() {
|
|
820
|
-
return Object.keys(this.children).length;
|
|
821
|
-
}
|
|
822
|
-
/** @docsNotRequired */
|
|
823
|
-
toString() {
|
|
824
|
-
return serializePaths(this);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
/**
|
|
828
|
-
* @description
|
|
839
|
+
* // create /team/33/(user/11//right:chat)
|
|
840
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right:
|
|
841
|
+
* 'chat'}}], null, null);
|
|
829
842
|
*
|
|
830
|
-
*
|
|
843
|
+
* // remove the right secondary node
|
|
844
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right: null}}]);
|
|
831
845
|
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
846
|
+
* // For the examples below, assume the current URL is for the `/team/33/user/11` and the
|
|
847
|
+
* `ActivatedRouteSnapshot` points to `user/11`:
|
|
834
848
|
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
849
|
+
* // navigate to /team/33/user/11/details
|
|
850
|
+
* createUrlTreeFromSnapshot(snapshot, ['details']);
|
|
837
851
|
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
* class MyComponent {
|
|
841
|
-
* constructor(router: Router) {
|
|
842
|
-
* const tree: UrlTree = router.parseUrl('/team;id=33');
|
|
843
|
-
* const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
|
|
844
|
-
* const s: UrlSegment[] = g.segments;
|
|
845
|
-
* s[0].path; // returns 'team'
|
|
846
|
-
* s[0].parameters; // returns {id: 33}
|
|
847
|
-
* }
|
|
848
|
-
* }
|
|
849
|
-
* ```
|
|
852
|
+
* // navigate to /team/33/user/22
|
|
853
|
+
* createUrlTreeFromSnapshot(snapshot, ['../22']);
|
|
850
854
|
*
|
|
851
|
-
*
|
|
855
|
+
* // navigate to /team/44/user/22
|
|
856
|
+
* createUrlTreeFromSnapshot(snapshot, ['../../team/44/user/22']);
|
|
857
|
+
* ```
|
|
852
858
|
*/
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
859
|
+
function createUrlTreeFromSnapshot(relativeTo, commands, queryParams = null, fragment = null) {
|
|
860
|
+
const relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeTo);
|
|
861
|
+
return createUrlTreeFromSegmentGroup(relativeToUrlSegmentGroup, commands, queryParams, fragment);
|
|
862
|
+
}
|
|
863
|
+
function createSegmentGroupFromRoute(route) {
|
|
864
|
+
let targetGroup;
|
|
865
|
+
function createSegmentGroupFromRouteRecursive(currentRoute) {
|
|
866
|
+
const childOutlets = {};
|
|
867
|
+
for (const childSnapshot of currentRoute.children) {
|
|
868
|
+
const root = createSegmentGroupFromRouteRecursive(childSnapshot);
|
|
869
|
+
childOutlets[childSnapshot.outlet] = root;
|
|
870
|
+
}
|
|
871
|
+
const segmentGroup = new UrlSegmentGroup(currentRoute.url, childOutlets);
|
|
872
|
+
if (currentRoute === route) {
|
|
873
|
+
targetGroup = segmentGroup;
|
|
874
|
+
}
|
|
875
|
+
return segmentGroup;
|
|
876
|
+
}
|
|
877
|
+
const rootCandidate = createSegmentGroupFromRouteRecursive(route.root);
|
|
878
|
+
const rootSegmentGroup = createRoot(rootCandidate);
|
|
879
|
+
return targetGroup ?? rootSegmentGroup;
|
|
880
|
+
}
|
|
881
|
+
function createUrlTreeFromSegmentGroup(relativeTo, commands, queryParams, fragment) {
|
|
882
|
+
let root = relativeTo;
|
|
883
|
+
while (root.parent) {
|
|
884
|
+
root = root.parent;
|
|
885
|
+
}
|
|
886
|
+
// There are no commands so the `UrlTree` goes to the same path as the one created from the
|
|
887
|
+
// `UrlSegmentGroup`. All we need to do is update the `queryParams` and `fragment` without
|
|
888
|
+
// applying any other logic.
|
|
889
|
+
if (commands.length === 0) {
|
|
890
|
+
return tree(root, root, root, queryParams, fragment);
|
|
867
891
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
return
|
|
892
|
+
const nav = computeNavigation(commands);
|
|
893
|
+
if (nav.toRoot()) {
|
|
894
|
+
return tree(root, root, new UrlSegmentGroup([], {}), queryParams, fragment);
|
|
871
895
|
}
|
|
896
|
+
const position = findStartingPositionForTargetGroup(nav, root, relativeTo);
|
|
897
|
+
const newSegmentGroup = position.processChildren ?
|
|
898
|
+
updateSegmentGroupChildren(position.segmentGroup, position.index, nav.commands) :
|
|
899
|
+
updateSegmentGroup(position.segmentGroup, position.index, nav.commands);
|
|
900
|
+
return tree(root, position.segmentGroup, newSegmentGroup, queryParams, fragment);
|
|
872
901
|
}
|
|
873
|
-
function
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
}
|
|
881
|
-
function
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
902
|
+
function createUrlTree(route, urlTree, commands, queryParams, fragment) {
|
|
903
|
+
if (commands.length === 0) {
|
|
904
|
+
return tree(urlTree.root, urlTree.root, urlTree.root, queryParams, fragment);
|
|
905
|
+
}
|
|
906
|
+
const nav = computeNavigation(commands);
|
|
907
|
+
if (nav.toRoot()) {
|
|
908
|
+
return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment);
|
|
909
|
+
}
|
|
910
|
+
function createTreeUsingPathIndex(lastPathIndex) {
|
|
911
|
+
const startingPosition = findStartingPosition(nav, urlTree, route.snapshot?._urlSegment, lastPathIndex);
|
|
912
|
+
const segmentGroup = startingPosition.processChildren ?
|
|
913
|
+
updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
|
|
914
|
+
updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
|
|
915
|
+
return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
|
|
916
|
+
}
|
|
917
|
+
// Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this
|
|
918
|
+
// may be the case. Since we try to access it at an earlier point before the refactor to add the
|
|
919
|
+
// warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it
|
|
920
|
+
// didn't before.
|
|
921
|
+
const result = createTreeUsingPathIndex(route.snapshot?._lastPathIndex);
|
|
922
|
+
// Check if application is relying on `relativeLinkResolution: 'legacy'`
|
|
923
|
+
if (typeof ngDevMode === 'undefined' || !!ngDevMode) {
|
|
924
|
+
const correctedResult = createTreeUsingPathIndex(route.snapshot?._correctedLastPathIndex);
|
|
925
|
+
if (correctedResult.toString() !== result.toString()) {
|
|
926
|
+
console.warn(`relativeLinkResolution: 'legacy' is deprecated and will be removed in a future version of Angular. The link to ${result.toString()} will change to ${correctedResult.toString()} if the code is not updated before then.`);
|
|
891
927
|
}
|
|
892
|
-
}
|
|
893
|
-
return
|
|
928
|
+
}
|
|
929
|
+
return result;
|
|
894
930
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
*
|
|
898
|
-
* Serializes and deserializes a URL string into a URL tree.
|
|
899
|
-
*
|
|
900
|
-
* The url serialization strategy is customizable. You can
|
|
901
|
-
* make all URLs case insensitive by providing a custom UrlSerializer.
|
|
902
|
-
*
|
|
903
|
-
* See `DefaultUrlSerializer` for an example of a URL serializer.
|
|
904
|
-
*
|
|
905
|
-
* @publicApi
|
|
906
|
-
*/
|
|
907
|
-
class UrlSerializer {
|
|
931
|
+
function isMatrixParams(command) {
|
|
932
|
+
return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
|
|
908
933
|
}
|
|
909
934
|
/**
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
* A default implementation of the `UrlSerializer`.
|
|
913
|
-
*
|
|
914
|
-
* Example URLs:
|
|
915
|
-
*
|
|
916
|
-
* ```
|
|
917
|
-
* /inbox/33(popup:compose)
|
|
918
|
-
* /inbox/33;open=true/messages/44
|
|
919
|
-
* ```
|
|
920
|
-
*
|
|
921
|
-
* DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the
|
|
922
|
-
* colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to
|
|
923
|
-
* specify route specific parameters.
|
|
924
|
-
*
|
|
925
|
-
* @publicApi
|
|
935
|
+
* Determines if a given command has an `outlets` map. When we encounter a command
|
|
936
|
+
* with an outlets k/v map, we need to apply each outlet individually to the existing segment.
|
|
926
937
|
*/
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
parse(url) {
|
|
930
|
-
const p = new UrlParser(url);
|
|
931
|
-
return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
|
|
932
|
-
}
|
|
933
|
-
/** Converts a `UrlTree` into a url */
|
|
934
|
-
serialize(tree) {
|
|
935
|
-
const segment = `/${serializeSegment(tree.root, true)}`;
|
|
936
|
-
const query = serializeQueryParams(tree.queryParams);
|
|
937
|
-
const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';
|
|
938
|
-
return `${segment}${query}${fragment}`;
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
const DEFAULT_SERIALIZER = new DefaultUrlSerializer();
|
|
942
|
-
function serializePaths(segment) {
|
|
943
|
-
return segment.segments.map(p => serializePath(p)).join('/');
|
|
938
|
+
function isCommandWithOutlets(command) {
|
|
939
|
+
return typeof command === 'object' && command != null && command.outlets;
|
|
944
940
|
}
|
|
945
|
-
function
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
const primary = segment.children[PRIMARY_OUTLET] ?
|
|
951
|
-
serializeSegment(segment.children[PRIMARY_OUTLET], false) :
|
|
952
|
-
'';
|
|
953
|
-
const children = [];
|
|
954
|
-
forEach(segment.children, (v, k) => {
|
|
955
|
-
if (k !== PRIMARY_OUTLET) {
|
|
956
|
-
children.push(`${k}:${serializeSegment(v, false)}`);
|
|
957
|
-
}
|
|
941
|
+
function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) {
|
|
942
|
+
let qp = {};
|
|
943
|
+
if (queryParams) {
|
|
944
|
+
forEach(queryParams, (value, name) => {
|
|
945
|
+
qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
|
|
958
946
|
});
|
|
959
|
-
|
|
947
|
+
}
|
|
948
|
+
let rootCandidate;
|
|
949
|
+
if (oldRoot === oldSegmentGroup) {
|
|
950
|
+
rootCandidate = newSegmentGroup;
|
|
960
951
|
}
|
|
961
952
|
else {
|
|
962
|
-
|
|
963
|
-
if (k === PRIMARY_OUTLET) {
|
|
964
|
-
return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];
|
|
965
|
-
}
|
|
966
|
-
return [`${k}:${serializeSegment(v, false)}`];
|
|
967
|
-
});
|
|
968
|
-
// use no parenthesis if the only child is a primary outlet route
|
|
969
|
-
if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) {
|
|
970
|
-
return `${serializePaths(segment)}/${children[0]}`;
|
|
971
|
-
}
|
|
972
|
-
return `${serializePaths(segment)}/(${children.join('//')})`;
|
|
953
|
+
rootCandidate = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup);
|
|
973
954
|
}
|
|
955
|
+
const newRoot = createRoot(squashSegmentGroup(rootCandidate));
|
|
956
|
+
return new UrlTree(newRoot, qp, fragment);
|
|
974
957
|
}
|
|
975
958
|
/**
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
|
|
981
|
-
function encodeUriString(s) {
|
|
982
|
-
return encodeURIComponent(s)
|
|
983
|
-
.replace(/%40/g, '@')
|
|
984
|
-
.replace(/%3A/gi, ':')
|
|
985
|
-
.replace(/%24/g, '$')
|
|
986
|
-
.replace(/%2C/gi, ',');
|
|
987
|
-
}
|
|
988
|
-
/**
|
|
989
|
-
* This function should be used to encode both keys and values in a query string key/value. In
|
|
990
|
-
* the following URL, you need to call encodeUriQuery on "k" and "v":
|
|
991
|
-
*
|
|
992
|
-
* http://www.site.org/html;mk=mv?k=v#f
|
|
959
|
+
* Replaces the `oldSegment` which is located in some child of the `current` with the `newSegment`.
|
|
960
|
+
* This also has the effect of creating new `UrlSegmentGroup` copies to update references. This
|
|
961
|
+
* shouldn't be necessary but the fallback logic for an invalid ActivatedRoute in the creation uses
|
|
962
|
+
* the Router's current url tree. If we don't create new segment groups, we end up modifying that
|
|
963
|
+
* value.
|
|
993
964
|
*/
|
|
994
|
-
function
|
|
995
|
-
|
|
965
|
+
function replaceSegment(current, oldSegment, newSegment) {
|
|
966
|
+
const children = {};
|
|
967
|
+
forEach(current.children, (c, outletName) => {
|
|
968
|
+
if (c === oldSegment) {
|
|
969
|
+
children[outletName] = newSegment;
|
|
970
|
+
}
|
|
971
|
+
else {
|
|
972
|
+
children[outletName] = replaceSegment(c, oldSegment, newSegment);
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
return new UrlSegmentGroup(current.segments, children);
|
|
996
976
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
function encodeUriSegment(s) {
|
|
1014
|
-
return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&');
|
|
1015
|
-
}
|
|
1016
|
-
function decode(s) {
|
|
1017
|
-
return decodeURIComponent(s);
|
|
1018
|
-
}
|
|
1019
|
-
// Query keys/values should have the "+" replaced first, as "+" in a query string is " ".
|
|
1020
|
-
// decodeURIComponent function will not decode "+" as a space.
|
|
1021
|
-
function decodeQuery(s) {
|
|
1022
|
-
return decode(s.replace(/\+/g, '%20'));
|
|
977
|
+
class Navigation {
|
|
978
|
+
constructor(isAbsolute, numberOfDoubleDots, commands) {
|
|
979
|
+
this.isAbsolute = isAbsolute;
|
|
980
|
+
this.numberOfDoubleDots = numberOfDoubleDots;
|
|
981
|
+
this.commands = commands;
|
|
982
|
+
if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
|
|
983
|
+
throw new Error('Root segment cannot have matrix parameters');
|
|
984
|
+
}
|
|
985
|
+
const cmdWithOutlet = commands.find(isCommandWithOutlets);
|
|
986
|
+
if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
|
|
987
|
+
throw new Error('{outlets:{}} has to be the last command');
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
toRoot() {
|
|
991
|
+
return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
|
|
992
|
+
}
|
|
1023
993
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
994
|
+
/** Transforms commands to a normalized `Navigation` */
|
|
995
|
+
function computeNavigation(commands) {
|
|
996
|
+
if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
|
|
997
|
+
return new Navigation(true, 0, commands);
|
|
998
|
+
}
|
|
999
|
+
let numberOfDoubleDots = 0;
|
|
1000
|
+
let isAbsolute = false;
|
|
1001
|
+
const res = commands.reduce((res, cmd, cmdIdx) => {
|
|
1002
|
+
if (typeof cmd === 'object' && cmd != null) {
|
|
1003
|
+
if (cmd.outlets) {
|
|
1004
|
+
const outlets = {};
|
|
1005
|
+
forEach(cmd.outlets, (commands, name) => {
|
|
1006
|
+
outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
|
|
1007
|
+
});
|
|
1008
|
+
return [...res, { outlets }];
|
|
1009
|
+
}
|
|
1010
|
+
if (cmd.segmentPath) {
|
|
1011
|
+
return [...res, cmd.segmentPath];
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
if (!(typeof cmd === 'string')) {
|
|
1015
|
+
return [...res, cmd];
|
|
1016
|
+
}
|
|
1017
|
+
if (cmdIdx === 0) {
|
|
1018
|
+
cmd.split('/').forEach((urlPart, partIndex) => {
|
|
1019
|
+
if (partIndex == 0 && urlPart === '.') {
|
|
1020
|
+
// skip './a'
|
|
1021
|
+
}
|
|
1022
|
+
else if (partIndex == 0 && urlPart === '') { // '/a'
|
|
1023
|
+
isAbsolute = true;
|
|
1024
|
+
}
|
|
1025
|
+
else if (urlPart === '..') { // '../a'
|
|
1026
|
+
numberOfDoubleDots++;
|
|
1027
|
+
}
|
|
1028
|
+
else if (urlPart != '') {
|
|
1029
|
+
res.push(urlPart);
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
return res;
|
|
1033
|
+
}
|
|
1034
|
+
return [...res, cmd];
|
|
1035
|
+
}, []);
|
|
1036
|
+
return new Navigation(isAbsolute, numberOfDoubleDots, res);
|
|
1026
1037
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
.
|
|
1030
|
-
.
|
|
1038
|
+
class Position {
|
|
1039
|
+
constructor(segmentGroup, processChildren, index) {
|
|
1040
|
+
this.segmentGroup = segmentGroup;
|
|
1041
|
+
this.processChildren = processChildren;
|
|
1042
|
+
this.index = index;
|
|
1043
|
+
}
|
|
1031
1044
|
}
|
|
1032
|
-
function
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1045
|
+
function findStartingPositionForTargetGroup(nav, root, target) {
|
|
1046
|
+
if (nav.isAbsolute) {
|
|
1047
|
+
return new Position(root, true, 0);
|
|
1048
|
+
}
|
|
1049
|
+
if (!target) {
|
|
1050
|
+
// `NaN` is used only to maintain backwards compatibility with incorrectly mocked
|
|
1051
|
+
// `ActivatedRouteSnapshot` in tests. In prior versions of this code, the position here was
|
|
1052
|
+
// determined based on an internal property that was rarely mocked, resulting in `NaN`. In
|
|
1053
|
+
// reality, this code path should _never_ be touched since `target` is not allowed to be falsey.
|
|
1054
|
+
return new Position(root, false, NaN);
|
|
1055
|
+
}
|
|
1056
|
+
if (target.parent === null) {
|
|
1057
|
+
return new Position(target, true, 0);
|
|
1058
|
+
}
|
|
1059
|
+
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
|
|
1060
|
+
const index = target.segments.length - 1 + modifier;
|
|
1061
|
+
return createPositionApplyingDoubleDots(target, index, nav.numberOfDoubleDots);
|
|
1042
1062
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1063
|
+
function findStartingPosition(nav, tree, segmentGroup, lastPathIndex) {
|
|
1064
|
+
if (nav.isAbsolute) {
|
|
1065
|
+
return new Position(tree.root, true, 0);
|
|
1066
|
+
}
|
|
1067
|
+
if (lastPathIndex === -1) {
|
|
1068
|
+
// Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
|
|
1069
|
+
// see issue #26224, #13011, #35687
|
|
1070
|
+
// However, if the ActivatedRoute is the root we should process children like above.
|
|
1071
|
+
const processChildren = segmentGroup === tree.root;
|
|
1072
|
+
return new Position(segmentGroup, processChildren, 0);
|
|
1073
|
+
}
|
|
1074
|
+
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
|
|
1075
|
+
const index = lastPathIndex + modifier;
|
|
1076
|
+
return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
|
|
1047
1077
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1078
|
+
function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) {
|
|
1079
|
+
let g = group;
|
|
1080
|
+
let ci = index;
|
|
1081
|
+
let dd = numberOfDoubleDots;
|
|
1082
|
+
while (dd > ci) {
|
|
1083
|
+
dd -= ci;
|
|
1084
|
+
g = g.parent;
|
|
1085
|
+
if (!g) {
|
|
1086
|
+
throw new Error('Invalid number of \'../\'');
|
|
1087
|
+
}
|
|
1088
|
+
ci = g.segments.length;
|
|
1089
|
+
}
|
|
1090
|
+
return new Position(g, false, ci - dd);
|
|
1053
1091
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
return
|
|
1092
|
+
function getOutlets(commands) {
|
|
1093
|
+
if (isCommandWithOutlets(commands[0])) {
|
|
1094
|
+
return commands[0].outlets;
|
|
1095
|
+
}
|
|
1096
|
+
return { [PRIMARY_OUTLET]: commands };
|
|
1059
1097
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
this.remaining = url;
|
|
1098
|
+
function updateSegmentGroup(segmentGroup, startIndex, commands) {
|
|
1099
|
+
if (!segmentGroup) {
|
|
1100
|
+
segmentGroup = new UrlSegmentGroup([], {});
|
|
1064
1101
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
|
|
1068
|
-
return new UrlSegmentGroup([], {});
|
|
1069
|
-
}
|
|
1070
|
-
// The root segment group never has segments
|
|
1071
|
-
return new UrlSegmentGroup([], this.parseChildren());
|
|
1102
|
+
if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
|
|
1103
|
+
return updateSegmentGroupChildren(segmentGroup, startIndex, commands);
|
|
1072
1104
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
return params;
|
|
1105
|
+
const m = prefixedWith(segmentGroup, startIndex, commands);
|
|
1106
|
+
const slicedCommands = commands.slice(m.commandIndex);
|
|
1107
|
+
if (m.match && m.pathIndex < segmentGroup.segments.length) {
|
|
1108
|
+
const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
|
|
1109
|
+
g.children[PRIMARY_OUTLET] =
|
|
1110
|
+
new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
|
|
1111
|
+
return updateSegmentGroupChildren(g, 0, slicedCommands);
|
|
1081
1112
|
}
|
|
1082
|
-
|
|
1083
|
-
return
|
|
1113
|
+
else if (m.match && slicedCommands.length === 0) {
|
|
1114
|
+
return new UrlSegmentGroup(segmentGroup.segments, {});
|
|
1084
1115
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
return {};
|
|
1088
|
-
}
|
|
1089
|
-
this.consumeOptional('/');
|
|
1090
|
-
const segments = [];
|
|
1091
|
-
if (!this.peekStartsWith('(')) {
|
|
1092
|
-
segments.push(this.parseSegment());
|
|
1093
|
-
}
|
|
1094
|
-
while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {
|
|
1095
|
-
this.capture('/');
|
|
1096
|
-
segments.push(this.parseSegment());
|
|
1097
|
-
}
|
|
1098
|
-
let children = {};
|
|
1099
|
-
if (this.peekStartsWith('/(')) {
|
|
1100
|
-
this.capture('/');
|
|
1101
|
-
children = this.parseParens(true);
|
|
1102
|
-
}
|
|
1103
|
-
let res = {};
|
|
1104
|
-
if (this.peekStartsWith('(')) {
|
|
1105
|
-
res = this.parseParens(false);
|
|
1106
|
-
}
|
|
1107
|
-
if (segments.length > 0 || Object.keys(children).length > 0) {
|
|
1108
|
-
res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
|
|
1109
|
-
}
|
|
1110
|
-
return res;
|
|
1116
|
+
else if (m.match && !segmentGroup.hasChildren()) {
|
|
1117
|
+
return createNewSegmentGroup(segmentGroup, startIndex, commands);
|
|
1111
1118
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
parseSegment() {
|
|
1115
|
-
const path = matchSegments(this.remaining);
|
|
1116
|
-
if (path === '' && this.peekStartsWith(';')) {
|
|
1117
|
-
throw new Error(`Empty path url segment cannot have parameters: '${this.remaining}'.`);
|
|
1118
|
-
}
|
|
1119
|
-
this.capture(path);
|
|
1120
|
-
return new UrlSegment(decode(path), this.parseMatrixParams());
|
|
1119
|
+
else if (m.match) {
|
|
1120
|
+
return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands);
|
|
1121
1121
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
while (this.consumeOptional(';')) {
|
|
1125
|
-
this.parseParam(params);
|
|
1126
|
-
}
|
|
1127
|
-
return params;
|
|
1122
|
+
else {
|
|
1123
|
+
return createNewSegmentGroup(segmentGroup, startIndex, commands);
|
|
1128
1124
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
if (
|
|
1139
|
-
|
|
1140
|
-
|
|
1125
|
+
}
|
|
1126
|
+
function updateSegmentGroupChildren(segmentGroup, startIndex, commands) {
|
|
1127
|
+
if (commands.length === 0) {
|
|
1128
|
+
return new UrlSegmentGroup(segmentGroup.segments, {});
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
const outlets = getOutlets(commands);
|
|
1132
|
+
const children = {};
|
|
1133
|
+
forEach(outlets, (commands, outlet) => {
|
|
1134
|
+
if (typeof commands === 'string') {
|
|
1135
|
+
commands = [commands];
|
|
1136
|
+
}
|
|
1137
|
+
if (commands !== null) {
|
|
1138
|
+
children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
forEach(segmentGroup.children, (child, childOutlet) => {
|
|
1142
|
+
if (outlets[childOutlet] === undefined) {
|
|
1143
|
+
children[childOutlet] = child;
|
|
1141
1144
|
}
|
|
1145
|
+
});
|
|
1146
|
+
return new UrlSegmentGroup(segmentGroup.segments, children);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
function prefixedWith(segmentGroup, startIndex, commands) {
|
|
1150
|
+
let currentCommandIndex = 0;
|
|
1151
|
+
let currentPathIndex = startIndex;
|
|
1152
|
+
const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
|
|
1153
|
+
while (currentPathIndex < segmentGroup.segments.length) {
|
|
1154
|
+
if (currentCommandIndex >= commands.length)
|
|
1155
|
+
return noMatch;
|
|
1156
|
+
const path = segmentGroup.segments[currentPathIndex];
|
|
1157
|
+
const command = commands[currentCommandIndex];
|
|
1158
|
+
// Do not try to consume command as part of the prefixing if it has outlets because it can
|
|
1159
|
+
// contain outlets other than the one being processed. Consuming the outlets command would
|
|
1160
|
+
// result in other outlets being ignored.
|
|
1161
|
+
if (isCommandWithOutlets(command)) {
|
|
1162
|
+
break;
|
|
1142
1163
|
}
|
|
1143
|
-
|
|
1164
|
+
const curr = `${command}`;
|
|
1165
|
+
const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
|
|
1166
|
+
if (currentPathIndex > 0 && curr === undefined)
|
|
1167
|
+
break;
|
|
1168
|
+
if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
|
|
1169
|
+
if (!compare(curr, next, path))
|
|
1170
|
+
return noMatch;
|
|
1171
|
+
currentCommandIndex += 2;
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
if (!compare(curr, {}, path))
|
|
1175
|
+
return noMatch;
|
|
1176
|
+
currentCommandIndex++;
|
|
1177
|
+
}
|
|
1178
|
+
currentPathIndex++;
|
|
1144
1179
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1180
|
+
return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
|
|
1181
|
+
}
|
|
1182
|
+
function createNewSegmentGroup(segmentGroup, startIndex, commands) {
|
|
1183
|
+
const paths = segmentGroup.segments.slice(0, startIndex);
|
|
1184
|
+
let i = 0;
|
|
1185
|
+
while (i < commands.length) {
|
|
1186
|
+
const command = commands[i];
|
|
1187
|
+
if (isCommandWithOutlets(command)) {
|
|
1188
|
+
const children = createNewSegmentChildren(command.outlets);
|
|
1189
|
+
return new UrlSegmentGroup(paths, children);
|
|
1150
1190
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
this.capture(value);
|
|
1158
|
-
}
|
|
1191
|
+
// if we start with an object literal, we need to reuse the path part from the segment
|
|
1192
|
+
if (i === 0 && isMatrixParams(commands[0])) {
|
|
1193
|
+
const p = segmentGroup.segments[startIndex];
|
|
1194
|
+
paths.push(new UrlSegment(p.path, stringify(commands[0])));
|
|
1195
|
+
i++;
|
|
1196
|
+
continue;
|
|
1159
1197
|
}
|
|
1160
|
-
const
|
|
1161
|
-
const
|
|
1162
|
-
if (
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
if (!Array.isArray(currentVal)) {
|
|
1166
|
-
currentVal = [currentVal];
|
|
1167
|
-
params[decodedKey] = currentVal;
|
|
1168
|
-
}
|
|
1169
|
-
currentVal.push(decodedVal);
|
|
1198
|
+
const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`;
|
|
1199
|
+
const next = (i < commands.length - 1) ? commands[i + 1] : null;
|
|
1200
|
+
if (curr && next && isMatrixParams(next)) {
|
|
1201
|
+
paths.push(new UrlSegment(curr, stringify(next)));
|
|
1202
|
+
i += 2;
|
|
1170
1203
|
}
|
|
1171
1204
|
else {
|
|
1172
|
-
|
|
1173
|
-
|
|
1205
|
+
paths.push(new UrlSegment(curr, {}));
|
|
1206
|
+
i++;
|
|
1174
1207
|
}
|
|
1175
1208
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
// if is is not one of these characters, then the segment was unescaped
|
|
1184
|
-
// or the group was not closed
|
|
1185
|
-
if (next !== '/' && next !== ')' && next !== ';') {
|
|
1186
|
-
throw new Error(`Cannot parse url '${this.url}'`);
|
|
1187
|
-
}
|
|
1188
|
-
let outletName = undefined;
|
|
1189
|
-
if (path.indexOf(':') > -1) {
|
|
1190
|
-
outletName = path.slice(0, path.indexOf(':'));
|
|
1191
|
-
this.capture(outletName);
|
|
1192
|
-
this.capture(':');
|
|
1193
|
-
}
|
|
1194
|
-
else if (allowPrimary) {
|
|
1195
|
-
outletName = PRIMARY_OUTLET;
|
|
1196
|
-
}
|
|
1197
|
-
const children = this.parseChildren();
|
|
1198
|
-
segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] :
|
|
1199
|
-
new UrlSegmentGroup([], children);
|
|
1200
|
-
this.consumeOptional('//');
|
|
1209
|
+
return new UrlSegmentGroup(paths, {});
|
|
1210
|
+
}
|
|
1211
|
+
function createNewSegmentChildren(outlets) {
|
|
1212
|
+
const children = {};
|
|
1213
|
+
forEach(outlets, (commands, outlet) => {
|
|
1214
|
+
if (typeof commands === 'string') {
|
|
1215
|
+
commands = [commands];
|
|
1201
1216
|
}
|
|
1202
|
-
|
|
1217
|
+
if (commands !== null) {
|
|
1218
|
+
children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands);
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
return children;
|
|
1222
|
+
}
|
|
1223
|
+
function stringify(params) {
|
|
1224
|
+
const res = {};
|
|
1225
|
+
forEach(params, (v, k) => res[k] = `${v}`);
|
|
1226
|
+
return res;
|
|
1227
|
+
}
|
|
1228
|
+
function compare(path, params, segment) {
|
|
1229
|
+
return path == segment.path && shallowEqual(params, segment.parameters);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* @license
|
|
1234
|
+
* Copyright Google LLC All Rights Reserved.
|
|
1235
|
+
*
|
|
1236
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
1237
|
+
* found in the LICENSE file at https://angular.io/license
|
|
1238
|
+
*/
|
|
1239
|
+
/**
|
|
1240
|
+
* Base for events the router goes through, as opposed to events tied to a specific
|
|
1241
|
+
* route. Fired one time for any given navigation.
|
|
1242
|
+
*
|
|
1243
|
+
* The following code shows how a class subscribes to router events.
|
|
1244
|
+
*
|
|
1245
|
+
* ```ts
|
|
1246
|
+
* import {Event, RouterEvent, Router} from '@angular/router';
|
|
1247
|
+
*
|
|
1248
|
+
* class MyService {
|
|
1249
|
+
* constructor(public router: Router) {
|
|
1250
|
+
* router.events.pipe(
|
|
1251
|
+
* filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
|
|
1252
|
+
* ).subscribe((e: RouterEvent) => {
|
|
1253
|
+
* // Do something
|
|
1254
|
+
* });
|
|
1255
|
+
* }
|
|
1256
|
+
* }
|
|
1257
|
+
* ```
|
|
1258
|
+
*
|
|
1259
|
+
* @see `Event`
|
|
1260
|
+
* @see [Router events summary](guide/router-reference#router-events)
|
|
1261
|
+
* @publicApi
|
|
1262
|
+
*/
|
|
1263
|
+
class RouterEvent {
|
|
1264
|
+
constructor(
|
|
1265
|
+
/** A unique ID that the router assigns to every router navigation. */
|
|
1266
|
+
id,
|
|
1267
|
+
/** The URL that is the destination for this navigation. */
|
|
1268
|
+
url) {
|
|
1269
|
+
this.id = id;
|
|
1270
|
+
this.url = url;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* An event triggered when a navigation starts.
|
|
1275
|
+
*
|
|
1276
|
+
* @publicApi
|
|
1277
|
+
*/
|
|
1278
|
+
class NavigationStart extends RouterEvent {
|
|
1279
|
+
constructor(
|
|
1280
|
+
/** @docsNotRequired */
|
|
1281
|
+
id,
|
|
1282
|
+
/** @docsNotRequired */
|
|
1283
|
+
url,
|
|
1284
|
+
/** @docsNotRequired */
|
|
1285
|
+
navigationTrigger = 'imperative',
|
|
1286
|
+
/** @docsNotRequired */
|
|
1287
|
+
restoredState = null) {
|
|
1288
|
+
super(id, url);
|
|
1289
|
+
this.type = 0 /* EventType.NavigationStart */;
|
|
1290
|
+
this.navigationTrigger = navigationTrigger;
|
|
1291
|
+
this.restoredState = restoredState;
|
|
1292
|
+
}
|
|
1293
|
+
/** @docsNotRequired */
|
|
1294
|
+
toString() {
|
|
1295
|
+
return `NavigationStart(id: ${this.id}, url: '${this.url}')`;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* An event triggered when a navigation ends successfully.
|
|
1300
|
+
*
|
|
1301
|
+
* @see `NavigationStart`
|
|
1302
|
+
* @see `NavigationCancel`
|
|
1303
|
+
* @see `NavigationError`
|
|
1304
|
+
*
|
|
1305
|
+
* @publicApi
|
|
1306
|
+
*/
|
|
1307
|
+
class NavigationEnd extends RouterEvent {
|
|
1308
|
+
constructor(
|
|
1309
|
+
/** @docsNotRequired */
|
|
1310
|
+
id,
|
|
1311
|
+
/** @docsNotRequired */
|
|
1312
|
+
url,
|
|
1313
|
+
/** @docsNotRequired */
|
|
1314
|
+
urlAfterRedirects) {
|
|
1315
|
+
super(id, url);
|
|
1316
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1317
|
+
this.type = 1 /* EventType.NavigationEnd */;
|
|
1203
1318
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1319
|
+
/** @docsNotRequired */
|
|
1320
|
+
toString() {
|
|
1321
|
+
return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;
|
|
1206
1322
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* An event triggered when a navigation is canceled, directly or indirectly.
|
|
1326
|
+
* This can happen for several reasons including when a route guard
|
|
1327
|
+
* returns `false` or initiates a redirect by returning a `UrlTree`.
|
|
1328
|
+
*
|
|
1329
|
+
* @see `NavigationStart`
|
|
1330
|
+
* @see `NavigationEnd`
|
|
1331
|
+
* @see `NavigationError`
|
|
1332
|
+
*
|
|
1333
|
+
* @publicApi
|
|
1334
|
+
*/
|
|
1335
|
+
class NavigationCancel extends RouterEvent {
|
|
1336
|
+
constructor(
|
|
1337
|
+
/** @docsNotRequired */
|
|
1338
|
+
id,
|
|
1339
|
+
/** @docsNotRequired */
|
|
1340
|
+
url,
|
|
1341
|
+
/** @docsNotRequired */
|
|
1342
|
+
reason) {
|
|
1343
|
+
super(id, url);
|
|
1344
|
+
this.reason = reason;
|
|
1345
|
+
this.type = 2 /* EventType.NavigationCancel */;
|
|
1214
1346
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1347
|
+
/** @docsNotRequired */
|
|
1348
|
+
toString() {
|
|
1349
|
+
return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
|
|
1219
1350
|
}
|
|
1220
1351
|
}
|
|
1221
|
-
|
|
1222
1352
|
/**
|
|
1223
|
-
*
|
|
1224
|
-
* Copyright Google LLC All Rights Reserved.
|
|
1353
|
+
* An event triggered when a navigation fails due to an unexpected error.
|
|
1225
1354
|
*
|
|
1226
|
-
*
|
|
1227
|
-
*
|
|
1355
|
+
* @see `NavigationStart`
|
|
1356
|
+
* @see `NavigationEnd`
|
|
1357
|
+
* @see `NavigationCancel`
|
|
1358
|
+
*
|
|
1359
|
+
* @publicApi
|
|
1228
1360
|
*/
|
|
1229
|
-
class
|
|
1230
|
-
constructor(
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1361
|
+
class NavigationError extends RouterEvent {
|
|
1362
|
+
constructor(
|
|
1363
|
+
/** @docsNotRequired */
|
|
1364
|
+
id,
|
|
1365
|
+
/** @docsNotRequired */
|
|
1366
|
+
url,
|
|
1367
|
+
/** @docsNotRequired */
|
|
1368
|
+
error) {
|
|
1369
|
+
super(id, url);
|
|
1370
|
+
this.error = error;
|
|
1371
|
+
this.type = 3 /* EventType.NavigationError */;
|
|
1235
1372
|
}
|
|
1236
|
-
/**
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
parent(t) {
|
|
1240
|
-
const p = this.pathFromRoot(t);
|
|
1241
|
-
return p.length > 1 ? p[p.length - 2] : null;
|
|
1373
|
+
/** @docsNotRequired */
|
|
1374
|
+
toString() {
|
|
1375
|
+
return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;
|
|
1242
1376
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* An event triggered when routes are recognized.
|
|
1380
|
+
*
|
|
1381
|
+
* @publicApi
|
|
1382
|
+
*/
|
|
1383
|
+
class RoutesRecognized extends RouterEvent {
|
|
1384
|
+
constructor(
|
|
1385
|
+
/** @docsNotRequired */
|
|
1386
|
+
id,
|
|
1387
|
+
/** @docsNotRequired */
|
|
1388
|
+
url,
|
|
1389
|
+
/** @docsNotRequired */
|
|
1390
|
+
urlAfterRedirects,
|
|
1391
|
+
/** @docsNotRequired */
|
|
1392
|
+
state) {
|
|
1393
|
+
super(id, url);
|
|
1394
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1395
|
+
this.state = state;
|
|
1396
|
+
this.type = 4 /* EventType.RoutesRecognized */;
|
|
1249
1397
|
}
|
|
1250
|
-
/**
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
firstChild(t) {
|
|
1254
|
-
const n = findNode(t, this._root);
|
|
1255
|
-
return n && n.children.length > 0 ? n.children[0].value : null;
|
|
1398
|
+
/** @docsNotRequired */
|
|
1399
|
+
toString() {
|
|
1400
|
+
return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
1256
1401
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* An event triggered at the start of the Guard phase of routing.
|
|
1405
|
+
*
|
|
1406
|
+
* @see `GuardsCheckEnd`
|
|
1407
|
+
*
|
|
1408
|
+
* @publicApi
|
|
1409
|
+
*/
|
|
1410
|
+
class GuardsCheckStart extends RouterEvent {
|
|
1411
|
+
constructor(
|
|
1412
|
+
/** @docsNotRequired */
|
|
1413
|
+
id,
|
|
1414
|
+
/** @docsNotRequired */
|
|
1415
|
+
url,
|
|
1416
|
+
/** @docsNotRequired */
|
|
1417
|
+
urlAfterRedirects,
|
|
1418
|
+
/** @docsNotRequired */
|
|
1419
|
+
state) {
|
|
1420
|
+
super(id, url);
|
|
1421
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1422
|
+
this.state = state;
|
|
1423
|
+
this.type = 7 /* EventType.GuardsCheckStart */;
|
|
1266
1424
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
*/
|
|
1270
|
-
pathFromRoot(t) {
|
|
1271
|
-
return findPath(t, this._root).map(s => s.value);
|
|
1425
|
+
toString() {
|
|
1426
|
+
return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
1272
1427
|
}
|
|
1273
1428
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1429
|
+
/**
|
|
1430
|
+
* An event triggered at the end of the Guard phase of routing.
|
|
1431
|
+
*
|
|
1432
|
+
* @see `GuardsCheckStart`
|
|
1433
|
+
*
|
|
1434
|
+
* @publicApi
|
|
1435
|
+
*/
|
|
1436
|
+
class GuardsCheckEnd extends RouterEvent {
|
|
1437
|
+
constructor(
|
|
1438
|
+
/** @docsNotRequired */
|
|
1439
|
+
id,
|
|
1440
|
+
/** @docsNotRequired */
|
|
1441
|
+
url,
|
|
1442
|
+
/** @docsNotRequired */
|
|
1443
|
+
urlAfterRedirects,
|
|
1444
|
+
/** @docsNotRequired */
|
|
1445
|
+
state,
|
|
1446
|
+
/** @docsNotRequired */
|
|
1447
|
+
shouldActivate) {
|
|
1448
|
+
super(id, url);
|
|
1449
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1450
|
+
this.state = state;
|
|
1451
|
+
this.shouldActivate = shouldActivate;
|
|
1452
|
+
this.type = 8 /* EventType.GuardsCheckEnd */;
|
|
1453
|
+
}
|
|
1454
|
+
toString() {
|
|
1455
|
+
return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
|
|
1282
1456
|
}
|
|
1283
|
-
return null;
|
|
1284
1457
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1458
|
+
/**
|
|
1459
|
+
* An event triggered at the start of the Resolve phase of routing.
|
|
1460
|
+
*
|
|
1461
|
+
* Runs in the "resolve" phase whether or not there is anything to resolve.
|
|
1462
|
+
* In future, may change to only run when there are things to be resolved.
|
|
1463
|
+
*
|
|
1464
|
+
* @see `ResolveEnd`
|
|
1465
|
+
*
|
|
1466
|
+
* @publicApi
|
|
1467
|
+
*/
|
|
1468
|
+
class ResolveStart extends RouterEvent {
|
|
1469
|
+
constructor(
|
|
1470
|
+
/** @docsNotRequired */
|
|
1471
|
+
id,
|
|
1472
|
+
/** @docsNotRequired */
|
|
1473
|
+
url,
|
|
1474
|
+
/** @docsNotRequired */
|
|
1475
|
+
urlAfterRedirects,
|
|
1476
|
+
/** @docsNotRequired */
|
|
1477
|
+
state) {
|
|
1478
|
+
super(id, url);
|
|
1479
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1480
|
+
this.state = state;
|
|
1481
|
+
this.type = 5 /* EventType.ResolveStart */;
|
|
1482
|
+
}
|
|
1483
|
+
toString() {
|
|
1484
|
+
return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
1295
1485
|
}
|
|
1296
|
-
return [];
|
|
1297
1486
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1487
|
+
/**
|
|
1488
|
+
* An event triggered at the end of the Resolve phase of routing.
|
|
1489
|
+
* @see `ResolveStart`.
|
|
1490
|
+
*
|
|
1491
|
+
* @publicApi
|
|
1492
|
+
*/
|
|
1493
|
+
class ResolveEnd extends RouterEvent {
|
|
1494
|
+
constructor(
|
|
1495
|
+
/** @docsNotRequired */
|
|
1496
|
+
id,
|
|
1497
|
+
/** @docsNotRequired */
|
|
1498
|
+
url,
|
|
1499
|
+
/** @docsNotRequired */
|
|
1500
|
+
urlAfterRedirects,
|
|
1501
|
+
/** @docsNotRequired */
|
|
1502
|
+
state) {
|
|
1503
|
+
super(id, url);
|
|
1504
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1505
|
+
this.state = state;
|
|
1506
|
+
this.type = 6 /* EventType.ResolveEnd */;
|
|
1302
1507
|
}
|
|
1303
1508
|
toString() {
|
|
1304
|
-
return `
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
// Return the list of T indexed by outlet name
|
|
1308
|
-
function nodeChildrenAsMap(node) {
|
|
1309
|
-
const map = {};
|
|
1310
|
-
if (node) {
|
|
1311
|
-
node.children.forEach(child => map[child.value.outlet] = child);
|
|
1509
|
+
return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
1312
1510
|
}
|
|
1313
|
-
return map;
|
|
1314
1511
|
}
|
|
1315
|
-
|
|
1316
1512
|
/**
|
|
1317
|
-
*
|
|
1318
|
-
* Copyright Google LLC All Rights Reserved.
|
|
1513
|
+
* An event triggered before lazy loading a route configuration.
|
|
1319
1514
|
*
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1515
|
+
* @see `RouteConfigLoadEnd`
|
|
1516
|
+
*
|
|
1517
|
+
* @publicApi
|
|
1322
1518
|
*/
|
|
1519
|
+
class RouteConfigLoadStart {
|
|
1520
|
+
constructor(
|
|
1521
|
+
/** @docsNotRequired */
|
|
1522
|
+
route) {
|
|
1523
|
+
this.route = route;
|
|
1524
|
+
this.type = 9 /* EventType.RouteConfigLoadStart */;
|
|
1525
|
+
}
|
|
1526
|
+
toString() {
|
|
1527
|
+
return `RouteConfigLoadStart(path: ${this.route.path})`;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1323
1530
|
/**
|
|
1324
|
-
*
|
|
1325
|
-
*
|
|
1326
|
-
* @usageNotes
|
|
1327
|
-
*
|
|
1328
|
-
* Every node in the route tree is an `ActivatedRoute` instance
|
|
1329
|
-
* that knows about the "consumed" URL segments, the extracted parameters,
|
|
1330
|
-
* and the resolved data.
|
|
1331
|
-
* Use the `ActivatedRoute` properties to traverse the tree from any node.
|
|
1332
|
-
*
|
|
1333
|
-
* The following fragment shows how a component gets the root node
|
|
1334
|
-
* of the current state to establish its own route tree:
|
|
1335
|
-
*
|
|
1336
|
-
* ```
|
|
1337
|
-
* @Component({templateUrl:'template.html'})
|
|
1338
|
-
* class MyComponent {
|
|
1339
|
-
* constructor(router: Router) {
|
|
1340
|
-
* const state: RouterState = router.routerState;
|
|
1341
|
-
* const root: ActivatedRoute = state.root;
|
|
1342
|
-
* const child = root.firstChild;
|
|
1343
|
-
* const id: Observable<string> = child.params.map(p => p.id);
|
|
1344
|
-
* //...
|
|
1345
|
-
* }
|
|
1346
|
-
* }
|
|
1347
|
-
* ```
|
|
1531
|
+
* An event triggered when a route has been lazy loaded.
|
|
1348
1532
|
*
|
|
1349
|
-
* @see `
|
|
1350
|
-
* @see [Getting route information](guide/router#getting-route-information)
|
|
1533
|
+
* @see `RouteConfigLoadStart`
|
|
1351
1534
|
*
|
|
1352
1535
|
* @publicApi
|
|
1353
1536
|
*/
|
|
1354
|
-
class
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
this.snapshot = snapshot;
|
|
1361
|
-
setRouterState(this, root);
|
|
1537
|
+
class RouteConfigLoadEnd {
|
|
1538
|
+
constructor(
|
|
1539
|
+
/** @docsNotRequired */
|
|
1540
|
+
route) {
|
|
1541
|
+
this.route = route;
|
|
1542
|
+
this.type = 10 /* EventType.RouteConfigLoadEnd */;
|
|
1362
1543
|
}
|
|
1363
1544
|
toString() {
|
|
1364
|
-
return this.
|
|
1545
|
+
return `RouteConfigLoadEnd(path: ${this.route.path})`;
|
|
1365
1546
|
}
|
|
1366
1547
|
}
|
|
1367
|
-
function createEmptyState(urlTree, rootComponent) {
|
|
1368
|
-
const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
|
|
1369
|
-
const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
|
|
1370
|
-
const emptyParams = new BehaviorSubject({});
|
|
1371
|
-
const emptyData = new BehaviorSubject({});
|
|
1372
|
-
const emptyQueryParams = new BehaviorSubject({});
|
|
1373
|
-
const fragment = new BehaviorSubject('');
|
|
1374
|
-
const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
|
|
1375
|
-
activated.snapshot = snapshot.root;
|
|
1376
|
-
return new RouterState(new TreeNode(activated, []), snapshot);
|
|
1377
|
-
}
|
|
1378
|
-
function createEmptyStateSnapshot(urlTree, rootComponent) {
|
|
1379
|
-
const emptyParams = {};
|
|
1380
|
-
const emptyData = {};
|
|
1381
|
-
const emptyQueryParams = {};
|
|
1382
|
-
const fragment = '';
|
|
1383
|
-
const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
|
|
1384
|
-
return new RouterStateSnapshot('', new TreeNode(activated, []));
|
|
1385
|
-
}
|
|
1386
1548
|
/**
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
1389
|
-
*
|
|
1390
|
-
*
|
|
1391
|
-
* The following example shows how to construct a component using information from a
|
|
1392
|
-
* currently activated route.
|
|
1393
|
-
*
|
|
1394
|
-
* Note: the observables in this class only emit when the current and previous values differ based
|
|
1395
|
-
* on shallow equality. For example, changing deeply nested properties in resolved `data` will not
|
|
1396
|
-
* cause the `ActivatedRoute.data` `Observable` to emit a new value.
|
|
1397
|
-
*
|
|
1398
|
-
* {@example router/activated-route/module.ts region="activated-route"
|
|
1399
|
-
* header="activated-route.component.ts"}
|
|
1400
|
-
*
|
|
1401
|
-
* @see [Getting route information](guide/router#getting-route-information)
|
|
1549
|
+
* An event triggered at the start of the child-activation
|
|
1550
|
+
* part of the Resolve phase of routing.
|
|
1551
|
+
* @see `ChildActivationEnd`
|
|
1552
|
+
* @see `ResolveStart`
|
|
1402
1553
|
*
|
|
1403
1554
|
* @publicApi
|
|
1404
1555
|
*/
|
|
1405
|
-
class
|
|
1406
|
-
/** @internal */
|
|
1556
|
+
class ChildActivationStart {
|
|
1407
1557
|
constructor(
|
|
1408
|
-
/**
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
/** An observable of the query parameters shared by all the routes. */
|
|
1413
|
-
queryParams,
|
|
1414
|
-
/** An observable of the URL fragment shared by all the routes. */
|
|
1415
|
-
fragment,
|
|
1416
|
-
/** An observable of the static and resolved data of this route. */
|
|
1417
|
-
data,
|
|
1418
|
-
/** The outlet name of the route, a constant. */
|
|
1419
|
-
outlet,
|
|
1420
|
-
/** The component of the route, a constant. */
|
|
1421
|
-
component, futureSnapshot) {
|
|
1422
|
-
this.url = url;
|
|
1423
|
-
this.params = params;
|
|
1424
|
-
this.queryParams = queryParams;
|
|
1425
|
-
this.fragment = fragment;
|
|
1426
|
-
this.data = data;
|
|
1427
|
-
this.outlet = outlet;
|
|
1428
|
-
this.component = component;
|
|
1429
|
-
this._futureSnapshot = futureSnapshot;
|
|
1558
|
+
/** @docsNotRequired */
|
|
1559
|
+
snapshot) {
|
|
1560
|
+
this.snapshot = snapshot;
|
|
1561
|
+
this.type = 11 /* EventType.ChildActivationStart */;
|
|
1430
1562
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
return
|
|
1563
|
+
toString() {
|
|
1564
|
+
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
1565
|
+
return `ChildActivationStart(path: '${path}')`;
|
|
1434
1566
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* An event triggered at the end of the child-activation part
|
|
1570
|
+
* of the Resolve phase of routing.
|
|
1571
|
+
* @see `ChildActivationStart`
|
|
1572
|
+
* @see `ResolveStart`
|
|
1573
|
+
* @publicApi
|
|
1574
|
+
*/
|
|
1575
|
+
class ChildActivationEnd {
|
|
1576
|
+
constructor(
|
|
1577
|
+
/** @docsNotRequired */
|
|
1578
|
+
snapshot) {
|
|
1579
|
+
this.snapshot = snapshot;
|
|
1580
|
+
this.type = 12 /* EventType.ChildActivationEnd */;
|
|
1438
1581
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
return
|
|
1582
|
+
toString() {
|
|
1583
|
+
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
1584
|
+
return `ChildActivationEnd(path: '${path}')`;
|
|
1442
1585
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* An event triggered at the start of the activation part
|
|
1589
|
+
* of the Resolve phase of routing.
|
|
1590
|
+
* @see `ActivationEnd`
|
|
1591
|
+
* @see `ResolveStart`
|
|
1592
|
+
*
|
|
1593
|
+
* @publicApi
|
|
1594
|
+
*/
|
|
1595
|
+
class ActivationStart {
|
|
1596
|
+
constructor(
|
|
1597
|
+
/** @docsNotRequired */
|
|
1598
|
+
snapshot) {
|
|
1599
|
+
this.snapshot = snapshot;
|
|
1600
|
+
this.type = 13 /* EventType.ActivationStart */;
|
|
1446
1601
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
return
|
|
1602
|
+
toString() {
|
|
1603
|
+
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
1604
|
+
return `ActivationStart(path: '${path}')`;
|
|
1450
1605
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* An event triggered at the end of the activation part
|
|
1609
|
+
* of the Resolve phase of routing.
|
|
1610
|
+
* @see `ActivationStart`
|
|
1611
|
+
* @see `ResolveStart`
|
|
1612
|
+
*
|
|
1613
|
+
* @publicApi
|
|
1614
|
+
*/
|
|
1615
|
+
class ActivationEnd {
|
|
1616
|
+
constructor(
|
|
1617
|
+
/** @docsNotRequired */
|
|
1618
|
+
snapshot) {
|
|
1619
|
+
this.snapshot = snapshot;
|
|
1620
|
+
this.type = 14 /* EventType.ActivationEnd */;
|
|
1454
1621
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
* The map supports retrieving single and multiple values from the same parameter.
|
|
1459
|
-
*/
|
|
1460
|
-
get paramMap() {
|
|
1461
|
-
if (!this._paramMap) {
|
|
1462
|
-
this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
|
|
1463
|
-
}
|
|
1464
|
-
return this._paramMap;
|
|
1622
|
+
toString() {
|
|
1623
|
+
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
1624
|
+
return `ActivationEnd(path: '${path}')`;
|
|
1465
1625
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* An event triggered by scrolling.
|
|
1629
|
+
*
|
|
1630
|
+
* @publicApi
|
|
1631
|
+
*/
|
|
1632
|
+
class Scroll {
|
|
1633
|
+
constructor(
|
|
1634
|
+
/** @docsNotRequired */
|
|
1635
|
+
routerEvent,
|
|
1636
|
+
/** @docsNotRequired */
|
|
1637
|
+
position,
|
|
1638
|
+
/** @docsNotRequired */
|
|
1639
|
+
anchor) {
|
|
1640
|
+
this.routerEvent = routerEvent;
|
|
1641
|
+
this.position = position;
|
|
1642
|
+
this.anchor = anchor;
|
|
1643
|
+
this.type = 15 /* EventType.Scroll */;
|
|
1476
1644
|
}
|
|
1477
1645
|
toString() {
|
|
1478
|
-
|
|
1646
|
+
const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
|
|
1647
|
+
return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
|
|
1479
1648
|
}
|
|
1480
1649
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1650
|
+
function stringifyEvent(routerEvent) {
|
|
1651
|
+
if (!('type' in routerEvent)) {
|
|
1652
|
+
return `Unknown Router Event: ${routerEvent.constructor.name}`;
|
|
1653
|
+
}
|
|
1654
|
+
switch (routerEvent.type) {
|
|
1655
|
+
case 14 /* EventType.ActivationEnd */:
|
|
1656
|
+
return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
|
|
1657
|
+
case 13 /* EventType.ActivationStart */:
|
|
1658
|
+
return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
|
|
1659
|
+
case 12 /* EventType.ChildActivationEnd */:
|
|
1660
|
+
return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
|
|
1661
|
+
case 11 /* EventType.ChildActivationStart */:
|
|
1662
|
+
return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
|
|
1663
|
+
case 8 /* EventType.GuardsCheckEnd */:
|
|
1664
|
+
return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
|
|
1665
|
+
case 7 /* EventType.GuardsCheckStart */:
|
|
1666
|
+
return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
1667
|
+
case 2 /* EventType.NavigationCancel */:
|
|
1668
|
+
return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
|
|
1669
|
+
case 1 /* EventType.NavigationEnd */:
|
|
1670
|
+
return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
|
|
1671
|
+
case 3 /* EventType.NavigationError */:
|
|
1672
|
+
return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
|
|
1673
|
+
case 0 /* EventType.NavigationStart */:
|
|
1674
|
+
return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
|
|
1675
|
+
case 6 /* EventType.ResolveEnd */:
|
|
1676
|
+
return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
1677
|
+
case 5 /* EventType.ResolveStart */:
|
|
1678
|
+
return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
1679
|
+
case 10 /* EventType.RouteConfigLoadEnd */:
|
|
1680
|
+
return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
|
|
1681
|
+
case 9 /* EventType.RouteConfigLoadStart */:
|
|
1682
|
+
return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
|
|
1683
|
+
case 4 /* EventType.RoutesRecognized */:
|
|
1684
|
+
return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
1685
|
+
case 15 /* EventType.Scroll */:
|
|
1686
|
+
const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
|
|
1687
|
+
return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
|
|
1506
1688
|
}
|
|
1507
|
-
return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
|
|
1508
|
-
}
|
|
1509
|
-
/** @internal */
|
|
1510
|
-
function flattenInherited(pathFromRoot) {
|
|
1511
|
-
return pathFromRoot.reduce((res, curr) => {
|
|
1512
|
-
const params = { ...res.params, ...curr.params };
|
|
1513
|
-
const data = { ...res.data, ...curr.data };
|
|
1514
|
-
const resolve = { ...curr.data, ...res.resolve, ...curr.routeConfig?.data, ...curr._resolvedData };
|
|
1515
|
-
return { params, data, resolve };
|
|
1516
|
-
}, { params: {}, data: {}, resolve: {} });
|
|
1517
1689
|
}
|
|
1690
|
+
|
|
1518
1691
|
/**
|
|
1519
|
-
* @
|
|
1520
|
-
*
|
|
1521
|
-
* Contains the information about a route associated with a component loaded in an
|
|
1522
|
-
* outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
|
|
1523
|
-
* traverse the router state tree.
|
|
1524
|
-
*
|
|
1525
|
-
* The following example initializes a component with route information extracted
|
|
1526
|
-
* from the snapshot of the root node at the time of creation.
|
|
1527
|
-
*
|
|
1528
|
-
* ```
|
|
1529
|
-
* @Component({templateUrl:'./my-component.html'})
|
|
1530
|
-
* class MyComponent {
|
|
1531
|
-
* constructor(route: ActivatedRoute) {
|
|
1532
|
-
* const id: string = route.snapshot.params.id;
|
|
1533
|
-
* const url: string = route.snapshot.url.join('');
|
|
1534
|
-
* const user = route.snapshot.data.user;
|
|
1535
|
-
* }
|
|
1536
|
-
* }
|
|
1537
|
-
* ```
|
|
1692
|
+
* @license
|
|
1693
|
+
* Copyright Google LLC All Rights Reserved.
|
|
1538
1694
|
*
|
|
1539
|
-
*
|
|
1695
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
1696
|
+
* found in the LICENSE file at https://angular.io/license
|
|
1540
1697
|
*/
|
|
1541
|
-
class
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1698
|
+
class Tree {
|
|
1699
|
+
constructor(root) {
|
|
1700
|
+
this._root = root;
|
|
1701
|
+
}
|
|
1702
|
+
get root() {
|
|
1703
|
+
return this._root.value;
|
|
1704
|
+
}
|
|
1546
1705
|
/**
|
|
1547
|
-
*
|
|
1548
|
-
*
|
|
1549
|
-
* You can compute all params (or data) in the router state or to get params outside
|
|
1550
|
-
* of an activated component by traversing the `RouterState` tree as in the following
|
|
1551
|
-
* example:
|
|
1552
|
-
* ```
|
|
1553
|
-
* collectRouteParams(router: Router) {
|
|
1554
|
-
* let params = {};
|
|
1555
|
-
* let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
|
|
1556
|
-
* while (stack.length > 0) {
|
|
1557
|
-
* const route = stack.pop()!;
|
|
1558
|
-
* params = {...params, ...route.params};
|
|
1559
|
-
* stack.push(...route.children);
|
|
1560
|
-
* }
|
|
1561
|
-
* return params;
|
|
1562
|
-
* }
|
|
1563
|
-
* ```
|
|
1706
|
+
* @internal
|
|
1564
1707
|
*/
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
/** The URL fragment shared by all the routes */
|
|
1569
|
-
fragment,
|
|
1570
|
-
/** The static and resolved data of this route */
|
|
1571
|
-
data,
|
|
1572
|
-
/** The outlet name of the route */
|
|
1573
|
-
outlet,
|
|
1574
|
-
/** The component of the route */
|
|
1575
|
-
component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
|
|
1576
|
-
this.url = url;
|
|
1577
|
-
this.params = params;
|
|
1578
|
-
this.queryParams = queryParams;
|
|
1579
|
-
this.fragment = fragment;
|
|
1580
|
-
this.data = data;
|
|
1581
|
-
this.outlet = outlet;
|
|
1582
|
-
this.component = component;
|
|
1583
|
-
this.routeConfig = routeConfig;
|
|
1584
|
-
this._urlSegment = urlSegment;
|
|
1585
|
-
this._lastPathIndex = lastPathIndex;
|
|
1586
|
-
this._correctedLastPathIndex = correctedLastPathIndex ?? lastPathIndex;
|
|
1587
|
-
this._resolve = resolve;
|
|
1708
|
+
parent(t) {
|
|
1709
|
+
const p = this.pathFromRoot(t);
|
|
1710
|
+
return p.length > 1 ? p[p.length - 2] : null;
|
|
1588
1711
|
}
|
|
1589
|
-
/**
|
|
1590
|
-
|
|
1591
|
-
|
|
1712
|
+
/**
|
|
1713
|
+
* @internal
|
|
1714
|
+
*/
|
|
1715
|
+
children(t) {
|
|
1716
|
+
const n = findNode(t, this._root);
|
|
1717
|
+
return n ? n.children.map(t => t.value) : [];
|
|
1592
1718
|
}
|
|
1593
|
-
/**
|
|
1594
|
-
|
|
1595
|
-
|
|
1719
|
+
/**
|
|
1720
|
+
* @internal
|
|
1721
|
+
*/
|
|
1722
|
+
firstChild(t) {
|
|
1723
|
+
const n = findNode(t, this._root);
|
|
1724
|
+
return n && n.children.length > 0 ? n.children[0].value : null;
|
|
1596
1725
|
}
|
|
1597
|
-
/**
|
|
1598
|
-
|
|
1599
|
-
|
|
1726
|
+
/**
|
|
1727
|
+
* @internal
|
|
1728
|
+
*/
|
|
1729
|
+
siblings(t) {
|
|
1730
|
+
const p = findPath(t, this._root);
|
|
1731
|
+
if (p.length < 2)
|
|
1732
|
+
return [];
|
|
1733
|
+
const c = p[p.length - 2].children.map(c => c.value);
|
|
1734
|
+
return c.filter(cc => cc !== t);
|
|
1600
1735
|
}
|
|
1601
|
-
/**
|
|
1602
|
-
|
|
1603
|
-
|
|
1736
|
+
/**
|
|
1737
|
+
* @internal
|
|
1738
|
+
*/
|
|
1739
|
+
pathFromRoot(t) {
|
|
1740
|
+
return findPath(t, this._root).map(s => s.value);
|
|
1604
1741
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1742
|
+
}
|
|
1743
|
+
// DFS for the node matching the value
|
|
1744
|
+
function findNode(value, node) {
|
|
1745
|
+
if (value === node.value)
|
|
1746
|
+
return node;
|
|
1747
|
+
for (const child of node.children) {
|
|
1748
|
+
const node = findNode(value, child);
|
|
1749
|
+
if (node)
|
|
1750
|
+
return node;
|
|
1608
1751
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1752
|
+
return null;
|
|
1753
|
+
}
|
|
1754
|
+
// Return the path to the node with the given value using DFS
|
|
1755
|
+
function findPath(value, node) {
|
|
1756
|
+
if (value === node.value)
|
|
1757
|
+
return [node];
|
|
1758
|
+
for (const child of node.children) {
|
|
1759
|
+
const path = findPath(value, child);
|
|
1760
|
+
if (path.length) {
|
|
1761
|
+
path.unshift(node);
|
|
1762
|
+
return path;
|
|
1612
1763
|
}
|
|
1613
|
-
return this._paramMap;
|
|
1614
1764
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1765
|
+
return [];
|
|
1766
|
+
}
|
|
1767
|
+
class TreeNode {
|
|
1768
|
+
constructor(value, children) {
|
|
1769
|
+
this.value = value;
|
|
1770
|
+
this.children = children;
|
|
1620
1771
|
}
|
|
1621
1772
|
toString() {
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1773
|
+
return `TreeNode(${this.value})`;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
// Return the list of T indexed by outlet name
|
|
1777
|
+
function nodeChildrenAsMap(node) {
|
|
1778
|
+
const map = {};
|
|
1779
|
+
if (node) {
|
|
1780
|
+
node.children.forEach(child => map[child.value.outlet] = child);
|
|
1625
1781
|
}
|
|
1782
|
+
return map;
|
|
1626
1783
|
}
|
|
1784
|
+
|
|
1627
1785
|
/**
|
|
1628
|
-
* @
|
|
1786
|
+
* @license
|
|
1787
|
+
* Copyright Google LLC All Rights Reserved.
|
|
1629
1788
|
*
|
|
1630
|
-
*
|
|
1789
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
1790
|
+
* found in the LICENSE file at https://angular.io/license
|
|
1791
|
+
*/
|
|
1792
|
+
/**
|
|
1793
|
+
* Represents the state of the router as a tree of activated routes.
|
|
1794
|
+
*
|
|
1795
|
+
* @usageNotes
|
|
1631
1796
|
*
|
|
1632
|
-
*
|
|
1633
|
-
* the "consumed" URL segments, the extracted parameters,
|
|
1797
|
+
* Every node in the route tree is an `ActivatedRoute` instance
|
|
1798
|
+
* that knows about the "consumed" URL segments, the extracted parameters,
|
|
1799
|
+
* and the resolved data.
|
|
1800
|
+
* Use the `ActivatedRoute` properties to traverse the tree from any node.
|
|
1634
1801
|
*
|
|
1635
|
-
* The following
|
|
1636
|
-
*
|
|
1802
|
+
* The following fragment shows how a component gets the root node
|
|
1803
|
+
* of the current state to establish its own route tree:
|
|
1637
1804
|
*
|
|
1638
1805
|
* ```
|
|
1639
1806
|
* @Component({templateUrl:'template.html'})
|
|
1640
1807
|
* class MyComponent {
|
|
1641
1808
|
* constructor(router: Router) {
|
|
1642
1809
|
* const state: RouterState = router.routerState;
|
|
1643
|
-
* const
|
|
1644
|
-
* const root: ActivatedRouteSnapshot = snapshot.root;
|
|
1810
|
+
* const root: ActivatedRoute = state.root;
|
|
1645
1811
|
* const child = root.firstChild;
|
|
1646
1812
|
* const id: Observable<string> = child.params.map(p => p.id);
|
|
1647
1813
|
* //...
|
|
@@ -1649,423 +1815,418 @@ class ActivatedRouteSnapshot {
|
|
|
1649
1815
|
* }
|
|
1650
1816
|
* ```
|
|
1651
1817
|
*
|
|
1818
|
+
* @see `ActivatedRoute`
|
|
1819
|
+
* @see [Getting route information](guide/router#getting-route-information)
|
|
1820
|
+
*
|
|
1652
1821
|
* @publicApi
|
|
1653
1822
|
*/
|
|
1654
|
-
class
|
|
1823
|
+
class RouterState extends Tree {
|
|
1655
1824
|
/** @internal */
|
|
1656
|
-
constructor(
|
|
1657
|
-
/** The
|
|
1658
|
-
|
|
1825
|
+
constructor(root,
|
|
1826
|
+
/** The current snapshot of the router state */
|
|
1827
|
+
snapshot) {
|
|
1659
1828
|
super(root);
|
|
1660
|
-
this.
|
|
1829
|
+
this.snapshot = snapshot;
|
|
1661
1830
|
setRouterState(this, root);
|
|
1662
1831
|
}
|
|
1663
1832
|
toString() {
|
|
1664
|
-
return
|
|
1833
|
+
return this.snapshot.toString();
|
|
1665
1834
|
}
|
|
1666
1835
|
}
|
|
1667
|
-
function
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
const
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
* So we push new values into the observables only when they are not the initial values.
|
|
1678
|
-
* And we detect that by checking if the snapshot field is set.
|
|
1679
|
-
*/
|
|
1680
|
-
function advanceActivatedRoute(route) {
|
|
1681
|
-
if (route.snapshot) {
|
|
1682
|
-
const currentSnapshot = route.snapshot;
|
|
1683
|
-
const nextSnapshot = route._futureSnapshot;
|
|
1684
|
-
route.snapshot = nextSnapshot;
|
|
1685
|
-
if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
|
|
1686
|
-
route.queryParams.next(nextSnapshot.queryParams);
|
|
1687
|
-
}
|
|
1688
|
-
if (currentSnapshot.fragment !== nextSnapshot.fragment) {
|
|
1689
|
-
route.fragment.next(nextSnapshot.fragment);
|
|
1690
|
-
}
|
|
1691
|
-
if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
|
|
1692
|
-
route.params.next(nextSnapshot.params);
|
|
1693
|
-
}
|
|
1694
|
-
if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
|
|
1695
|
-
route.url.next(nextSnapshot.url);
|
|
1696
|
-
}
|
|
1697
|
-
if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
|
|
1698
|
-
route.data.next(nextSnapshot.data);
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
else {
|
|
1702
|
-
route.snapshot = route._futureSnapshot;
|
|
1703
|
-
// this is for resolved data
|
|
1704
|
-
route.data.next(route._futureSnapshot.data);
|
|
1705
|
-
}
|
|
1836
|
+
function createEmptyState(urlTree, rootComponent) {
|
|
1837
|
+
const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
|
|
1838
|
+
const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
|
|
1839
|
+
const emptyParams = new BehaviorSubject({});
|
|
1840
|
+
const emptyData = new BehaviorSubject({});
|
|
1841
|
+
const emptyQueryParams = new BehaviorSubject({});
|
|
1842
|
+
const fragment = new BehaviorSubject('');
|
|
1843
|
+
const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
|
|
1844
|
+
activated.snapshot = snapshot.root;
|
|
1845
|
+
return new RouterState(new TreeNode(activated, []), snapshot);
|
|
1706
1846
|
}
|
|
1707
|
-
function
|
|
1708
|
-
const
|
|
1709
|
-
const
|
|
1710
|
-
|
|
1711
|
-
|
|
1847
|
+
function createEmptyStateSnapshot(urlTree, rootComponent) {
|
|
1848
|
+
const emptyParams = {};
|
|
1849
|
+
const emptyData = {};
|
|
1850
|
+
const emptyQueryParams = {};
|
|
1851
|
+
const fragment = '';
|
|
1852
|
+
const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
|
|
1853
|
+
return new RouterStateSnapshot('', new TreeNode(activated, []));
|
|
1712
1854
|
}
|
|
1713
|
-
|
|
1714
1855
|
/**
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1856
|
+
* Provides access to information about a route associated with a component
|
|
1857
|
+
* that is loaded in an outlet.
|
|
1858
|
+
* Use to traverse the `RouterState` tree and extract information from nodes.
|
|
1717
1859
|
*
|
|
1718
|
-
*
|
|
1719
|
-
*
|
|
1720
|
-
*/
|
|
1721
|
-
function createRouterState(routeReuseStrategy, curr, prevState) {
|
|
1722
|
-
const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
|
|
1723
|
-
return new RouterState(root, curr);
|
|
1724
|
-
}
|
|
1725
|
-
function createNode(routeReuseStrategy, curr, prevState) {
|
|
1726
|
-
// reuse an activated route that is currently displayed on the screen
|
|
1727
|
-
if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
|
|
1728
|
-
const value = prevState.value;
|
|
1729
|
-
value._futureSnapshot = curr.value;
|
|
1730
|
-
const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
|
|
1731
|
-
return new TreeNode(value, children);
|
|
1732
|
-
}
|
|
1733
|
-
else {
|
|
1734
|
-
if (routeReuseStrategy.shouldAttach(curr.value)) {
|
|
1735
|
-
// retrieve an activated route that is used to be displayed, but is not currently displayed
|
|
1736
|
-
const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
|
|
1737
|
-
if (detachedRouteHandle !== null) {
|
|
1738
|
-
const tree = detachedRouteHandle.route;
|
|
1739
|
-
tree.value._futureSnapshot = curr.value;
|
|
1740
|
-
tree.children = curr.children.map(c => createNode(routeReuseStrategy, c));
|
|
1741
|
-
return tree;
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
const value = createActivatedRoute(curr.value);
|
|
1745
|
-
const children = curr.children.map(c => createNode(routeReuseStrategy, c));
|
|
1746
|
-
return new TreeNode(value, children);
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
|
|
1750
|
-
return curr.children.map(child => {
|
|
1751
|
-
for (const p of prevState.children) {
|
|
1752
|
-
if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {
|
|
1753
|
-
return createNode(routeReuseStrategy, child, p);
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
return createNode(routeReuseStrategy, child);
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
function createActivatedRoute(c) {
|
|
1760
|
-
return new ActivatedRoute(new BehaviorSubject(c.url), new BehaviorSubject(c.params), new BehaviorSubject(c.queryParams), new BehaviorSubject(c.fragment), new BehaviorSubject(c.data), c.outlet, c.component, c);
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
/**
|
|
1764
|
-
* @license
|
|
1765
|
-
* Copyright Google LLC All Rights Reserved.
|
|
1860
|
+
* The following example shows how to construct a component using information from a
|
|
1861
|
+
* currently activated route.
|
|
1766
1862
|
*
|
|
1767
|
-
*
|
|
1768
|
-
*
|
|
1863
|
+
* Note: the observables in this class only emit when the current and previous values differ based
|
|
1864
|
+
* on shallow equality. For example, changing deeply nested properties in resolved `data` will not
|
|
1865
|
+
* cause the `ActivatedRoute.data` `Observable` to emit a new value.
|
|
1866
|
+
*
|
|
1867
|
+
* {@example router/activated-route/module.ts region="activated-route"
|
|
1868
|
+
* header="activated-route.component.ts"}
|
|
1869
|
+
*
|
|
1870
|
+
* @see [Getting route information](guide/router#getting-route-information)
|
|
1871
|
+
*
|
|
1872
|
+
* @publicApi
|
|
1769
1873
|
*/
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
}
|
|
1874
|
+
class ActivatedRoute {
|
|
1875
|
+
/** @internal */
|
|
1876
|
+
constructor(
|
|
1877
|
+
/** An observable of the URL segments matched by this route. */
|
|
1878
|
+
url,
|
|
1879
|
+
/** An observable of the matrix parameters scoped to this route. */
|
|
1880
|
+
params,
|
|
1881
|
+
/** An observable of the query parameters shared by all the routes. */
|
|
1882
|
+
queryParams,
|
|
1883
|
+
/** An observable of the URL fragment shared by all the routes. */
|
|
1884
|
+
fragment,
|
|
1885
|
+
/** An observable of the static and resolved data of this route. */
|
|
1886
|
+
data,
|
|
1887
|
+
/** The outlet name of the route, a constant. */
|
|
1888
|
+
outlet,
|
|
1889
|
+
/** The component of the route, a constant. */
|
|
1890
|
+
component, futureSnapshot) {
|
|
1891
|
+
this.url = url;
|
|
1892
|
+
this.params = params;
|
|
1893
|
+
this.queryParams = queryParams;
|
|
1894
|
+
this.fragment = fragment;
|
|
1895
|
+
this.data = data;
|
|
1896
|
+
this.outlet = outlet;
|
|
1897
|
+
this.component = component;
|
|
1898
|
+
this._futureSnapshot = futureSnapshot;
|
|
1796
1899
|
}
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
|
|
1801
|
-
}
|
|
1802
|
-
/**
|
|
1803
|
-
* Determines if a given command has an `outlets` map. When we encounter a command
|
|
1804
|
-
* with an outlets k/v map, we need to apply each outlet individually to the existing segment.
|
|
1805
|
-
*/
|
|
1806
|
-
function isCommandWithOutlets(command) {
|
|
1807
|
-
return typeof command === 'object' && command != null && command.outlets;
|
|
1808
|
-
}
|
|
1809
|
-
function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) {
|
|
1810
|
-
let qp = {};
|
|
1811
|
-
if (queryParams) {
|
|
1812
|
-
forEach(queryParams, (value, name) => {
|
|
1813
|
-
qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
|
|
1814
|
-
});
|
|
1900
|
+
/** The configuration used to match this route. */
|
|
1901
|
+
get routeConfig() {
|
|
1902
|
+
return this._futureSnapshot.routeConfig;
|
|
1815
1903
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1904
|
+
/** The root of the router state. */
|
|
1905
|
+
get root() {
|
|
1906
|
+
return this._routerState.root;
|
|
1818
1907
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1908
|
+
/** The parent of this route in the router state tree. */
|
|
1909
|
+
get parent() {
|
|
1910
|
+
return this._routerState.parent(this);
|
|
1911
|
+
}
|
|
1912
|
+
/** The first child of this route in the router state tree. */
|
|
1913
|
+
get firstChild() {
|
|
1914
|
+
return this._routerState.firstChild(this);
|
|
1915
|
+
}
|
|
1916
|
+
/** The children of this route in the router state tree. */
|
|
1917
|
+
get children() {
|
|
1918
|
+
return this._routerState.children(this);
|
|
1919
|
+
}
|
|
1920
|
+
/** The path from the root of the router state tree to this route. */
|
|
1921
|
+
get pathFromRoot() {
|
|
1922
|
+
return this._routerState.pathFromRoot(this);
|
|
1923
|
+
}
|
|
1924
|
+
/**
|
|
1925
|
+
* An Observable that contains a map of the required and optional parameters
|
|
1926
|
+
* specific to the route.
|
|
1927
|
+
* The map supports retrieving single and multiple values from the same parameter.
|
|
1928
|
+
*/
|
|
1929
|
+
get paramMap() {
|
|
1930
|
+
if (!this._paramMap) {
|
|
1931
|
+
this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
|
|
1841
1932
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1933
|
+
return this._paramMap;
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* An Observable that contains a map of the query parameters available to all routes.
|
|
1937
|
+
* The map supports retrieving single and multiple values from the query parameter.
|
|
1938
|
+
*/
|
|
1939
|
+
get queryParamMap() {
|
|
1940
|
+
if (!this._queryParamMap) {
|
|
1941
|
+
this._queryParamMap =
|
|
1942
|
+
this.queryParams.pipe(map((p) => convertToParamMap(p)));
|
|
1845
1943
|
}
|
|
1944
|
+
return this._queryParamMap;
|
|
1846
1945
|
}
|
|
1847
|
-
|
|
1848
|
-
return this.
|
|
1946
|
+
toString() {
|
|
1947
|
+
return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
|
|
1849
1948
|
}
|
|
1850
1949
|
}
|
|
1851
|
-
/**
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1950
|
+
/**
|
|
1951
|
+
* Returns the inherited params, data, and resolve for a given route.
|
|
1952
|
+
* By default, this only inherits values up to the nearest path-less or component-less route.
|
|
1953
|
+
* @internal
|
|
1954
|
+
*/
|
|
1955
|
+
function inheritedParamsDataResolve(route, paramsInheritanceStrategy = 'emptyOnly') {
|
|
1956
|
+
const pathFromRoot = route.pathFromRoot;
|
|
1957
|
+
let inheritingStartingFrom = 0;
|
|
1958
|
+
if (paramsInheritanceStrategy !== 'always') {
|
|
1959
|
+
inheritingStartingFrom = pathFromRoot.length - 1;
|
|
1960
|
+
while (inheritingStartingFrom >= 1) {
|
|
1961
|
+
const current = pathFromRoot[inheritingStartingFrom];
|
|
1962
|
+
const parent = pathFromRoot[inheritingStartingFrom - 1];
|
|
1963
|
+
// current route is an empty path => inherits its parent's params and data
|
|
1964
|
+
if (current.routeConfig && current.routeConfig.path === '') {
|
|
1965
|
+
inheritingStartingFrom--;
|
|
1966
|
+
// parent is componentless => current route should inherit its params and data
|
|
1866
1967
|
}
|
|
1867
|
-
if (
|
|
1868
|
-
|
|
1968
|
+
else if (!parent.component) {
|
|
1969
|
+
inheritingStartingFrom--;
|
|
1970
|
+
}
|
|
1971
|
+
else {
|
|
1972
|
+
break;
|
|
1869
1973
|
}
|
|
1870
1974
|
}
|
|
1871
|
-
if (!(typeof cmd === 'string')) {
|
|
1872
|
-
return [...res, cmd];
|
|
1873
|
-
}
|
|
1874
|
-
if (cmdIdx === 0) {
|
|
1875
|
-
cmd.split('/').forEach((urlPart, partIndex) => {
|
|
1876
|
-
if (partIndex == 0 && urlPart === '.') {
|
|
1877
|
-
// skip './a'
|
|
1878
|
-
}
|
|
1879
|
-
else if (partIndex == 0 && urlPart === '') { // '/a'
|
|
1880
|
-
isAbsolute = true;
|
|
1881
|
-
}
|
|
1882
|
-
else if (urlPart === '..') { // '../a'
|
|
1883
|
-
numberOfDoubleDots++;
|
|
1884
|
-
}
|
|
1885
|
-
else if (urlPart != '') {
|
|
1886
|
-
res.push(urlPart);
|
|
1887
|
-
}
|
|
1888
|
-
});
|
|
1889
|
-
return res;
|
|
1890
|
-
}
|
|
1891
|
-
return [...res, cmd];
|
|
1892
|
-
}, []);
|
|
1893
|
-
return new Navigation(isAbsolute, numberOfDoubleDots, res);
|
|
1894
|
-
}
|
|
1895
|
-
class Position {
|
|
1896
|
-
constructor(segmentGroup, processChildren, index) {
|
|
1897
|
-
this.segmentGroup = segmentGroup;
|
|
1898
|
-
this.processChildren = processChildren;
|
|
1899
|
-
this.index = index;
|
|
1900
1975
|
}
|
|
1976
|
+
return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
|
|
1901
1977
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
const processChildren = segmentGroup === tree.root;
|
|
1911
|
-
return new Position(segmentGroup, processChildren, 0);
|
|
1912
|
-
}
|
|
1913
|
-
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
|
|
1914
|
-
const index = lastPathIndex + modifier;
|
|
1915
|
-
return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
|
|
1978
|
+
/** @internal */
|
|
1979
|
+
function flattenInherited(pathFromRoot) {
|
|
1980
|
+
return pathFromRoot.reduce((res, curr) => {
|
|
1981
|
+
const params = { ...res.params, ...curr.params };
|
|
1982
|
+
const data = { ...res.data, ...curr.data };
|
|
1983
|
+
const resolve = { ...curr.data, ...res.resolve, ...curr.routeConfig?.data, ...curr._resolvedData };
|
|
1984
|
+
return { params, data, resolve };
|
|
1985
|
+
}, { params: {}, data: {}, resolve: {} });
|
|
1916
1986
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1987
|
+
/**
|
|
1988
|
+
* @description
|
|
1989
|
+
*
|
|
1990
|
+
* Contains the information about a route associated with a component loaded in an
|
|
1991
|
+
* outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
|
|
1992
|
+
* traverse the router state tree.
|
|
1993
|
+
*
|
|
1994
|
+
* The following example initializes a component with route information extracted
|
|
1995
|
+
* from the snapshot of the root node at the time of creation.
|
|
1996
|
+
*
|
|
1997
|
+
* ```
|
|
1998
|
+
* @Component({templateUrl:'./my-component.html'})
|
|
1999
|
+
* class MyComponent {
|
|
2000
|
+
* constructor(route: ActivatedRoute) {
|
|
2001
|
+
* const id: string = route.snapshot.params.id;
|
|
2002
|
+
* const url: string = route.snapshot.url.join('');
|
|
2003
|
+
* const user = route.snapshot.data.user;
|
|
2004
|
+
* }
|
|
2005
|
+
* }
|
|
2006
|
+
* ```
|
|
2007
|
+
*
|
|
2008
|
+
* @publicApi
|
|
2009
|
+
*/
|
|
2010
|
+
class ActivatedRouteSnapshot {
|
|
2011
|
+
/** @internal */
|
|
2012
|
+
constructor(
|
|
2013
|
+
/** The URL segments matched by this route */
|
|
2014
|
+
url,
|
|
2015
|
+
/**
|
|
2016
|
+
* The matrix parameters scoped to this route.
|
|
2017
|
+
*
|
|
2018
|
+
* You can compute all params (or data) in the router state or to get params outside
|
|
2019
|
+
* of an activated component by traversing the `RouterState` tree as in the following
|
|
2020
|
+
* example:
|
|
2021
|
+
* ```
|
|
2022
|
+
* collectRouteParams(router: Router) {
|
|
2023
|
+
* let params = {};
|
|
2024
|
+
* let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
|
|
2025
|
+
* while (stack.length > 0) {
|
|
2026
|
+
* const route = stack.pop()!;
|
|
2027
|
+
* params = {...params, ...route.params};
|
|
2028
|
+
* stack.push(...route.children);
|
|
2029
|
+
* }
|
|
2030
|
+
* return params;
|
|
2031
|
+
* }
|
|
2032
|
+
* ```
|
|
2033
|
+
*/
|
|
2034
|
+
params,
|
|
2035
|
+
/** The query parameters shared by all the routes */
|
|
2036
|
+
queryParams,
|
|
2037
|
+
/** The URL fragment shared by all the routes */
|
|
2038
|
+
fragment,
|
|
2039
|
+
/** The static and resolved data of this route */
|
|
2040
|
+
data,
|
|
2041
|
+
/** The outlet name of the route */
|
|
2042
|
+
outlet,
|
|
2043
|
+
/** The component of the route */
|
|
2044
|
+
component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
|
|
2045
|
+
this.url = url;
|
|
2046
|
+
this.params = params;
|
|
2047
|
+
this.queryParams = queryParams;
|
|
2048
|
+
this.fragment = fragment;
|
|
2049
|
+
this.data = data;
|
|
2050
|
+
this.outlet = outlet;
|
|
2051
|
+
this.component = component;
|
|
2052
|
+
this.routeConfig = routeConfig;
|
|
2053
|
+
this._urlSegment = urlSegment;
|
|
2054
|
+
this._lastPathIndex = lastPathIndex;
|
|
2055
|
+
this._correctedLastPathIndex = correctedLastPathIndex ?? lastPathIndex;
|
|
2056
|
+
this._resolve = resolve;
|
|
1928
2057
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
if (isCommandWithOutlets(commands[0])) {
|
|
1933
|
-
return commands[0].outlets;
|
|
2058
|
+
/** The root of the router state */
|
|
2059
|
+
get root() {
|
|
2060
|
+
return this._routerState.root;
|
|
1934
2061
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
if (!segmentGroup) {
|
|
1939
|
-
segmentGroup = new UrlSegmentGroup([], {});
|
|
2062
|
+
/** The parent of this route in the router state tree */
|
|
2063
|
+
get parent() {
|
|
2064
|
+
return this._routerState.parent(this);
|
|
1940
2065
|
}
|
|
1941
|
-
|
|
1942
|
-
|
|
2066
|
+
/** The first child of this route in the router state tree */
|
|
2067
|
+
get firstChild() {
|
|
2068
|
+
return this._routerState.firstChild(this);
|
|
1943
2069
|
}
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
|
|
1948
|
-
g.children[PRIMARY_OUTLET] =
|
|
1949
|
-
new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
|
|
1950
|
-
return updateSegmentGroupChildren(g, 0, slicedCommands);
|
|
2070
|
+
/** The children of this route in the router state tree */
|
|
2071
|
+
get children() {
|
|
2072
|
+
return this._routerState.children(this);
|
|
1951
2073
|
}
|
|
1952
|
-
|
|
1953
|
-
|
|
2074
|
+
/** The path from the root of the router state tree to this route */
|
|
2075
|
+
get pathFromRoot() {
|
|
2076
|
+
return this._routerState.pathFromRoot(this);
|
|
1954
2077
|
}
|
|
1955
|
-
|
|
1956
|
-
|
|
2078
|
+
get paramMap() {
|
|
2079
|
+
if (!this._paramMap) {
|
|
2080
|
+
this._paramMap = convertToParamMap(this.params);
|
|
2081
|
+
}
|
|
2082
|
+
return this._paramMap;
|
|
1957
2083
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
2084
|
+
get queryParamMap() {
|
|
2085
|
+
if (!this._queryParamMap) {
|
|
2086
|
+
this._queryParamMap = convertToParamMap(this.queryParams);
|
|
2087
|
+
}
|
|
2088
|
+
return this._queryParamMap;
|
|
1960
2089
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
2090
|
+
toString() {
|
|
2091
|
+
const url = this.url.map(segment => segment.toString()).join('/');
|
|
2092
|
+
const matched = this.routeConfig ? this.routeConfig.path : '';
|
|
2093
|
+
return `Route(url:'${url}', path:'${matched}')`;
|
|
1963
2094
|
}
|
|
1964
2095
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
2096
|
+
/**
|
|
2097
|
+
* @description
|
|
2098
|
+
*
|
|
2099
|
+
* Represents the state of the router at a moment in time.
|
|
2100
|
+
*
|
|
2101
|
+
* This is a tree of activated route snapshots. Every node in this tree knows about
|
|
2102
|
+
* the "consumed" URL segments, the extracted parameters, and the resolved data.
|
|
2103
|
+
*
|
|
2104
|
+
* The following example shows how a component is initialized with information
|
|
2105
|
+
* from the snapshot of the root node's state at the time of creation.
|
|
2106
|
+
*
|
|
2107
|
+
* ```
|
|
2108
|
+
* @Component({templateUrl:'template.html'})
|
|
2109
|
+
* class MyComponent {
|
|
2110
|
+
* constructor(router: Router) {
|
|
2111
|
+
* const state: RouterState = router.routerState;
|
|
2112
|
+
* const snapshot: RouterStateSnapshot = state.snapshot;
|
|
2113
|
+
* const root: ActivatedRouteSnapshot = snapshot.root;
|
|
2114
|
+
* const child = root.firstChild;
|
|
2115
|
+
* const id: Observable<string> = child.params.map(p => p.id);
|
|
2116
|
+
* //...
|
|
2117
|
+
* }
|
|
2118
|
+
* }
|
|
2119
|
+
* ```
|
|
2120
|
+
*
|
|
2121
|
+
* @publicApi
|
|
2122
|
+
*/
|
|
2123
|
+
class RouterStateSnapshot extends Tree {
|
|
2124
|
+
/** @internal */
|
|
2125
|
+
constructor(
|
|
2126
|
+
/** The url from which this snapshot was created */
|
|
2127
|
+
url, root) {
|
|
2128
|
+
super(root);
|
|
2129
|
+
this.url = url;
|
|
2130
|
+
setRouterState(this, root);
|
|
1968
2131
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
const children = {};
|
|
1972
|
-
forEach(outlets, (commands, outlet) => {
|
|
1973
|
-
if (typeof commands === 'string') {
|
|
1974
|
-
commands = [commands];
|
|
1975
|
-
}
|
|
1976
|
-
if (commands !== null) {
|
|
1977
|
-
children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
|
|
1978
|
-
}
|
|
1979
|
-
});
|
|
1980
|
-
forEach(segmentGroup.children, (child, childOutlet) => {
|
|
1981
|
-
if (outlets[childOutlet] === undefined) {
|
|
1982
|
-
children[childOutlet] = child;
|
|
1983
|
-
}
|
|
1984
|
-
});
|
|
1985
|
-
return new UrlSegmentGroup(segmentGroup.segments, children);
|
|
2132
|
+
toString() {
|
|
2133
|
+
return serializeNode(this._root);
|
|
1986
2134
|
}
|
|
1987
2135
|
}
|
|
1988
|
-
function
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
|
|
1992
|
-
while (currentPathIndex < segmentGroup.segments.length) {
|
|
1993
|
-
if (currentCommandIndex >= commands.length)
|
|
1994
|
-
return noMatch;
|
|
1995
|
-
const path = segmentGroup.segments[currentPathIndex];
|
|
1996
|
-
const command = commands[currentCommandIndex];
|
|
1997
|
-
// Do not try to consume command as part of the prefixing if it has outlets because it can
|
|
1998
|
-
// contain outlets other than the one being processed. Consuming the outlets command would
|
|
1999
|
-
// result in other outlets being ignored.
|
|
2000
|
-
if (isCommandWithOutlets(command)) {
|
|
2001
|
-
break;
|
|
2002
|
-
}
|
|
2003
|
-
const curr = `${command}`;
|
|
2004
|
-
const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
|
|
2005
|
-
if (currentPathIndex > 0 && curr === undefined)
|
|
2006
|
-
break;
|
|
2007
|
-
if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
|
|
2008
|
-
if (!compare(curr, next, path))
|
|
2009
|
-
return noMatch;
|
|
2010
|
-
currentCommandIndex += 2;
|
|
2011
|
-
}
|
|
2012
|
-
else {
|
|
2013
|
-
if (!compare(curr, {}, path))
|
|
2014
|
-
return noMatch;
|
|
2015
|
-
currentCommandIndex++;
|
|
2016
|
-
}
|
|
2017
|
-
currentPathIndex++;
|
|
2018
|
-
}
|
|
2019
|
-
return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
|
|
2136
|
+
function setRouterState(state, node) {
|
|
2137
|
+
node.value._routerState = state;
|
|
2138
|
+
node.children.forEach(c => setRouterState(state, c));
|
|
2020
2139
|
}
|
|
2021
|
-
function
|
|
2022
|
-
const
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2140
|
+
function serializeNode(node) {
|
|
2141
|
+
const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';
|
|
2142
|
+
return `${node.value}${c}`;
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* The expectation is that the activate route is created with the right set of parameters.
|
|
2146
|
+
* So we push new values into the observables only when they are not the initial values.
|
|
2147
|
+
* And we detect that by checking if the snapshot field is set.
|
|
2148
|
+
*/
|
|
2149
|
+
function advanceActivatedRoute(route) {
|
|
2150
|
+
if (route.snapshot) {
|
|
2151
|
+
const currentSnapshot = route.snapshot;
|
|
2152
|
+
const nextSnapshot = route._futureSnapshot;
|
|
2153
|
+
route.snapshot = nextSnapshot;
|
|
2154
|
+
if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
|
|
2155
|
+
route.queryParams.next(nextSnapshot.queryParams);
|
|
2029
2156
|
}
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
const p = segmentGroup.segments[startIndex];
|
|
2033
|
-
paths.push(new UrlSegment(p.path, stringify(commands[0])));
|
|
2034
|
-
i++;
|
|
2035
|
-
continue;
|
|
2157
|
+
if (currentSnapshot.fragment !== nextSnapshot.fragment) {
|
|
2158
|
+
route.fragment.next(nextSnapshot.fragment);
|
|
2036
2159
|
}
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
if (curr && next && isMatrixParams(next)) {
|
|
2040
|
-
paths.push(new UrlSegment(curr, stringify(next)));
|
|
2041
|
-
i += 2;
|
|
2160
|
+
if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
|
|
2161
|
+
route.params.next(nextSnapshot.params);
|
|
2042
2162
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2163
|
+
if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
|
|
2164
|
+
route.url.next(nextSnapshot.url);
|
|
2165
|
+
}
|
|
2166
|
+
if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
|
|
2167
|
+
route.data.next(nextSnapshot.data);
|
|
2046
2168
|
}
|
|
2047
2169
|
}
|
|
2048
|
-
|
|
2170
|
+
else {
|
|
2171
|
+
route.snapshot = route._futureSnapshot;
|
|
2172
|
+
// this is for resolved data
|
|
2173
|
+
route.data.next(route._futureSnapshot.data);
|
|
2174
|
+
}
|
|
2049
2175
|
}
|
|
2050
|
-
function
|
|
2051
|
-
const
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2176
|
+
function equalParamsAndUrlSegments(a, b) {
|
|
2177
|
+
const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url);
|
|
2178
|
+
const parentsMismatch = !a.parent !== !b.parent;
|
|
2179
|
+
return equalUrlParams && !parentsMismatch &&
|
|
2180
|
+
(!a.parent || equalParamsAndUrlSegments(a.parent, b.parent));
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
/**
|
|
2184
|
+
* @license
|
|
2185
|
+
* Copyright Google LLC All Rights Reserved.
|
|
2186
|
+
*
|
|
2187
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
2188
|
+
* found in the LICENSE file at https://angular.io/license
|
|
2189
|
+
*/
|
|
2190
|
+
function createRouterState(routeReuseStrategy, curr, prevState) {
|
|
2191
|
+
const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
|
|
2192
|
+
return new RouterState(root, curr);
|
|
2193
|
+
}
|
|
2194
|
+
function createNode(routeReuseStrategy, curr, prevState) {
|
|
2195
|
+
// reuse an activated route that is currently displayed on the screen
|
|
2196
|
+
if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
|
|
2197
|
+
const value = prevState.value;
|
|
2198
|
+
value._futureSnapshot = curr.value;
|
|
2199
|
+
const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
|
|
2200
|
+
return new TreeNode(value, children);
|
|
2201
|
+
}
|
|
2202
|
+
else {
|
|
2203
|
+
if (routeReuseStrategy.shouldAttach(curr.value)) {
|
|
2204
|
+
// retrieve an activated route that is used to be displayed, but is not currently displayed
|
|
2205
|
+
const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
|
|
2206
|
+
if (detachedRouteHandle !== null) {
|
|
2207
|
+
const tree = detachedRouteHandle.route;
|
|
2208
|
+
tree.value._futureSnapshot = curr.value;
|
|
2209
|
+
tree.children = curr.children.map(c => createNode(routeReuseStrategy, c));
|
|
2210
|
+
return tree;
|
|
2211
|
+
}
|
|
2055
2212
|
}
|
|
2056
|
-
|
|
2057
|
-
|
|
2213
|
+
const value = createActivatedRoute(curr.value);
|
|
2214
|
+
const children = curr.children.map(c => createNode(routeReuseStrategy, c));
|
|
2215
|
+
return new TreeNode(value, children);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
|
|
2219
|
+
return curr.children.map(child => {
|
|
2220
|
+
for (const p of prevState.children) {
|
|
2221
|
+
if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {
|
|
2222
|
+
return createNode(routeReuseStrategy, child, p);
|
|
2223
|
+
}
|
|
2058
2224
|
}
|
|
2225
|
+
return createNode(routeReuseStrategy, child);
|
|
2059
2226
|
});
|
|
2060
|
-
return children;
|
|
2061
|
-
}
|
|
2062
|
-
function stringify(params) {
|
|
2063
|
-
const res = {};
|
|
2064
|
-
forEach(params, (v, k) => res[k] = `${v}`);
|
|
2065
|
-
return res;
|
|
2066
2227
|
}
|
|
2067
|
-
function
|
|
2068
|
-
return
|
|
2228
|
+
function createActivatedRoute(c) {
|
|
2229
|
+
return new ActivatedRoute(new BehaviorSubject(c.url), new BehaviorSubject(c.params), new BehaviorSubject(c.queryParams), new BehaviorSubject(c.fragment), new BehaviorSubject(c.data), c.outlet, c.component, c);
|
|
2069
2230
|
}
|
|
2070
2231
|
|
|
2071
2232
|
/**
|
|
@@ -2328,9 +2489,9 @@ class RouterOutlet {
|
|
|
2328
2489
|
this.activateEvents.emit(this.activated.instance);
|
|
2329
2490
|
}
|
|
2330
2491
|
}
|
|
2331
|
-
RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
2332
|
-
RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.
|
|
2333
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
2492
|
+
RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterOutlet, deps: [{ token: ChildrenOutletContexts }, { token: i0.ViewContainerRef }, { token: 'name', attribute: true }, { token: i0.ChangeDetectorRef }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Directive });
|
|
2493
|
+
RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterOutlet, selector: "router-outlet", outputs: { activateEvents: "activate", deactivateEvents: "deactivate", attachEvents: "attach", detachEvents: "detach" }, exportAs: ["outlet"], ngImport: i0 });
|
|
2494
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterOutlet, decorators: [{
|
|
2334
2495
|
type: Directive,
|
|
2335
2496
|
args: [{ selector: 'router-outlet', exportAs: 'outlet' }]
|
|
2336
2497
|
}], ctorParameters: function () { return [{ type: ChildrenOutletContexts }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
|
|
@@ -2387,9 +2548,9 @@ function isComponentFactoryResolver(item) {
|
|
|
2387
2548
|
*/
|
|
2388
2549
|
class ɵEmptyOutletComponent {
|
|
2389
2550
|
}
|
|
2390
|
-
ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
2391
|
-
ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.
|
|
2392
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
2551
|
+
ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: ɵEmptyOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2552
|
+
ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.0-next.0", type: ɵEmptyOutletComponent, selector: "ng-component", ngImport: i0, template: `<router-outlet></router-outlet>`, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
|
|
2553
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: ɵEmptyOutletComponent, decorators: [{
|
|
2393
2554
|
type: Component,
|
|
2394
2555
|
args: [{ template: `<router-outlet></router-outlet>` }]
|
|
2395
2556
|
}] });
|
|
@@ -3073,9 +3234,7 @@ class ApplyRedirects {
|
|
|
3073
3234
|
return new Error(`Cannot match any routes. URL Segment: '${e.segmentGroup}'`);
|
|
3074
3235
|
}
|
|
3075
3236
|
createUrlTree(rootCandidate, queryParams, fragment) {
|
|
3076
|
-
const root = rootCandidate
|
|
3077
|
-
new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
|
|
3078
|
-
rootCandidate;
|
|
3237
|
+
const root = createRoot(rootCandidate);
|
|
3079
3238
|
return new UrlTree(root, queryParams, fragment);
|
|
3080
3239
|
}
|
|
3081
3240
|
expandSegmentGroup(injector, routes, segmentGroup, outlet) {
|
|
@@ -3327,39 +3486,6 @@ class ApplyRedirects {
|
|
|
3327
3486
|
return redirectToUrlSegment;
|
|
3328
3487
|
}
|
|
3329
3488
|
}
|
|
3330
|
-
/**
|
|
3331
|
-
* When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
|
|
3332
|
-
*
|
|
3333
|
-
* When a segment group has only one child which is a primary outlet, merges that child into the
|
|
3334
|
-
* parent. That is, the child segment group's segments are merged into the `s` and the child's
|
|
3335
|
-
* children become the children of `s`. Think of this like a 'squash', merging the child segment
|
|
3336
|
-
* group into the parent.
|
|
3337
|
-
*/
|
|
3338
|
-
function mergeTrivialChildren(s) {
|
|
3339
|
-
if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
|
|
3340
|
-
const c = s.children[PRIMARY_OUTLET];
|
|
3341
|
-
return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
|
|
3342
|
-
}
|
|
3343
|
-
return s;
|
|
3344
|
-
}
|
|
3345
|
-
/**
|
|
3346
|
-
* Recursively merges primary segment children into their parents and also drops empty children
|
|
3347
|
-
* (those which have no segments and no children themselves). The latter prevents serializing a
|
|
3348
|
-
* group into something like `/a(aux:)`, where `aux` is an empty child segment.
|
|
3349
|
-
*/
|
|
3350
|
-
function squashSegmentGroup(segmentGroup) {
|
|
3351
|
-
const newChildren = {};
|
|
3352
|
-
for (const childOutlet of Object.keys(segmentGroup.children)) {
|
|
3353
|
-
const child = segmentGroup.children[childOutlet];
|
|
3354
|
-
const childCandidate = squashSegmentGroup(child);
|
|
3355
|
-
// don't add empty children
|
|
3356
|
-
if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
|
|
3357
|
-
newChildren[childOutlet] = childCandidate;
|
|
3358
|
-
}
|
|
3359
|
-
}
|
|
3360
|
-
const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
|
|
3361
|
-
return mergeTrivialChildren(s);
|
|
3362
|
-
}
|
|
3363
3489
|
|
|
3364
3490
|
/**
|
|
3365
3491
|
* @license
|
|
@@ -4186,9 +4312,9 @@ class RouterConfigLoader {
|
|
|
4186
4312
|
}));
|
|
4187
4313
|
}
|
|
4188
4314
|
}
|
|
4189
|
-
RouterConfigLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
4190
|
-
RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
4191
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
4315
|
+
RouterConfigLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader, deps: [{ token: i0.Injector }, { token: i0.Compiler }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4316
|
+
RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader });
|
|
4317
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader, decorators: [{
|
|
4192
4318
|
type: Injectable
|
|
4193
4319
|
}], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.Compiler }]; } });
|
|
4194
4320
|
|
|
@@ -5193,9 +5319,9 @@ class Router {
|
|
|
5193
5319
|
return { navigationId };
|
|
5194
5320
|
}
|
|
5195
5321
|
}
|
|
5196
|
-
Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5197
|
-
Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
5198
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5322
|
+
Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
|
|
5323
|
+
Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router });
|
|
5324
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router, decorators: [{
|
|
5199
5325
|
type: Injectable
|
|
5200
5326
|
}], ctorParameters: function () { return [{ type: i0.Type }, { type: UrlSerializer }, { type: ChildrenOutletContexts }, { type: i3.Location }, { type: i0.Injector }, { type: i0.Compiler }, { type: undefined }]; } });
|
|
5201
5327
|
function validateCommands(commands) {
|
|
@@ -5394,9 +5520,9 @@ class RouterLink {
|
|
|
5394
5520
|
});
|
|
5395
5521
|
}
|
|
5396
5522
|
}
|
|
5397
|
-
RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5398
|
-
RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.
|
|
5399
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5523
|
+
RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLink, deps: [{ token: Router }, { token: ActivatedRoute }, { token: 'tabindex', attribute: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
5524
|
+
RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLink, selector: ":not(a):not(area)[routerLink]", inputs: { queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick()" } }, usesOnChanges: true, ngImport: i0 });
|
|
5525
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLink, decorators: [{
|
|
5400
5526
|
type: Directive,
|
|
5401
5527
|
args: [{ selector: ':not(a):not(area)[routerLink]' }]
|
|
5402
5528
|
}], ctorParameters: function () { return [{ type: Router }, { type: ActivatedRoute }, { type: undefined, decorators: [{
|
|
@@ -5513,9 +5639,9 @@ class RouterLinkWithHref {
|
|
|
5513
5639
|
});
|
|
5514
5640
|
}
|
|
5515
5641
|
}
|
|
5516
|
-
RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5517
|
-
RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.
|
|
5518
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5642
|
+
RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkWithHref, deps: [{ token: Router }, { token: ActivatedRoute }, { token: i3.LocationStrategy }], target: i0.ɵɵFactoryTarget.Directive });
|
|
5643
|
+
RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: { target: "target", queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick($event.button,$event.ctrlKey,$event.shiftKey,$event.altKey,$event.metaKey)" }, properties: { "attr.target": "this.target", "attr.href": "this.href" } }, usesOnChanges: true, ngImport: i0 });
|
|
5644
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkWithHref, decorators: [{
|
|
5519
5645
|
type: Directive,
|
|
5520
5646
|
args: [{ selector: 'a[routerLink],area[routerLink]' }]
|
|
5521
5647
|
}], ctorParameters: function () { return [{ type: Router }, { type: ActivatedRoute }, { type: i3.LocationStrategy }]; }, propDecorators: { target: [{
|
|
@@ -5740,9 +5866,9 @@ class RouterLinkActive {
|
|
|
5740
5866
|
this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
|
|
5741
5867
|
}
|
|
5742
5868
|
}
|
|
5743
|
-
RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5744
|
-
RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.
|
|
5745
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5869
|
+
RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkActive, deps: [{ token: Router }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: RouterLink, optional: true }, { token: RouterLinkWithHref, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
|
|
5870
|
+
RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: { routerLinkActiveOptions: "routerLinkActiveOptions", ariaCurrentWhenActive: "ariaCurrentWhenActive", routerLinkActive: "routerLinkActive" }, outputs: { isActiveChange: "isActiveChange" }, queries: [{ propertyName: "links", predicate: RouterLink, descendants: true }, { propertyName: "linksWithHrefs", predicate: RouterLinkWithHref, descendants: true }], exportAs: ["routerLinkActive"], usesOnChanges: true, ngImport: i0 });
|
|
5871
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkActive, decorators: [{
|
|
5746
5872
|
type: Directive,
|
|
5747
5873
|
args: [{
|
|
5748
5874
|
selector: '[routerLinkActive]',
|
|
@@ -5845,9 +5971,9 @@ class DefaultTitleStrategy extends TitleStrategy {
|
|
|
5845
5971
|
}
|
|
5846
5972
|
}
|
|
5847
5973
|
}
|
|
5848
|
-
DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5849
|
-
DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
5850
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5974
|
+
DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, deps: [{ token: i1.Title }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5975
|
+
DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, providedIn: 'root' });
|
|
5976
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, decorators: [{
|
|
5851
5977
|
type: Injectable,
|
|
5852
5978
|
args: [{ providedIn: 'root' }]
|
|
5853
5979
|
}], ctorParameters: function () { return [{ type: i1.Title }]; } });
|
|
@@ -5980,9 +6106,9 @@ class RouterPreloader {
|
|
|
5980
6106
|
});
|
|
5981
6107
|
}
|
|
5982
6108
|
}
|
|
5983
|
-
RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5984
|
-
RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
5985
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
6109
|
+
RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader, deps: [{ token: Router }, { token: i0.Compiler }, { token: i0.EnvironmentInjector }, { token: PreloadingStrategy }, { token: RouterConfigLoader }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6110
|
+
RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader });
|
|
6111
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader, decorators: [{
|
|
5986
6112
|
type: Injectable
|
|
5987
6113
|
}], ctorParameters: function () { return [{ type: Router }, { type: i0.Compiler }, { type: i0.EnvironmentInjector }, { type: PreloadingStrategy }, { type: RouterConfigLoader }]; } });
|
|
5988
6114
|
|
|
@@ -6068,9 +6194,9 @@ class RouterScroller {
|
|
|
6068
6194
|
}
|
|
6069
6195
|
}
|
|
6070
6196
|
}
|
|
6071
|
-
RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
6072
|
-
RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
6073
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
6197
|
+
RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
|
|
6198
|
+
RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller });
|
|
6199
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller, decorators: [{
|
|
6074
6200
|
type: Injectable
|
|
6075
6201
|
}], ctorParameters: function () { return [{ type: Router }, { type: i3.ViewportScroller }, { type: undefined }]; } });
|
|
6076
6202
|
|
|
@@ -6212,10 +6338,10 @@ class RouterModule {
|
|
|
6212
6338
|
return { ngModule: RouterModule, providers: [provideRoutes(routes)] };
|
|
6213
6339
|
}
|
|
6214
6340
|
}
|
|
6215
|
-
RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
6216
|
-
RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.
|
|
6217
|
-
RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.
|
|
6218
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
6341
|
+
RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, deps: [{ token: ROUTER_FORROOT_GUARD, optional: true }, { token: Router, optional: true }], target: i0.ɵɵFactoryTarget.NgModule });
|
|
6342
|
+
RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, declarations: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent], exports: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent] });
|
|
6343
|
+
RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule });
|
|
6344
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, decorators: [{
|
|
6219
6345
|
type: NgModule,
|
|
6220
6346
|
args: [{
|
|
6221
6347
|
declarations: ROUTER_DIRECTIVES,
|
|
@@ -6392,9 +6518,9 @@ class RouterInitializer {
|
|
|
6392
6518
|
this.destroyed = true;
|
|
6393
6519
|
}
|
|
6394
6520
|
}
|
|
6395
|
-
RouterInitializer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
6396
|
-
RouterInitializer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
6397
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
6521
|
+
RouterInitializer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6522
|
+
RouterInitializer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer });
|
|
6523
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer, decorators: [{
|
|
6398
6524
|
type: Injectable
|
|
6399
6525
|
}], ctorParameters: function () { return [{ type: i0.Injector }]; } });
|
|
6400
6526
|
function getAppInitializer(r) {
|
|
@@ -6434,7 +6560,7 @@ function provideRouterInitializer() {
|
|
|
6434
6560
|
/**
|
|
6435
6561
|
* @publicApi
|
|
6436
6562
|
*/
|
|
6437
|
-
const VERSION = new Version('14.0.
|
|
6563
|
+
const VERSION = new Version('14.1.0-next.0');
|
|
6438
6564
|
|
|
6439
6565
|
/**
|
|
6440
6566
|
* @license
|
|
@@ -6473,5 +6599,5 @@ const VERSION = new Version('14.0.1');
|
|
|
6473
6599
|
* Generated bundle index. Do not edit.
|
|
6474
6600
|
*/
|
|
6475
6601
|
|
|
6476
|
-
export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, BaseRouteReuseStrategy, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultTitleStrategy, DefaultUrlSerializer, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, NoPreloading, OutletContext, PRIMARY_OUTLET, PreloadAllModules, PreloadingStrategy, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, ROUTES, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterLink, RouterLinkActive, RouterLinkWithHref, RouterModule, RouterOutlet, RouterPreloader, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, TitleStrategy, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VERSION, convertToParamMap, provideRoutes, ɵEmptyOutletComponent, ROUTER_PROVIDERS as ɵROUTER_PROVIDERS, assignExtraOptionsToRouter as ɵassignExtraOptionsToRouter, flatten as ɵflatten };
|
|
6602
|
+
export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, BaseRouteReuseStrategy, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultTitleStrategy, DefaultUrlSerializer, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, NoPreloading, OutletContext, PRIMARY_OUTLET, PreloadAllModules, PreloadingStrategy, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, ROUTES, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterLink, RouterLinkActive, RouterLinkWithHref, RouterModule, RouterOutlet, RouterPreloader, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, TitleStrategy, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VERSION, convertToParamMap, createUrlTreeFromSnapshot, provideRoutes, ɵEmptyOutletComponent, ROUTER_PROVIDERS as ɵROUTER_PROVIDERS, assignExtraOptionsToRouter as ɵassignExtraOptionsToRouter, flatten as ɵflatten };
|
|
6477
6603
|
//# sourceMappingURL=router.mjs.map
|