@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/fesm2015/router.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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
|
*/
|
|
@@ -20,1630 +20,1797 @@ 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;
|
|
79
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
|
+
}
|
|
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
|
-
this.urlAfterRedirects = urlAfterRedirects;
|
|
263
|
-
this.state = state;
|
|
264
|
-
this.type = 5 /* EventType.ResolveStart */;
|
|
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;
|
|
265
322
|
}
|
|
266
|
-
|
|
267
|
-
|
|
323
|
+
get queryParamMap() {
|
|
324
|
+
if (!this._queryParamMap) {
|
|
325
|
+
this._queryParamMap = convertToParamMap(this.queryParams);
|
|
326
|
+
}
|
|
327
|
+
return this._queryParamMap;
|
|
268
328
|
}
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* An event triggered at the end of the Resolve phase of routing.
|
|
272
|
-
* @see `ResolveStart`.
|
|
273
|
-
*
|
|
274
|
-
* @publicApi
|
|
275
|
-
*/
|
|
276
|
-
class ResolveEnd extends RouterEvent {
|
|
277
|
-
constructor(
|
|
278
|
-
/** @docsNotRequired */
|
|
279
|
-
id,
|
|
280
|
-
/** @docsNotRequired */
|
|
281
|
-
url,
|
|
282
|
-
/** @docsNotRequired */
|
|
283
|
-
urlAfterRedirects,
|
|
284
329
|
/** @docsNotRequired */
|
|
285
|
-
state) {
|
|
286
|
-
super(id, url);
|
|
287
|
-
this.urlAfterRedirects = urlAfterRedirects;
|
|
288
|
-
this.state = state;
|
|
289
|
-
this.type = 6 /* EventType.ResolveEnd */;
|
|
290
|
-
}
|
|
291
330
|
toString() {
|
|
292
|
-
return
|
|
331
|
+
return DEFAULT_SERIALIZER.serialize(this);
|
|
293
332
|
}
|
|
294
333
|
}
|
|
295
334
|
/**
|
|
296
|
-
*
|
|
335
|
+
* @description
|
|
297
336
|
*
|
|
298
|
-
*
|
|
337
|
+
* Represents the parsed URL segment group.
|
|
338
|
+
*
|
|
339
|
+
* See `UrlTree` for more information.
|
|
299
340
|
*
|
|
300
341
|
* @publicApi
|
|
301
342
|
*/
|
|
302
|
-
class
|
|
343
|
+
class UrlSegmentGroup {
|
|
303
344
|
constructor(
|
|
304
|
-
/**
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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;
|
|
308
362
|
}
|
|
363
|
+
/** @docsNotRequired */
|
|
309
364
|
toString() {
|
|
310
|
-
return
|
|
365
|
+
return serializePaths(this);
|
|
311
366
|
}
|
|
312
367
|
}
|
|
313
368
|
/**
|
|
314
|
-
*
|
|
369
|
+
* @description
|
|
315
370
|
*
|
|
316
|
-
*
|
|
371
|
+
* Represents a single URL segment.
|
|
372
|
+
*
|
|
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.
|
|
375
|
+
*
|
|
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;
|
|
326
402
|
}
|
|
403
|
+
get parameterMap() {
|
|
404
|
+
if (!this._parameterMap) {
|
|
405
|
+
this._parameterMap = convertToParamMap(this.parameters);
|
|
406
|
+
}
|
|
407
|
+
return this._parameterMap;
|
|
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
|
-
if (!('type' in routerEvent)) {
|
|
436
|
-
return `Unknown Router Event: ${routerEvent.constructor.name}`;
|
|
437
|
-
}
|
|
438
|
-
switch (routerEvent.type) {
|
|
439
|
-
case 14 /* EventType.ActivationEnd */:
|
|
440
|
-
return `ActivationEnd(path: '${((_a = routerEvent.snapshot.routeConfig) === null || _a === void 0 ? void 0 : _a.path) || ''}')`;
|
|
441
|
-
case 13 /* EventType.ActivationStart */:
|
|
442
|
-
return `ActivationStart(path: '${((_b = routerEvent.snapshot.routeConfig) === null || _b === void 0 ? void 0 : _b.path) || ''}')`;
|
|
443
|
-
case 12 /* EventType.ChildActivationEnd */:
|
|
444
|
-
return `ChildActivationEnd(path: '${((_c = routerEvent.snapshot.routeConfig) === null || _c === void 0 ? void 0 : _c.path) || ''}')`;
|
|
445
|
-
case 11 /* EventType.ChildActivationStart */:
|
|
446
|
-
return `ChildActivationStart(path: '${((_d = routerEvent.snapshot.routeConfig) === null || _d === void 0 ? void 0 : _d.path) || ''}')`;
|
|
447
|
-
case 8 /* EventType.GuardsCheckEnd */:
|
|
448
|
-
return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
|
|
449
|
-
case 7 /* EventType.GuardsCheckStart */:
|
|
450
|
-
return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
451
|
-
case 2 /* EventType.NavigationCancel */:
|
|
452
|
-
return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
|
|
453
|
-
case 1 /* EventType.NavigationEnd */:
|
|
454
|
-
return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
|
|
455
|
-
case 3 /* EventType.NavigationError */:
|
|
456
|
-
return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
|
|
457
|
-
case 0 /* EventType.NavigationStart */:
|
|
458
|
-
return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
|
|
459
|
-
case 6 /* EventType.ResolveEnd */:
|
|
460
|
-
return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
461
|
-
case 5 /* EventType.ResolveStart */:
|
|
462
|
-
return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
463
|
-
case 10 /* EventType.RouteConfigLoadEnd */:
|
|
464
|
-
return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
|
|
465
|
-
case 9 /* EventType.RouteConfigLoadStart */:
|
|
466
|
-
return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
|
|
467
|
-
case 4 /* EventType.RoutesRecognized */:
|
|
468
|
-
return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
469
|
-
case 15 /* EventType.Scroll */:
|
|
470
|
-
const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
|
|
471
|
-
return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
|
|
472
|
-
}
|
|
557
|
+
function decode(s) {
|
|
558
|
+
return decodeURIComponent(s);
|
|
473
559
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
*
|
|
479
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
480
|
-
* found in the LICENSE file at https://angular.io/license
|
|
481
|
-
*/
|
|
482
|
-
/**
|
|
483
|
-
* The primary routing outlet.
|
|
484
|
-
*
|
|
485
|
-
* @publicApi
|
|
486
|
-
*/
|
|
487
|
-
const PRIMARY_OUTLET = 'primary';
|
|
488
|
-
class ParamsAsMap {
|
|
489
|
-
constructor(params) {
|
|
490
|
-
this.params = params || {};
|
|
491
|
-
}
|
|
492
|
-
has(name) {
|
|
493
|
-
return Object.prototype.hasOwnProperty.call(this.params, name);
|
|
494
|
-
}
|
|
495
|
-
get(name) {
|
|
496
|
-
if (this.has(name)) {
|
|
497
|
-
const v = this.params[name];
|
|
498
|
-
return Array.isArray(v) ? v[0] : v;
|
|
499
|
-
}
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
getAll(name) {
|
|
503
|
-
if (this.has(name)) {
|
|
504
|
-
const v = this.params[name];
|
|
505
|
-
return Array.isArray(v) ? v : [v];
|
|
506
|
-
}
|
|
507
|
-
return [];
|
|
508
|
-
}
|
|
509
|
-
get keys() {
|
|
510
|
-
return Object.keys(this.params);
|
|
511
|
-
}
|
|
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'));
|
|
512
564
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
* @param params The instance to convert.
|
|
516
|
-
* @returns The new map instance.
|
|
517
|
-
*
|
|
518
|
-
* @publicApi
|
|
519
|
-
*/
|
|
520
|
-
function convertToParamMap(params) {
|
|
521
|
-
return new ParamsAsMap(params);
|
|
565
|
+
function serializePath(path) {
|
|
566
|
+
return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`;
|
|
522
567
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return error;
|
|
568
|
+
function serializeMatrixParams(params) {
|
|
569
|
+
return Object.keys(params)
|
|
570
|
+
.map(key => `;${encodeUriSegment(key)}=${encodeUriSegment(params[key])}`)
|
|
571
|
+
.join('');
|
|
528
572
|
}
|
|
529
|
-
function
|
|
530
|
-
|
|
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('&')}` : '';
|
|
531
583
|
}
|
|
532
|
-
|
|
533
|
-
function
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
// The actual URL is shorter than the config, no match
|
|
537
|
-
return null;
|
|
538
|
-
}
|
|
539
|
-
if (route.pathMatch === 'full' &&
|
|
540
|
-
(segmentGroup.hasChildren() || parts.length < segments.length)) {
|
|
541
|
-
// The config is longer than the actual URL but we are looking for a full match, return null
|
|
542
|
-
return null;
|
|
543
|
-
}
|
|
544
|
-
const posParams = {};
|
|
545
|
-
// Check each config part against the actual URL
|
|
546
|
-
for (let index = 0; index < parts.length; index++) {
|
|
547
|
-
const part = parts[index];
|
|
548
|
-
const segment = segments[index];
|
|
549
|
-
const isParameter = part.startsWith(':');
|
|
550
|
-
if (isParameter) {
|
|
551
|
-
posParams[part.substring(1)] = segment;
|
|
552
|
-
}
|
|
553
|
-
else if (part !== segment.path) {
|
|
554
|
-
// The actual URL part does not match the config, no match
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
return { consumed: segments.slice(0, parts.length), posParams };
|
|
584
|
+
const SEGMENT_RE = /^[^\/()?;=#]+/;
|
|
585
|
+
function matchSegments(str) {
|
|
586
|
+
const match = str.match(SEGMENT_RE);
|
|
587
|
+
return match ? match[0] : '';
|
|
559
588
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
566
|
-
* found in the LICENSE file at https://angular.io/license
|
|
567
|
-
*/
|
|
568
|
-
function shallowEqualArrays(a, b) {
|
|
569
|
-
if (a.length !== b.length)
|
|
570
|
-
return false;
|
|
571
|
-
for (let i = 0; i < a.length; ++i) {
|
|
572
|
-
if (!shallowEqual(a[i], b[i]))
|
|
573
|
-
return false;
|
|
574
|
-
}
|
|
575
|
-
return true;
|
|
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] : '';
|
|
576
594
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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;
|
|
584
605
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
return false;
|
|
606
|
+
parseRootSegment() {
|
|
607
|
+
this.consumeOptional('/');
|
|
608
|
+
if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
|
|
609
|
+
return new UrlSegmentGroup([], {});
|
|
590
610
|
}
|
|
611
|
+
// The root segment group never has segments
|
|
612
|
+
return new UrlSegmentGroup([], this.parseChildren());
|
|
591
613
|
}
|
|
592
|
-
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return false;
|
|
601
|
-
const aSorted = [...a].sort();
|
|
602
|
-
const bSorted = [...b].sort();
|
|
603
|
-
return aSorted.every((val, index) => bSorted[index] === val);
|
|
614
|
+
parseQueryParams() {
|
|
615
|
+
const params = {};
|
|
616
|
+
if (this.consumeOptional('?')) {
|
|
617
|
+
do {
|
|
618
|
+
this.parseQueryParam(params);
|
|
619
|
+
} while (this.consumeOptional('&'));
|
|
620
|
+
}
|
|
621
|
+
return params;
|
|
604
622
|
}
|
|
605
|
-
|
|
606
|
-
return
|
|
623
|
+
parseFragment() {
|
|
624
|
+
return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;
|
|
607
625
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
if (map.hasOwnProperty(prop)) {
|
|
630
|
-
callback(map[prop], prop);
|
|
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);
|
|
631
647
|
}
|
|
648
|
+
if (segments.length > 0 || Object.keys(children).length > 0) {
|
|
649
|
+
res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
|
|
650
|
+
}
|
|
651
|
+
return res;
|
|
632
652
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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());
|
|
637
662
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
663
|
+
parseMatrixParams() {
|
|
664
|
+
const params = {};
|
|
665
|
+
while (this.consumeOptional(';')) {
|
|
666
|
+
this.parseParam(params);
|
|
667
|
+
}
|
|
668
|
+
return params;
|
|
643
669
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
'subset': containsSegmentGroup,
|
|
660
|
-
};
|
|
661
|
-
const paramCompareMap = {
|
|
662
|
-
'exact': equalParams,
|
|
663
|
-
'subset': containsParams,
|
|
664
|
-
'ignored': () => true,
|
|
665
|
-
};
|
|
666
|
-
function containsTree(container, containee, options) {
|
|
667
|
-
return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&
|
|
668
|
-
paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&
|
|
669
|
-
!(options.fragment === 'exact' && container.fragment !== containee.fragment);
|
|
670
|
-
}
|
|
671
|
-
function equalParams(container, containee) {
|
|
672
|
-
// TODO: This does not handle array params correctly.
|
|
673
|
-
return shallowEqual(container, containee);
|
|
674
|
-
}
|
|
675
|
-
function equalSegmentGroups(container, containee, matrixParams) {
|
|
676
|
-
if (!equalPath(container.segments, containee.segments))
|
|
677
|
-
return false;
|
|
678
|
-
if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {
|
|
679
|
-
return false;
|
|
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);
|
|
680
685
|
}
|
|
681
|
-
|
|
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
|
+
}
|
|
682
754
|
return false;
|
|
683
|
-
for (const c in containee.children) {
|
|
684
|
-
if (!container.children[c])
|
|
685
|
-
return false;
|
|
686
|
-
if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))
|
|
687
|
-
return false;
|
|
688
755
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
756
|
+
capture(str) {
|
|
757
|
+
if (!this.consumeOptional(str)) {
|
|
758
|
+
throw new Error(`Expected "${str}".`);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
694
761
|
}
|
|
695
|
-
function
|
|
696
|
-
return
|
|
762
|
+
function createRoot(rootCandidate) {
|
|
763
|
+
return rootCandidate.segments.length > 0 ?
|
|
764
|
+
new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
|
|
765
|
+
rootCandidate;
|
|
697
766
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
return false;
|
|
712
|
-
if (!matrixParamsMatch(container.segments, containeePaths, matrixParams))
|
|
713
|
-
return false;
|
|
714
|
-
for (const c in containee.children) {
|
|
715
|
-
if (!container.children[c])
|
|
716
|
-
return false;
|
|
717
|
-
if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {
|
|
718
|
-
return false;
|
|
719
|
-
}
|
|
767
|
+
/**
|
|
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.
|
|
771
|
+
*/
|
|
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;
|
|
720
780
|
}
|
|
721
|
-
return true;
|
|
722
|
-
}
|
|
723
|
-
else {
|
|
724
|
-
const current = containeePaths.slice(0, container.segments.length);
|
|
725
|
-
const next = containeePaths.slice(container.segments.length);
|
|
726
|
-
if (!equalPath(container.segments, current))
|
|
727
|
-
return false;
|
|
728
|
-
if (!matrixParamsMatch(container.segments, current, matrixParams))
|
|
729
|
-
return false;
|
|
730
|
-
if (!container.children[PRIMARY_OUTLET])
|
|
731
|
-
return false;
|
|
732
|
-
return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams);
|
|
733
781
|
}
|
|
782
|
+
const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
|
|
783
|
+
return mergeTrivialChildren(s);
|
|
734
784
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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);
|
|
797
|
+
}
|
|
798
|
+
return s;
|
|
739
799
|
}
|
|
800
|
+
|
|
740
801
|
/**
|
|
741
|
-
* @
|
|
802
|
+
* @license
|
|
803
|
+
* Copyright Google LLC All Rights Reserved.
|
|
742
804
|
*
|
|
743
|
-
*
|
|
805
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
806
|
+
* found in the LICENSE file at https://angular.io/license
|
|
807
|
+
*/
|
|
808
|
+
/**
|
|
809
|
+
* Creates a `UrlTree` relative to an `ActivatedRouteSnapshot`.
|
|
810
|
+
*
|
|
811
|
+
* @publicApi
|
|
744
812
|
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
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.
|
|
748
822
|
*
|
|
749
823
|
* @usageNotes
|
|
750
|
-
* ### Example
|
|
751
824
|
*
|
|
752
825
|
* ```
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
* constructor(router: Router) {
|
|
756
|
-
* const tree: UrlTree =
|
|
757
|
-
* router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
|
|
758
|
-
* const f = tree.fragment; // return 'fragment'
|
|
759
|
-
* const q = tree.queryParams; // returns {debug: 'true'}
|
|
760
|
-
* const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
|
|
761
|
-
* const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
|
|
762
|
-
* g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
|
|
763
|
-
* g.children['support'].segments; // return 1 segment 'help'
|
|
764
|
-
* }
|
|
765
|
-
* }
|
|
766
|
-
* ```
|
|
826
|
+
* // create /team/33/user/11
|
|
827
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team', 33, 'user', 11]);
|
|
767
828
|
*
|
|
768
|
-
*
|
|
769
|
-
|
|
770
|
-
class UrlTree {
|
|
771
|
-
/** @internal */
|
|
772
|
-
constructor(
|
|
773
|
-
/** The root segment group of the URL tree */
|
|
774
|
-
root,
|
|
775
|
-
/** The query params of the URL */
|
|
776
|
-
queryParams,
|
|
777
|
-
/** The fragment of the URL */
|
|
778
|
-
fragment) {
|
|
779
|
-
this.root = root;
|
|
780
|
-
this.queryParams = queryParams;
|
|
781
|
-
this.fragment = fragment;
|
|
782
|
-
}
|
|
783
|
-
get queryParamMap() {
|
|
784
|
-
if (!this._queryParamMap) {
|
|
785
|
-
this._queryParamMap = convertToParamMap(this.queryParams);
|
|
786
|
-
}
|
|
787
|
-
return this._queryParamMap;
|
|
788
|
-
}
|
|
789
|
-
/** @docsNotRequired */
|
|
790
|
-
toString() {
|
|
791
|
-
return DEFAULT_SERIALIZER.serialize(this);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
/**
|
|
795
|
-
* @description
|
|
829
|
+
* // create /team/33;expand=true/user/11
|
|
830
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team', 33, {expand: true}, 'user', 11]);
|
|
796
831
|
*
|
|
797
|
-
*
|
|
832
|
+
* // you can collapse static segments like this (this works only with the first passed-in value):
|
|
833
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team/33/user', userId]);
|
|
798
834
|
*
|
|
799
|
-
*
|
|
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'}]);
|
|
800
838
|
*
|
|
801
|
-
*
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
constructor(
|
|
805
|
-
/** The URL segments of this group. See `UrlSegment` for more information */
|
|
806
|
-
segments,
|
|
807
|
-
/** The list of children of this group */
|
|
808
|
-
children) {
|
|
809
|
-
this.segments = segments;
|
|
810
|
-
this.children = children;
|
|
811
|
-
/** The parent node in the url tree */
|
|
812
|
-
this.parent = null;
|
|
813
|
-
forEach(children, (v, k) => v.parent = this);
|
|
814
|
-
}
|
|
815
|
-
/** Whether the segment has child segments */
|
|
816
|
-
hasChildren() {
|
|
817
|
-
return this.numberOfChildren > 0;
|
|
818
|
-
}
|
|
819
|
-
/** Number of child segments */
|
|
820
|
-
get numberOfChildren() {
|
|
821
|
-
return Object.keys(this.children).length;
|
|
822
|
-
}
|
|
823
|
-
/** @docsNotRequired */
|
|
824
|
-
toString() {
|
|
825
|
-
return serializePaths(this);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
/**
|
|
829
|
-
* @description
|
|
839
|
+
* // create /team/33/(user/11//right:chat)
|
|
840
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right:
|
|
841
|
+
* 'chat'}}], null, null);
|
|
830
842
|
*
|
|
831
|
-
*
|
|
843
|
+
* // remove the right secondary node
|
|
844
|
+
* createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right: null}}]);
|
|
832
845
|
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
846
|
+
* // For the examples below, assume the current URL is for the `/team/33/user/11` and the
|
|
847
|
+
* `ActivatedRouteSnapshot` points to `user/11`:
|
|
835
848
|
*
|
|
836
|
-
*
|
|
837
|
-
*
|
|
849
|
+
* // navigate to /team/33/user/11/details
|
|
850
|
+
* createUrlTreeFromSnapshot(snapshot, ['details']);
|
|
838
851
|
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
* class MyComponent {
|
|
842
|
-
* constructor(router: Router) {
|
|
843
|
-
* const tree: UrlTree = router.parseUrl('/team;id=33');
|
|
844
|
-
* const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
|
|
845
|
-
* const s: UrlSegment[] = g.segments;
|
|
846
|
-
* s[0].path; // returns 'team'
|
|
847
|
-
* s[0].parameters; // returns {id: 33}
|
|
848
|
-
* }
|
|
849
|
-
* }
|
|
850
|
-
* ```
|
|
852
|
+
* // navigate to /team/33/user/22
|
|
853
|
+
* createUrlTreeFromSnapshot(snapshot, ['../22']);
|
|
851
854
|
*
|
|
852
|
-
*
|
|
855
|
+
* // navigate to /team/44/user/22
|
|
856
|
+
* createUrlTreeFromSnapshot(snapshot, ['../../team/44/user/22']);
|
|
857
|
+
* ```
|
|
853
858
|
*/
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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 !== null && targetGroup !== void 0 ? 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);
|
|
868
891
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
return
|
|
892
|
+
const nav = computeNavigation(commands);
|
|
893
|
+
if (nav.toRoot()) {
|
|
894
|
+
return tree(root, root, new UrlSegmentGroup([], {}), queryParams, fragment);
|
|
872
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);
|
|
873
901
|
}
|
|
874
|
-
function
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
902
|
+
function createUrlTree(route, urlTree, commands, queryParams, fragment) {
|
|
903
|
+
var _a, _b;
|
|
904
|
+
if (commands.length === 0) {
|
|
905
|
+
return tree(urlTree.root, urlTree.root, urlTree.root, queryParams, fragment);
|
|
906
|
+
}
|
|
907
|
+
const nav = computeNavigation(commands);
|
|
908
|
+
if (nav.toRoot()) {
|
|
909
|
+
return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment);
|
|
910
|
+
}
|
|
911
|
+
function createTreeUsingPathIndex(lastPathIndex) {
|
|
912
|
+
var _a;
|
|
913
|
+
const startingPosition = findStartingPosition(nav, urlTree, (_a = route.snapshot) === null || _a === void 0 ? void 0 : _a._urlSegment, lastPathIndex);
|
|
914
|
+
const segmentGroup = startingPosition.processChildren ?
|
|
915
|
+
updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
|
|
916
|
+
updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
|
|
917
|
+
return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
|
|
918
|
+
}
|
|
919
|
+
// Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this
|
|
920
|
+
// may be the case. Since we try to access it at an earlier point before the refactor to add the
|
|
921
|
+
// warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it
|
|
922
|
+
// didn't before.
|
|
923
|
+
const result = createTreeUsingPathIndex((_a = route.snapshot) === null || _a === void 0 ? void 0 : _a._lastPathIndex);
|
|
924
|
+
// Check if application is relying on `relativeLinkResolution: 'legacy'`
|
|
925
|
+
if (typeof ngDevMode === 'undefined' || !!ngDevMode) {
|
|
926
|
+
const correctedResult = createTreeUsingPathIndex((_b = route.snapshot) === null || _b === void 0 ? void 0 : _b._correctedLastPathIndex);
|
|
927
|
+
if (correctedResult.toString() !== result.toString()) {
|
|
928
|
+
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.`);
|
|
892
929
|
}
|
|
893
|
-
}
|
|
894
|
-
return
|
|
930
|
+
}
|
|
931
|
+
return result;
|
|
895
932
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
*
|
|
899
|
-
* Serializes and deserializes a URL string into a URL tree.
|
|
900
|
-
*
|
|
901
|
-
* The url serialization strategy is customizable. You can
|
|
902
|
-
* make all URLs case insensitive by providing a custom UrlSerializer.
|
|
903
|
-
*
|
|
904
|
-
* See `DefaultUrlSerializer` for an example of a URL serializer.
|
|
905
|
-
*
|
|
906
|
-
* @publicApi
|
|
907
|
-
*/
|
|
908
|
-
class UrlSerializer {
|
|
933
|
+
function isMatrixParams(command) {
|
|
934
|
+
return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
|
|
909
935
|
}
|
|
910
936
|
/**
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
* A default implementation of the `UrlSerializer`.
|
|
914
|
-
*
|
|
915
|
-
* Example URLs:
|
|
916
|
-
*
|
|
917
|
-
* ```
|
|
918
|
-
* /inbox/33(popup:compose)
|
|
919
|
-
* /inbox/33;open=true/messages/44
|
|
920
|
-
* ```
|
|
921
|
-
*
|
|
922
|
-
* DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the
|
|
923
|
-
* colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to
|
|
924
|
-
* specify route specific parameters.
|
|
925
|
-
*
|
|
926
|
-
* @publicApi
|
|
937
|
+
* Determines if a given command has an `outlets` map. When we encounter a command
|
|
938
|
+
* with an outlets k/v map, we need to apply each outlet individually to the existing segment.
|
|
927
939
|
*/
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
parse(url) {
|
|
931
|
-
const p = new UrlParser(url);
|
|
932
|
-
return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
|
|
933
|
-
}
|
|
934
|
-
/** Converts a `UrlTree` into a url */
|
|
935
|
-
serialize(tree) {
|
|
936
|
-
const segment = `/${serializeSegment(tree.root, true)}`;
|
|
937
|
-
const query = serializeQueryParams(tree.queryParams);
|
|
938
|
-
const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';
|
|
939
|
-
return `${segment}${query}${fragment}`;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
const DEFAULT_SERIALIZER = new DefaultUrlSerializer();
|
|
943
|
-
function serializePaths(segment) {
|
|
944
|
-
return segment.segments.map(p => serializePath(p)).join('/');
|
|
940
|
+
function isCommandWithOutlets(command) {
|
|
941
|
+
return typeof command === 'object' && command != null && command.outlets;
|
|
945
942
|
}
|
|
946
|
-
function
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
const primary = segment.children[PRIMARY_OUTLET] ?
|
|
952
|
-
serializeSegment(segment.children[PRIMARY_OUTLET], false) :
|
|
953
|
-
'';
|
|
954
|
-
const children = [];
|
|
955
|
-
forEach(segment.children, (v, k) => {
|
|
956
|
-
if (k !== PRIMARY_OUTLET) {
|
|
957
|
-
children.push(`${k}:${serializeSegment(v, false)}`);
|
|
958
|
-
}
|
|
943
|
+
function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) {
|
|
944
|
+
let qp = {};
|
|
945
|
+
if (queryParams) {
|
|
946
|
+
forEach(queryParams, (value, name) => {
|
|
947
|
+
qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
|
|
959
948
|
});
|
|
960
|
-
|
|
949
|
+
}
|
|
950
|
+
let rootCandidate;
|
|
951
|
+
if (oldRoot === oldSegmentGroup) {
|
|
952
|
+
rootCandidate = newSegmentGroup;
|
|
961
953
|
}
|
|
962
954
|
else {
|
|
963
|
-
|
|
964
|
-
if (k === PRIMARY_OUTLET) {
|
|
965
|
-
return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];
|
|
966
|
-
}
|
|
967
|
-
return [`${k}:${serializeSegment(v, false)}`];
|
|
968
|
-
});
|
|
969
|
-
// use no parenthesis if the only child is a primary outlet route
|
|
970
|
-
if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) {
|
|
971
|
-
return `${serializePaths(segment)}/${children[0]}`;
|
|
972
|
-
}
|
|
973
|
-
return `${serializePaths(segment)}/(${children.join('//')})`;
|
|
955
|
+
rootCandidate = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup);
|
|
974
956
|
}
|
|
957
|
+
const newRoot = createRoot(squashSegmentGroup(rootCandidate));
|
|
958
|
+
return new UrlTree(newRoot, qp, fragment);
|
|
975
959
|
}
|
|
976
960
|
/**
|
|
977
|
-
*
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
*
|
|
961
|
+
* Replaces the `oldSegment` which is located in some child of the `current` with the `newSegment`.
|
|
962
|
+
* This also has the effect of creating new `UrlSegmentGroup` copies to update references. This
|
|
963
|
+
* shouldn't be necessary but the fallback logic for an invalid ActivatedRoute in the creation uses
|
|
964
|
+
* the Router's current url tree. If we don't create new segment groups, we end up modifying that
|
|
965
|
+
* value.
|
|
981
966
|
*/
|
|
982
|
-
function
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
967
|
+
function replaceSegment(current, oldSegment, newSegment) {
|
|
968
|
+
const children = {};
|
|
969
|
+
forEach(current.children, (c, outletName) => {
|
|
970
|
+
if (c === oldSegment) {
|
|
971
|
+
children[outletName] = newSegment;
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
children[outletName] = replaceSegment(c, oldSegment, newSegment);
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
return new UrlSegmentGroup(current.segments, children);
|
|
988
978
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
return encodeURI(s);
|
|
1006
|
-
}
|
|
1007
|
-
/**
|
|
1008
|
-
* This function should be run on any URI segment as well as the key and value in a key/value
|
|
1009
|
-
* pair for matrix params. In the following URL, you need to call encodeUriSegment on "html",
|
|
1010
|
-
* "mk", and "mv":
|
|
1011
|
-
*
|
|
1012
|
-
* http://www.site.org/html;mk=mv?k=v#f
|
|
1013
|
-
*/
|
|
1014
|
-
function encodeUriSegment(s) {
|
|
1015
|
-
return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&');
|
|
1016
|
-
}
|
|
1017
|
-
function decode(s) {
|
|
1018
|
-
return decodeURIComponent(s);
|
|
1019
|
-
}
|
|
1020
|
-
// Query keys/values should have the "+" replaced first, as "+" in a query string is " ".
|
|
1021
|
-
// decodeURIComponent function will not decode "+" as a space.
|
|
1022
|
-
function decodeQuery(s) {
|
|
1023
|
-
return decode(s.replace(/\+/g, '%20'));
|
|
979
|
+
class Navigation {
|
|
980
|
+
constructor(isAbsolute, numberOfDoubleDots, commands) {
|
|
981
|
+
this.isAbsolute = isAbsolute;
|
|
982
|
+
this.numberOfDoubleDots = numberOfDoubleDots;
|
|
983
|
+
this.commands = commands;
|
|
984
|
+
if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
|
|
985
|
+
throw new Error('Root segment cannot have matrix parameters');
|
|
986
|
+
}
|
|
987
|
+
const cmdWithOutlet = commands.find(isCommandWithOutlets);
|
|
988
|
+
if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
|
|
989
|
+
throw new Error('{outlets:{}} has to be the last command');
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
toRoot() {
|
|
993
|
+
return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
|
|
994
|
+
}
|
|
1024
995
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
996
|
+
/** Transforms commands to a normalized `Navigation` */
|
|
997
|
+
function computeNavigation(commands) {
|
|
998
|
+
if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
|
|
999
|
+
return new Navigation(true, 0, commands);
|
|
1000
|
+
}
|
|
1001
|
+
let numberOfDoubleDots = 0;
|
|
1002
|
+
let isAbsolute = false;
|
|
1003
|
+
const res = commands.reduce((res, cmd, cmdIdx) => {
|
|
1004
|
+
if (typeof cmd === 'object' && cmd != null) {
|
|
1005
|
+
if (cmd.outlets) {
|
|
1006
|
+
const outlets = {};
|
|
1007
|
+
forEach(cmd.outlets, (commands, name) => {
|
|
1008
|
+
outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
|
|
1009
|
+
});
|
|
1010
|
+
return [...res, { outlets }];
|
|
1011
|
+
}
|
|
1012
|
+
if (cmd.segmentPath) {
|
|
1013
|
+
return [...res, cmd.segmentPath];
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
if (!(typeof cmd === 'string')) {
|
|
1017
|
+
return [...res, cmd];
|
|
1018
|
+
}
|
|
1019
|
+
if (cmdIdx === 0) {
|
|
1020
|
+
cmd.split('/').forEach((urlPart, partIndex) => {
|
|
1021
|
+
if (partIndex == 0 && urlPart === '.') {
|
|
1022
|
+
// skip './a'
|
|
1023
|
+
}
|
|
1024
|
+
else if (partIndex == 0 && urlPart === '') { // '/a'
|
|
1025
|
+
isAbsolute = true;
|
|
1026
|
+
}
|
|
1027
|
+
else if (urlPart === '..') { // '../a'
|
|
1028
|
+
numberOfDoubleDots++;
|
|
1029
|
+
}
|
|
1030
|
+
else if (urlPart != '') {
|
|
1031
|
+
res.push(urlPart);
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
return res;
|
|
1035
|
+
}
|
|
1036
|
+
return [...res, cmd];
|
|
1037
|
+
}, []);
|
|
1038
|
+
return new Navigation(isAbsolute, numberOfDoubleDots, res);
|
|
1027
1039
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
.
|
|
1031
|
-
.
|
|
1040
|
+
class Position {
|
|
1041
|
+
constructor(segmentGroup, processChildren, index) {
|
|
1042
|
+
this.segmentGroup = segmentGroup;
|
|
1043
|
+
this.processChildren = processChildren;
|
|
1044
|
+
this.index = index;
|
|
1045
|
+
}
|
|
1032
1046
|
}
|
|
1033
|
-
function
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1047
|
+
function findStartingPositionForTargetGroup(nav, root, target) {
|
|
1048
|
+
if (nav.isAbsolute) {
|
|
1049
|
+
return new Position(root, true, 0);
|
|
1050
|
+
}
|
|
1051
|
+
if (!target) {
|
|
1052
|
+
// `NaN` is used only to maintain backwards compatibility with incorrectly mocked
|
|
1053
|
+
// `ActivatedRouteSnapshot` in tests. In prior versions of this code, the position here was
|
|
1054
|
+
// determined based on an internal property that was rarely mocked, resulting in `NaN`. In
|
|
1055
|
+
// reality, this code path should _never_ be touched since `target` is not allowed to be falsey.
|
|
1056
|
+
return new Position(root, false, NaN);
|
|
1057
|
+
}
|
|
1058
|
+
if (target.parent === null) {
|
|
1059
|
+
return new Position(target, true, 0);
|
|
1060
|
+
}
|
|
1061
|
+
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
|
|
1062
|
+
const index = target.segments.length - 1 + modifier;
|
|
1063
|
+
return createPositionApplyingDoubleDots(target, index, nav.numberOfDoubleDots);
|
|
1043
1064
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1065
|
+
function findStartingPosition(nav, tree, segmentGroup, lastPathIndex) {
|
|
1066
|
+
if (nav.isAbsolute) {
|
|
1067
|
+
return new Position(tree.root, true, 0);
|
|
1068
|
+
}
|
|
1069
|
+
if (lastPathIndex === -1) {
|
|
1070
|
+
// Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
|
|
1071
|
+
// see issue #26224, #13011, #35687
|
|
1072
|
+
// However, if the ActivatedRoute is the root we should process children like above.
|
|
1073
|
+
const processChildren = segmentGroup === tree.root;
|
|
1074
|
+
return new Position(segmentGroup, processChildren, 0);
|
|
1075
|
+
}
|
|
1076
|
+
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
|
|
1077
|
+
const index = lastPathIndex + modifier;
|
|
1078
|
+
return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
|
|
1048
1079
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1080
|
+
function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) {
|
|
1081
|
+
let g = group;
|
|
1082
|
+
let ci = index;
|
|
1083
|
+
let dd = numberOfDoubleDots;
|
|
1084
|
+
while (dd > ci) {
|
|
1085
|
+
dd -= ci;
|
|
1086
|
+
g = g.parent;
|
|
1087
|
+
if (!g) {
|
|
1088
|
+
throw new Error('Invalid number of \'../\'');
|
|
1089
|
+
}
|
|
1090
|
+
ci = g.segments.length;
|
|
1091
|
+
}
|
|
1092
|
+
return new Position(g, false, ci - dd);
|
|
1054
1093
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
return
|
|
1094
|
+
function getOutlets(commands) {
|
|
1095
|
+
if (isCommandWithOutlets(commands[0])) {
|
|
1096
|
+
return commands[0].outlets;
|
|
1097
|
+
}
|
|
1098
|
+
return { [PRIMARY_OUTLET]: commands };
|
|
1060
1099
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
this.remaining = url;
|
|
1100
|
+
function updateSegmentGroup(segmentGroup, startIndex, commands) {
|
|
1101
|
+
if (!segmentGroup) {
|
|
1102
|
+
segmentGroup = new UrlSegmentGroup([], {});
|
|
1065
1103
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
|
|
1069
|
-
return new UrlSegmentGroup([], {});
|
|
1070
|
-
}
|
|
1071
|
-
// The root segment group never has segments
|
|
1072
|
-
return new UrlSegmentGroup([], this.parseChildren());
|
|
1104
|
+
if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
|
|
1105
|
+
return updateSegmentGroupChildren(segmentGroup, startIndex, commands);
|
|
1073
1106
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
return params;
|
|
1107
|
+
const m = prefixedWith(segmentGroup, startIndex, commands);
|
|
1108
|
+
const slicedCommands = commands.slice(m.commandIndex);
|
|
1109
|
+
if (m.match && m.pathIndex < segmentGroup.segments.length) {
|
|
1110
|
+
const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
|
|
1111
|
+
g.children[PRIMARY_OUTLET] =
|
|
1112
|
+
new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
|
|
1113
|
+
return updateSegmentGroupChildren(g, 0, slicedCommands);
|
|
1082
1114
|
}
|
|
1083
|
-
|
|
1084
|
-
return
|
|
1115
|
+
else if (m.match && slicedCommands.length === 0) {
|
|
1116
|
+
return new UrlSegmentGroup(segmentGroup.segments, {});
|
|
1085
1117
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
return {};
|
|
1089
|
-
}
|
|
1090
|
-
this.consumeOptional('/');
|
|
1091
|
-
const segments = [];
|
|
1092
|
-
if (!this.peekStartsWith('(')) {
|
|
1093
|
-
segments.push(this.parseSegment());
|
|
1094
|
-
}
|
|
1095
|
-
while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {
|
|
1096
|
-
this.capture('/');
|
|
1097
|
-
segments.push(this.parseSegment());
|
|
1098
|
-
}
|
|
1099
|
-
let children = {};
|
|
1100
|
-
if (this.peekStartsWith('/(')) {
|
|
1101
|
-
this.capture('/');
|
|
1102
|
-
children = this.parseParens(true);
|
|
1103
|
-
}
|
|
1104
|
-
let res = {};
|
|
1105
|
-
if (this.peekStartsWith('(')) {
|
|
1106
|
-
res = this.parseParens(false);
|
|
1107
|
-
}
|
|
1108
|
-
if (segments.length > 0 || Object.keys(children).length > 0) {
|
|
1109
|
-
res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
|
|
1110
|
-
}
|
|
1111
|
-
return res;
|
|
1118
|
+
else if (m.match && !segmentGroup.hasChildren()) {
|
|
1119
|
+
return createNewSegmentGroup(segmentGroup, startIndex, commands);
|
|
1112
1120
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
parseSegment() {
|
|
1116
|
-
const path = matchSegments(this.remaining);
|
|
1117
|
-
if (path === '' && this.peekStartsWith(';')) {
|
|
1118
|
-
throw new Error(`Empty path url segment cannot have parameters: '${this.remaining}'.`);
|
|
1119
|
-
}
|
|
1120
|
-
this.capture(path);
|
|
1121
|
-
return new UrlSegment(decode(path), this.parseMatrixParams());
|
|
1121
|
+
else if (m.match) {
|
|
1122
|
+
return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands);
|
|
1122
1123
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
while (this.consumeOptional(';')) {
|
|
1126
|
-
this.parseParam(params);
|
|
1127
|
-
}
|
|
1128
|
-
return params;
|
|
1124
|
+
else {
|
|
1125
|
+
return createNewSegmentGroup(segmentGroup, startIndex, commands);
|
|
1129
1126
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
if (
|
|
1140
|
-
|
|
1141
|
-
this.capture(value);
|
|
1127
|
+
}
|
|
1128
|
+
function updateSegmentGroupChildren(segmentGroup, startIndex, commands) {
|
|
1129
|
+
if (commands.length === 0) {
|
|
1130
|
+
return new UrlSegmentGroup(segmentGroup.segments, {});
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
const outlets = getOutlets(commands);
|
|
1134
|
+
const children = {};
|
|
1135
|
+
forEach(outlets, (commands, outlet) => {
|
|
1136
|
+
if (typeof commands === 'string') {
|
|
1137
|
+
commands = [commands];
|
|
1142
1138
|
}
|
|
1139
|
+
if (commands !== null) {
|
|
1140
|
+
children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
forEach(segmentGroup.children, (child, childOutlet) => {
|
|
1144
|
+
if (outlets[childOutlet] === undefined) {
|
|
1145
|
+
children[childOutlet] = child;
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
return new UrlSegmentGroup(segmentGroup.segments, children);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
function prefixedWith(segmentGroup, startIndex, commands) {
|
|
1152
|
+
let currentCommandIndex = 0;
|
|
1153
|
+
let currentPathIndex = startIndex;
|
|
1154
|
+
const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
|
|
1155
|
+
while (currentPathIndex < segmentGroup.segments.length) {
|
|
1156
|
+
if (currentCommandIndex >= commands.length)
|
|
1157
|
+
return noMatch;
|
|
1158
|
+
const path = segmentGroup.segments[currentPathIndex];
|
|
1159
|
+
const command = commands[currentCommandIndex];
|
|
1160
|
+
// Do not try to consume command as part of the prefixing if it has outlets because it can
|
|
1161
|
+
// contain outlets other than the one being processed. Consuming the outlets command would
|
|
1162
|
+
// result in other outlets being ignored.
|
|
1163
|
+
if (isCommandWithOutlets(command)) {
|
|
1164
|
+
break;
|
|
1143
1165
|
}
|
|
1144
|
-
|
|
1166
|
+
const curr = `${command}`;
|
|
1167
|
+
const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
|
|
1168
|
+
if (currentPathIndex > 0 && curr === undefined)
|
|
1169
|
+
break;
|
|
1170
|
+
if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
|
|
1171
|
+
if (!compare(curr, next, path))
|
|
1172
|
+
return noMatch;
|
|
1173
|
+
currentCommandIndex += 2;
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
if (!compare(curr, {}, path))
|
|
1177
|
+
return noMatch;
|
|
1178
|
+
currentCommandIndex++;
|
|
1179
|
+
}
|
|
1180
|
+
currentPathIndex++;
|
|
1145
1181
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1182
|
+
return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
|
|
1183
|
+
}
|
|
1184
|
+
function createNewSegmentGroup(segmentGroup, startIndex, commands) {
|
|
1185
|
+
const paths = segmentGroup.segments.slice(0, startIndex);
|
|
1186
|
+
let i = 0;
|
|
1187
|
+
while (i < commands.length) {
|
|
1188
|
+
const command = commands[i];
|
|
1189
|
+
if (isCommandWithOutlets(command)) {
|
|
1190
|
+
const children = createNewSegmentChildren(command.outlets);
|
|
1191
|
+
return new UrlSegmentGroup(paths, children);
|
|
1151
1192
|
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
this.capture(value);
|
|
1159
|
-
}
|
|
1193
|
+
// if we start with an object literal, we need to reuse the path part from the segment
|
|
1194
|
+
if (i === 0 && isMatrixParams(commands[0])) {
|
|
1195
|
+
const p = segmentGroup.segments[startIndex];
|
|
1196
|
+
paths.push(new UrlSegment(p.path, stringify(commands[0])));
|
|
1197
|
+
i++;
|
|
1198
|
+
continue;
|
|
1160
1199
|
}
|
|
1161
|
-
const
|
|
1162
|
-
const
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
if (!Array.isArray(currentVal)) {
|
|
1167
|
-
currentVal = [currentVal];
|
|
1168
|
-
params[decodedKey] = currentVal;
|
|
1169
|
-
}
|
|
1170
|
-
currentVal.push(decodedVal);
|
|
1200
|
+
const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`;
|
|
1201
|
+
const next = (i < commands.length - 1) ? commands[i + 1] : null;
|
|
1202
|
+
if (curr && next && isMatrixParams(next)) {
|
|
1203
|
+
paths.push(new UrlSegment(curr, stringify(next)));
|
|
1204
|
+
i += 2;
|
|
1171
1205
|
}
|
|
1172
1206
|
else {
|
|
1173
|
-
|
|
1174
|
-
|
|
1207
|
+
paths.push(new UrlSegment(curr, {}));
|
|
1208
|
+
i++;
|
|
1175
1209
|
}
|
|
1176
1210
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
// if is is not one of these characters, then the segment was unescaped
|
|
1185
|
-
// or the group was not closed
|
|
1186
|
-
if (next !== '/' && next !== ')' && next !== ';') {
|
|
1187
|
-
throw new Error(`Cannot parse url '${this.url}'`);
|
|
1188
|
-
}
|
|
1189
|
-
let outletName = undefined;
|
|
1190
|
-
if (path.indexOf(':') > -1) {
|
|
1191
|
-
outletName = path.slice(0, path.indexOf(':'));
|
|
1192
|
-
this.capture(outletName);
|
|
1193
|
-
this.capture(':');
|
|
1194
|
-
}
|
|
1195
|
-
else if (allowPrimary) {
|
|
1196
|
-
outletName = PRIMARY_OUTLET;
|
|
1197
|
-
}
|
|
1198
|
-
const children = this.parseChildren();
|
|
1199
|
-
segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] :
|
|
1200
|
-
new UrlSegmentGroup([], children);
|
|
1201
|
-
this.consumeOptional('//');
|
|
1211
|
+
return new UrlSegmentGroup(paths, {});
|
|
1212
|
+
}
|
|
1213
|
+
function createNewSegmentChildren(outlets) {
|
|
1214
|
+
const children = {};
|
|
1215
|
+
forEach(outlets, (commands, outlet) => {
|
|
1216
|
+
if (typeof commands === 'string') {
|
|
1217
|
+
commands = [commands];
|
|
1202
1218
|
}
|
|
1203
|
-
|
|
1219
|
+
if (commands !== null) {
|
|
1220
|
+
children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands);
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
return children;
|
|
1224
|
+
}
|
|
1225
|
+
function stringify(params) {
|
|
1226
|
+
const res = {};
|
|
1227
|
+
forEach(params, (v, k) => res[k] = `${v}`);
|
|
1228
|
+
return res;
|
|
1229
|
+
}
|
|
1230
|
+
function compare(path, params, segment) {
|
|
1231
|
+
return path == segment.path && shallowEqual(params, segment.parameters);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* @license
|
|
1236
|
+
* Copyright Google LLC All Rights Reserved.
|
|
1237
|
+
*
|
|
1238
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
1239
|
+
* found in the LICENSE file at https://angular.io/license
|
|
1240
|
+
*/
|
|
1241
|
+
/**
|
|
1242
|
+
* Base for events the router goes through, as opposed to events tied to a specific
|
|
1243
|
+
* route. Fired one time for any given navigation.
|
|
1244
|
+
*
|
|
1245
|
+
* The following code shows how a class subscribes to router events.
|
|
1246
|
+
*
|
|
1247
|
+
* ```ts
|
|
1248
|
+
* import {Event, RouterEvent, Router} from '@angular/router';
|
|
1249
|
+
*
|
|
1250
|
+
* class MyService {
|
|
1251
|
+
* constructor(public router: Router) {
|
|
1252
|
+
* router.events.pipe(
|
|
1253
|
+
* filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
|
|
1254
|
+
* ).subscribe((e: RouterEvent) => {
|
|
1255
|
+
* // Do something
|
|
1256
|
+
* });
|
|
1257
|
+
* }
|
|
1258
|
+
* }
|
|
1259
|
+
* ```
|
|
1260
|
+
*
|
|
1261
|
+
* @see `Event`
|
|
1262
|
+
* @see [Router events summary](guide/router-reference#router-events)
|
|
1263
|
+
* @publicApi
|
|
1264
|
+
*/
|
|
1265
|
+
class RouterEvent {
|
|
1266
|
+
constructor(
|
|
1267
|
+
/** A unique ID that the router assigns to every router navigation. */
|
|
1268
|
+
id,
|
|
1269
|
+
/** The URL that is the destination for this navigation. */
|
|
1270
|
+
url) {
|
|
1271
|
+
this.id = id;
|
|
1272
|
+
this.url = url;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* An event triggered when a navigation starts.
|
|
1277
|
+
*
|
|
1278
|
+
* @publicApi
|
|
1279
|
+
*/
|
|
1280
|
+
class NavigationStart extends RouterEvent {
|
|
1281
|
+
constructor(
|
|
1282
|
+
/** @docsNotRequired */
|
|
1283
|
+
id,
|
|
1284
|
+
/** @docsNotRequired */
|
|
1285
|
+
url,
|
|
1286
|
+
/** @docsNotRequired */
|
|
1287
|
+
navigationTrigger = 'imperative',
|
|
1288
|
+
/** @docsNotRequired */
|
|
1289
|
+
restoredState = null) {
|
|
1290
|
+
super(id, url);
|
|
1291
|
+
this.type = 0 /* EventType.NavigationStart */;
|
|
1292
|
+
this.navigationTrigger = navigationTrigger;
|
|
1293
|
+
this.restoredState = restoredState;
|
|
1294
|
+
}
|
|
1295
|
+
/** @docsNotRequired */
|
|
1296
|
+
toString() {
|
|
1297
|
+
return `NavigationStart(id: ${this.id}, url: '${this.url}')`;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* An event triggered when a navigation ends successfully.
|
|
1302
|
+
*
|
|
1303
|
+
* @see `NavigationStart`
|
|
1304
|
+
* @see `NavigationCancel`
|
|
1305
|
+
* @see `NavigationError`
|
|
1306
|
+
*
|
|
1307
|
+
* @publicApi
|
|
1308
|
+
*/
|
|
1309
|
+
class NavigationEnd extends RouterEvent {
|
|
1310
|
+
constructor(
|
|
1311
|
+
/** @docsNotRequired */
|
|
1312
|
+
id,
|
|
1313
|
+
/** @docsNotRequired */
|
|
1314
|
+
url,
|
|
1315
|
+
/** @docsNotRequired */
|
|
1316
|
+
urlAfterRedirects) {
|
|
1317
|
+
super(id, url);
|
|
1318
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1319
|
+
this.type = 1 /* EventType.NavigationEnd */;
|
|
1204
1320
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1321
|
+
/** @docsNotRequired */
|
|
1322
|
+
toString() {
|
|
1323
|
+
return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;
|
|
1207
1324
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* An event triggered when a navigation is canceled, directly or indirectly.
|
|
1328
|
+
* This can happen for several reasons including when a route guard
|
|
1329
|
+
* returns `false` or initiates a redirect by returning a `UrlTree`.
|
|
1330
|
+
*
|
|
1331
|
+
* @see `NavigationStart`
|
|
1332
|
+
* @see `NavigationEnd`
|
|
1333
|
+
* @see `NavigationError`
|
|
1334
|
+
*
|
|
1335
|
+
* @publicApi
|
|
1336
|
+
*/
|
|
1337
|
+
class NavigationCancel extends RouterEvent {
|
|
1338
|
+
constructor(
|
|
1339
|
+
/** @docsNotRequired */
|
|
1340
|
+
id,
|
|
1341
|
+
/** @docsNotRequired */
|
|
1342
|
+
url,
|
|
1343
|
+
/** @docsNotRequired */
|
|
1344
|
+
reason) {
|
|
1345
|
+
super(id, url);
|
|
1346
|
+
this.reason = reason;
|
|
1347
|
+
this.type = 2 /* EventType.NavigationCancel */;
|
|
1215
1348
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
}
|
|
1349
|
+
/** @docsNotRequired */
|
|
1350
|
+
toString() {
|
|
1351
|
+
return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
|
|
1220
1352
|
}
|
|
1221
1353
|
}
|
|
1222
|
-
|
|
1223
1354
|
/**
|
|
1224
|
-
*
|
|
1225
|
-
* Copyright Google LLC All Rights Reserved.
|
|
1355
|
+
* An event triggered when a navigation fails due to an unexpected error.
|
|
1226
1356
|
*
|
|
1227
|
-
*
|
|
1228
|
-
*
|
|
1357
|
+
* @see `NavigationStart`
|
|
1358
|
+
* @see `NavigationEnd`
|
|
1359
|
+
* @see `NavigationCancel`
|
|
1360
|
+
*
|
|
1361
|
+
* @publicApi
|
|
1229
1362
|
*/
|
|
1230
|
-
class
|
|
1231
|
-
constructor(
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1363
|
+
class NavigationError extends RouterEvent {
|
|
1364
|
+
constructor(
|
|
1365
|
+
/** @docsNotRequired */
|
|
1366
|
+
id,
|
|
1367
|
+
/** @docsNotRequired */
|
|
1368
|
+
url,
|
|
1369
|
+
/** @docsNotRequired */
|
|
1370
|
+
error) {
|
|
1371
|
+
super(id, url);
|
|
1372
|
+
this.error = error;
|
|
1373
|
+
this.type = 3 /* EventType.NavigationError */;
|
|
1236
1374
|
}
|
|
1237
|
-
/**
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
parent(t) {
|
|
1241
|
-
const p = this.pathFromRoot(t);
|
|
1242
|
-
return p.length > 1 ? p[p.length - 2] : null;
|
|
1375
|
+
/** @docsNotRequired */
|
|
1376
|
+
toString() {
|
|
1377
|
+
return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;
|
|
1243
1378
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* An event triggered when routes are recognized.
|
|
1382
|
+
*
|
|
1383
|
+
* @publicApi
|
|
1384
|
+
*/
|
|
1385
|
+
class RoutesRecognized extends RouterEvent {
|
|
1386
|
+
constructor(
|
|
1387
|
+
/** @docsNotRequired */
|
|
1388
|
+
id,
|
|
1389
|
+
/** @docsNotRequired */
|
|
1390
|
+
url,
|
|
1391
|
+
/** @docsNotRequired */
|
|
1392
|
+
urlAfterRedirects,
|
|
1393
|
+
/** @docsNotRequired */
|
|
1394
|
+
state) {
|
|
1395
|
+
super(id, url);
|
|
1396
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1397
|
+
this.state = state;
|
|
1398
|
+
this.type = 4 /* EventType.RoutesRecognized */;
|
|
1250
1399
|
}
|
|
1251
|
-
/**
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
firstChild(t) {
|
|
1255
|
-
const n = findNode(t, this._root);
|
|
1256
|
-
return n && n.children.length > 0 ? n.children[0].value : null;
|
|
1400
|
+
/** @docsNotRequired */
|
|
1401
|
+
toString() {
|
|
1402
|
+
return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
1257
1403
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* An event triggered at the start of the Guard phase of routing.
|
|
1407
|
+
*
|
|
1408
|
+
* @see `GuardsCheckEnd`
|
|
1409
|
+
*
|
|
1410
|
+
* @publicApi
|
|
1411
|
+
*/
|
|
1412
|
+
class GuardsCheckStart extends RouterEvent {
|
|
1413
|
+
constructor(
|
|
1414
|
+
/** @docsNotRequired */
|
|
1415
|
+
id,
|
|
1416
|
+
/** @docsNotRequired */
|
|
1417
|
+
url,
|
|
1418
|
+
/** @docsNotRequired */
|
|
1419
|
+
urlAfterRedirects,
|
|
1420
|
+
/** @docsNotRequired */
|
|
1421
|
+
state) {
|
|
1422
|
+
super(id, url);
|
|
1423
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1424
|
+
this.state = state;
|
|
1425
|
+
this.type = 7 /* EventType.GuardsCheckStart */;
|
|
1267
1426
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
*/
|
|
1271
|
-
pathFromRoot(t) {
|
|
1272
|
-
return findPath(t, this._root).map(s => s.value);
|
|
1427
|
+
toString() {
|
|
1428
|
+
return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
1273
1429
|
}
|
|
1274
1430
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1431
|
+
/**
|
|
1432
|
+
* An event triggered at the end of the Guard phase of routing.
|
|
1433
|
+
*
|
|
1434
|
+
* @see `GuardsCheckStart`
|
|
1435
|
+
*
|
|
1436
|
+
* @publicApi
|
|
1437
|
+
*/
|
|
1438
|
+
class GuardsCheckEnd extends RouterEvent {
|
|
1439
|
+
constructor(
|
|
1440
|
+
/** @docsNotRequired */
|
|
1441
|
+
id,
|
|
1442
|
+
/** @docsNotRequired */
|
|
1443
|
+
url,
|
|
1444
|
+
/** @docsNotRequired */
|
|
1445
|
+
urlAfterRedirects,
|
|
1446
|
+
/** @docsNotRequired */
|
|
1447
|
+
state,
|
|
1448
|
+
/** @docsNotRequired */
|
|
1449
|
+
shouldActivate) {
|
|
1450
|
+
super(id, url);
|
|
1451
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1452
|
+
this.state = state;
|
|
1453
|
+
this.shouldActivate = shouldActivate;
|
|
1454
|
+
this.type = 8 /* EventType.GuardsCheckEnd */;
|
|
1283
1455
|
}
|
|
1284
|
-
|
|
1285
|
-
}
|
|
1286
|
-
// Return the path to the node with the given value using DFS
|
|
1287
|
-
function findPath(value, node) {
|
|
1288
|
-
if (value === node.value)
|
|
1289
|
-
return [node];
|
|
1290
|
-
for (const child of node.children) {
|
|
1291
|
-
const path = findPath(value, child);
|
|
1292
|
-
if (path.length) {
|
|
1293
|
-
path.unshift(node);
|
|
1294
|
-
return path;
|
|
1295
|
-
}
|
|
1456
|
+
toString() {
|
|
1457
|
+
return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
|
|
1296
1458
|
}
|
|
1297
|
-
return [];
|
|
1298
1459
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1460
|
+
/**
|
|
1461
|
+
* An event triggered at the start of the Resolve phase of routing.
|
|
1462
|
+
*
|
|
1463
|
+
* Runs in the "resolve" phase whether or not there is anything to resolve.
|
|
1464
|
+
* In future, may change to only run when there are things to be resolved.
|
|
1465
|
+
*
|
|
1466
|
+
* @see `ResolveEnd`
|
|
1467
|
+
*
|
|
1468
|
+
* @publicApi
|
|
1469
|
+
*/
|
|
1470
|
+
class ResolveStart extends RouterEvent {
|
|
1471
|
+
constructor(
|
|
1472
|
+
/** @docsNotRequired */
|
|
1473
|
+
id,
|
|
1474
|
+
/** @docsNotRequired */
|
|
1475
|
+
url,
|
|
1476
|
+
/** @docsNotRequired */
|
|
1477
|
+
urlAfterRedirects,
|
|
1478
|
+
/** @docsNotRequired */
|
|
1479
|
+
state) {
|
|
1480
|
+
super(id, url);
|
|
1481
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1482
|
+
this.state = state;
|
|
1483
|
+
this.type = 5 /* EventType.ResolveStart */;
|
|
1303
1484
|
}
|
|
1304
1485
|
toString() {
|
|
1305
|
-
return `
|
|
1486
|
+
return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
1306
1487
|
}
|
|
1307
1488
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1489
|
+
/**
|
|
1490
|
+
* An event triggered at the end of the Resolve phase of routing.
|
|
1491
|
+
* @see `ResolveStart`.
|
|
1492
|
+
*
|
|
1493
|
+
* @publicApi
|
|
1494
|
+
*/
|
|
1495
|
+
class ResolveEnd extends RouterEvent {
|
|
1496
|
+
constructor(
|
|
1497
|
+
/** @docsNotRequired */
|
|
1498
|
+
id,
|
|
1499
|
+
/** @docsNotRequired */
|
|
1500
|
+
url,
|
|
1501
|
+
/** @docsNotRequired */
|
|
1502
|
+
urlAfterRedirects,
|
|
1503
|
+
/** @docsNotRequired */
|
|
1504
|
+
state) {
|
|
1505
|
+
super(id, url);
|
|
1506
|
+
this.urlAfterRedirects = urlAfterRedirects;
|
|
1507
|
+
this.state = state;
|
|
1508
|
+
this.type = 6 /* EventType.ResolveEnd */;
|
|
1509
|
+
}
|
|
1510
|
+
toString() {
|
|
1511
|
+
return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
|
1313
1512
|
}
|
|
1314
|
-
return map;
|
|
1315
1513
|
}
|
|
1316
|
-
|
|
1317
|
-
/**
|
|
1318
|
-
* @license
|
|
1319
|
-
* Copyright Google LLC All Rights Reserved.
|
|
1320
|
-
*
|
|
1321
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
1322
|
-
* found in the LICENSE file at https://angular.io/license
|
|
1323
|
-
*/
|
|
1324
1514
|
/**
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1327
|
-
* @usageNotes
|
|
1328
|
-
*
|
|
1329
|
-
* Every node in the route tree is an `ActivatedRoute` instance
|
|
1330
|
-
* that knows about the "consumed" URL segments, the extracted parameters,
|
|
1331
|
-
* and the resolved data.
|
|
1332
|
-
* Use the `ActivatedRoute` properties to traverse the tree from any node.
|
|
1333
|
-
*
|
|
1334
|
-
* The following fragment shows how a component gets the root node
|
|
1335
|
-
* of the current state to establish its own route tree:
|
|
1336
|
-
*
|
|
1337
|
-
* ```
|
|
1338
|
-
* @Component({templateUrl:'template.html'})
|
|
1339
|
-
* class MyComponent {
|
|
1340
|
-
* constructor(router: Router) {
|
|
1341
|
-
* const state: RouterState = router.routerState;
|
|
1342
|
-
* const root: ActivatedRoute = state.root;
|
|
1343
|
-
* const child = root.firstChild;
|
|
1344
|
-
* const id: Observable<string> = child.params.map(p => p.id);
|
|
1345
|
-
* //...
|
|
1346
|
-
* }
|
|
1347
|
-
* }
|
|
1348
|
-
* ```
|
|
1515
|
+
* An event triggered before lazy loading a route configuration.
|
|
1349
1516
|
*
|
|
1350
|
-
* @see `
|
|
1351
|
-
* @see [Getting route information](guide/router#getting-route-information)
|
|
1517
|
+
* @see `RouteConfigLoadEnd`
|
|
1352
1518
|
*
|
|
1353
1519
|
* @publicApi
|
|
1354
1520
|
*/
|
|
1355
|
-
class
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
this.snapshot = snapshot;
|
|
1362
|
-
setRouterState(this, root);
|
|
1521
|
+
class RouteConfigLoadStart {
|
|
1522
|
+
constructor(
|
|
1523
|
+
/** @docsNotRequired */
|
|
1524
|
+
route) {
|
|
1525
|
+
this.route = route;
|
|
1526
|
+
this.type = 9 /* EventType.RouteConfigLoadStart */;
|
|
1363
1527
|
}
|
|
1364
1528
|
toString() {
|
|
1365
|
-
return this.
|
|
1529
|
+
return `RouteConfigLoadStart(path: ${this.route.path})`;
|
|
1366
1530
|
}
|
|
1367
1531
|
}
|
|
1368
|
-
function createEmptyState(urlTree, rootComponent) {
|
|
1369
|
-
const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
|
|
1370
|
-
const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
|
|
1371
|
-
const emptyParams = new BehaviorSubject({});
|
|
1372
|
-
const emptyData = new BehaviorSubject({});
|
|
1373
|
-
const emptyQueryParams = new BehaviorSubject({});
|
|
1374
|
-
const fragment = new BehaviorSubject('');
|
|
1375
|
-
const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
|
|
1376
|
-
activated.snapshot = snapshot.root;
|
|
1377
|
-
return new RouterState(new TreeNode(activated, []), snapshot);
|
|
1378
|
-
}
|
|
1379
|
-
function createEmptyStateSnapshot(urlTree, rootComponent) {
|
|
1380
|
-
const emptyParams = {};
|
|
1381
|
-
const emptyData = {};
|
|
1382
|
-
const emptyQueryParams = {};
|
|
1383
|
-
const fragment = '';
|
|
1384
|
-
const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
|
|
1385
|
-
return new RouterStateSnapshot('', new TreeNode(activated, []));
|
|
1386
|
-
}
|
|
1387
1532
|
/**
|
|
1388
|
-
*
|
|
1389
|
-
* that is loaded in an outlet.
|
|
1390
|
-
* Use to traverse the `RouterState` tree and extract information from nodes.
|
|
1391
|
-
*
|
|
1392
|
-
* The following example shows how to construct a component using information from a
|
|
1393
|
-
* currently activated route.
|
|
1394
|
-
*
|
|
1395
|
-
* Note: the observables in this class only emit when the current and previous values differ based
|
|
1396
|
-
* on shallow equality. For example, changing deeply nested properties in resolved `data` will not
|
|
1397
|
-
* cause the `ActivatedRoute.data` `Observable` to emit a new value.
|
|
1398
|
-
*
|
|
1399
|
-
* {@example router/activated-route/module.ts region="activated-route"
|
|
1400
|
-
* header="activated-route.component.ts"}
|
|
1533
|
+
* An event triggered when a route has been lazy loaded.
|
|
1401
1534
|
*
|
|
1402
|
-
* @see
|
|
1535
|
+
* @see `RouteConfigLoadStart`
|
|
1403
1536
|
*
|
|
1404
1537
|
* @publicApi
|
|
1405
1538
|
*/
|
|
1406
|
-
class
|
|
1407
|
-
/** @internal */
|
|
1539
|
+
class RouteConfigLoadEnd {
|
|
1408
1540
|
constructor(
|
|
1409
|
-
/**
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
/** An observable of the query parameters shared by all the routes. */
|
|
1414
|
-
queryParams,
|
|
1415
|
-
/** An observable of the URL fragment shared by all the routes. */
|
|
1416
|
-
fragment,
|
|
1417
|
-
/** An observable of the static and resolved data of this route. */
|
|
1418
|
-
data,
|
|
1419
|
-
/** The outlet name of the route, a constant. */
|
|
1420
|
-
outlet,
|
|
1421
|
-
/** The component of the route, a constant. */
|
|
1422
|
-
component, futureSnapshot) {
|
|
1423
|
-
this.url = url;
|
|
1424
|
-
this.params = params;
|
|
1425
|
-
this.queryParams = queryParams;
|
|
1426
|
-
this.fragment = fragment;
|
|
1427
|
-
this.data = data;
|
|
1428
|
-
this.outlet = outlet;
|
|
1429
|
-
this.component = component;
|
|
1430
|
-
this._futureSnapshot = futureSnapshot;
|
|
1431
|
-
}
|
|
1432
|
-
/** The configuration used to match this route. */
|
|
1433
|
-
get routeConfig() {
|
|
1434
|
-
return this._futureSnapshot.routeConfig;
|
|
1435
|
-
}
|
|
1436
|
-
/** The root of the router state. */
|
|
1437
|
-
get root() {
|
|
1438
|
-
return this._routerState.root;
|
|
1541
|
+
/** @docsNotRequired */
|
|
1542
|
+
route) {
|
|
1543
|
+
this.route = route;
|
|
1544
|
+
this.type = 10 /* EventType.RouteConfigLoadEnd */;
|
|
1439
1545
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
return this._routerState.parent(this);
|
|
1546
|
+
toString() {
|
|
1547
|
+
return `RouteConfigLoadEnd(path: ${this.route.path})`;
|
|
1443
1548
|
}
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* An event triggered at the start of the child-activation
|
|
1552
|
+
* part of the Resolve phase of routing.
|
|
1553
|
+
* @see `ChildActivationEnd`
|
|
1554
|
+
* @see `ResolveStart`
|
|
1555
|
+
*
|
|
1556
|
+
* @publicApi
|
|
1557
|
+
*/
|
|
1558
|
+
class ChildActivationStart {
|
|
1559
|
+
constructor(
|
|
1560
|
+
/** @docsNotRequired */
|
|
1561
|
+
snapshot) {
|
|
1562
|
+
this.snapshot = snapshot;
|
|
1563
|
+
this.type = 11 /* EventType.ChildActivationStart */;
|
|
1447
1564
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
return
|
|
1565
|
+
toString() {
|
|
1566
|
+
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
1567
|
+
return `ChildActivationStart(path: '${path}')`;
|
|
1451
1568
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* An event triggered at the end of the child-activation part
|
|
1572
|
+
* of the Resolve phase of routing.
|
|
1573
|
+
* @see `ChildActivationStart`
|
|
1574
|
+
* @see `ResolveStart`
|
|
1575
|
+
* @publicApi
|
|
1576
|
+
*/
|
|
1577
|
+
class ChildActivationEnd {
|
|
1578
|
+
constructor(
|
|
1579
|
+
/** @docsNotRequired */
|
|
1580
|
+
snapshot) {
|
|
1581
|
+
this.snapshot = snapshot;
|
|
1582
|
+
this.type = 12 /* EventType.ChildActivationEnd */;
|
|
1455
1583
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
* The map supports retrieving single and multiple values from the same parameter.
|
|
1460
|
-
*/
|
|
1461
|
-
get paramMap() {
|
|
1462
|
-
if (!this._paramMap) {
|
|
1463
|
-
this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
|
|
1464
|
-
}
|
|
1465
|
-
return this._paramMap;
|
|
1584
|
+
toString() {
|
|
1585
|
+
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
1586
|
+
return `ChildActivationEnd(path: '${path}')`;
|
|
1466
1587
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* An event triggered at the start of the activation part
|
|
1591
|
+
* of the Resolve phase of routing.
|
|
1592
|
+
* @see `ActivationEnd`
|
|
1593
|
+
* @see `ResolveStart`
|
|
1594
|
+
*
|
|
1595
|
+
* @publicApi
|
|
1596
|
+
*/
|
|
1597
|
+
class ActivationStart {
|
|
1598
|
+
constructor(
|
|
1599
|
+
/** @docsNotRequired */
|
|
1600
|
+
snapshot) {
|
|
1601
|
+
this.snapshot = snapshot;
|
|
1602
|
+
this.type = 13 /* EventType.ActivationStart */;
|
|
1477
1603
|
}
|
|
1478
1604
|
toString() {
|
|
1479
|
-
|
|
1605
|
+
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
1606
|
+
return `ActivationStart(path: '${path}')`;
|
|
1480
1607
|
}
|
|
1481
1608
|
}
|
|
1482
1609
|
/**
|
|
1483
|
-
*
|
|
1484
|
-
*
|
|
1485
|
-
* @
|
|
1610
|
+
* An event triggered at the end of the activation part
|
|
1611
|
+
* of the Resolve phase of routing.
|
|
1612
|
+
* @see `ActivationStart`
|
|
1613
|
+
* @see `ResolveStart`
|
|
1614
|
+
*
|
|
1615
|
+
* @publicApi
|
|
1486
1616
|
*/
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
inheritingStartingFrom--;
|
|
1498
|
-
// parent is componentless => current route should inherit its params and data
|
|
1499
|
-
}
|
|
1500
|
-
else if (!parent.component) {
|
|
1501
|
-
inheritingStartingFrom--;
|
|
1502
|
-
}
|
|
1503
|
-
else {
|
|
1504
|
-
break;
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1617
|
+
class ActivationEnd {
|
|
1618
|
+
constructor(
|
|
1619
|
+
/** @docsNotRequired */
|
|
1620
|
+
snapshot) {
|
|
1621
|
+
this.snapshot = snapshot;
|
|
1622
|
+
this.type = 14 /* EventType.ActivationEnd */;
|
|
1623
|
+
}
|
|
1624
|
+
toString() {
|
|
1625
|
+
const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
|
|
1626
|
+
return `ActivationEnd(path: '${path}')`;
|
|
1507
1627
|
}
|
|
1508
|
-
return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
|
|
1509
|
-
}
|
|
1510
|
-
/** @internal */
|
|
1511
|
-
function flattenInherited(pathFromRoot) {
|
|
1512
|
-
return pathFromRoot.reduce((res, curr) => {
|
|
1513
|
-
var _a;
|
|
1514
|
-
const params = Object.assign(Object.assign({}, res.params), curr.params);
|
|
1515
|
-
const data = Object.assign(Object.assign({}, res.data), curr.data);
|
|
1516
|
-
const resolve = Object.assign(Object.assign(Object.assign(Object.assign({}, curr.data), res.resolve), (_a = curr.routeConfig) === null || _a === void 0 ? void 0 : _a.data), curr._resolvedData);
|
|
1517
|
-
return { params, data, resolve };
|
|
1518
|
-
}, { params: {}, data: {}, resolve: {} });
|
|
1519
1628
|
}
|
|
1520
1629
|
/**
|
|
1521
|
-
*
|
|
1522
|
-
*
|
|
1523
|
-
* Contains the information about a route associated with a component loaded in an
|
|
1524
|
-
* outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
|
|
1525
|
-
* traverse the router state tree.
|
|
1526
|
-
*
|
|
1527
|
-
* The following example initializes a component with route information extracted
|
|
1528
|
-
* from the snapshot of the root node at the time of creation.
|
|
1529
|
-
*
|
|
1530
|
-
* ```
|
|
1531
|
-
* @Component({templateUrl:'./my-component.html'})
|
|
1532
|
-
* class MyComponent {
|
|
1533
|
-
* constructor(route: ActivatedRoute) {
|
|
1534
|
-
* const id: string = route.snapshot.params.id;
|
|
1535
|
-
* const url: string = route.snapshot.url.join('');
|
|
1536
|
-
* const user = route.snapshot.data.user;
|
|
1537
|
-
* }
|
|
1538
|
-
* }
|
|
1539
|
-
* ```
|
|
1630
|
+
* An event triggered by scrolling.
|
|
1540
1631
|
*
|
|
1541
1632
|
* @publicApi
|
|
1542
1633
|
*/
|
|
1543
|
-
class
|
|
1544
|
-
/** @internal */
|
|
1634
|
+
class Scroll {
|
|
1545
1635
|
constructor(
|
|
1546
|
-
/**
|
|
1547
|
-
|
|
1636
|
+
/** @docsNotRequired */
|
|
1637
|
+
routerEvent,
|
|
1638
|
+
/** @docsNotRequired */
|
|
1639
|
+
position,
|
|
1640
|
+
/** @docsNotRequired */
|
|
1641
|
+
anchor) {
|
|
1642
|
+
this.routerEvent = routerEvent;
|
|
1643
|
+
this.position = position;
|
|
1644
|
+
this.anchor = anchor;
|
|
1645
|
+
this.type = 15 /* EventType.Scroll */;
|
|
1646
|
+
}
|
|
1647
|
+
toString() {
|
|
1648
|
+
const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
|
|
1649
|
+
return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
function stringifyEvent(routerEvent) {
|
|
1653
|
+
var _a, _b, _c, _d;
|
|
1654
|
+
if (!('type' in routerEvent)) {
|
|
1655
|
+
return `Unknown Router Event: ${routerEvent.constructor.name}`;
|
|
1656
|
+
}
|
|
1657
|
+
switch (routerEvent.type) {
|
|
1658
|
+
case 14 /* EventType.ActivationEnd */:
|
|
1659
|
+
return `ActivationEnd(path: '${((_a = routerEvent.snapshot.routeConfig) === null || _a === void 0 ? void 0 : _a.path) || ''}')`;
|
|
1660
|
+
case 13 /* EventType.ActivationStart */:
|
|
1661
|
+
return `ActivationStart(path: '${((_b = routerEvent.snapshot.routeConfig) === null || _b === void 0 ? void 0 : _b.path) || ''}')`;
|
|
1662
|
+
case 12 /* EventType.ChildActivationEnd */:
|
|
1663
|
+
return `ChildActivationEnd(path: '${((_c = routerEvent.snapshot.routeConfig) === null || _c === void 0 ? void 0 : _c.path) || ''}')`;
|
|
1664
|
+
case 11 /* EventType.ChildActivationStart */:
|
|
1665
|
+
return `ChildActivationStart(path: '${((_d = routerEvent.snapshot.routeConfig) === null || _d === void 0 ? void 0 : _d.path) || ''}')`;
|
|
1666
|
+
case 8 /* EventType.GuardsCheckEnd */:
|
|
1667
|
+
return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
|
|
1668
|
+
case 7 /* EventType.GuardsCheckStart */:
|
|
1669
|
+
return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
1670
|
+
case 2 /* EventType.NavigationCancel */:
|
|
1671
|
+
return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
|
|
1672
|
+
case 1 /* EventType.NavigationEnd */:
|
|
1673
|
+
return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
|
|
1674
|
+
case 3 /* EventType.NavigationError */:
|
|
1675
|
+
return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
|
|
1676
|
+
case 0 /* EventType.NavigationStart */:
|
|
1677
|
+
return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
|
|
1678
|
+
case 6 /* EventType.ResolveEnd */:
|
|
1679
|
+
return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
1680
|
+
case 5 /* EventType.ResolveStart */:
|
|
1681
|
+
return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
1682
|
+
case 10 /* EventType.RouteConfigLoadEnd */:
|
|
1683
|
+
return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
|
|
1684
|
+
case 9 /* EventType.RouteConfigLoadStart */:
|
|
1685
|
+
return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
|
|
1686
|
+
case 4 /* EventType.RoutesRecognized */:
|
|
1687
|
+
return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
|
|
1688
|
+
case 15 /* EventType.Scroll */:
|
|
1689
|
+
const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
|
|
1690
|
+
return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
/**
|
|
1695
|
+
* @license
|
|
1696
|
+
* Copyright Google LLC All Rights Reserved.
|
|
1697
|
+
*
|
|
1698
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
1699
|
+
* found in the LICENSE file at https://angular.io/license
|
|
1700
|
+
*/
|
|
1701
|
+
class Tree {
|
|
1702
|
+
constructor(root) {
|
|
1703
|
+
this._root = root;
|
|
1704
|
+
}
|
|
1705
|
+
get root() {
|
|
1706
|
+
return this._root.value;
|
|
1707
|
+
}
|
|
1548
1708
|
/**
|
|
1549
|
-
*
|
|
1550
|
-
*
|
|
1551
|
-
* You can compute all params (or data) in the router state or to get params outside
|
|
1552
|
-
* of an activated component by traversing the `RouterState` tree as in the following
|
|
1553
|
-
* example:
|
|
1554
|
-
* ```
|
|
1555
|
-
* collectRouteParams(router: Router) {
|
|
1556
|
-
* let params = {};
|
|
1557
|
-
* let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
|
|
1558
|
-
* while (stack.length > 0) {
|
|
1559
|
-
* const route = stack.pop()!;
|
|
1560
|
-
* params = {...params, ...route.params};
|
|
1561
|
-
* stack.push(...route.children);
|
|
1562
|
-
* }
|
|
1563
|
-
* return params;
|
|
1564
|
-
* }
|
|
1565
|
-
* ```
|
|
1709
|
+
* @internal
|
|
1566
1710
|
*/
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
/** The URL fragment shared by all the routes */
|
|
1571
|
-
fragment,
|
|
1572
|
-
/** The static and resolved data of this route */
|
|
1573
|
-
data,
|
|
1574
|
-
/** The outlet name of the route */
|
|
1575
|
-
outlet,
|
|
1576
|
-
/** The component of the route */
|
|
1577
|
-
component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
|
|
1578
|
-
this.url = url;
|
|
1579
|
-
this.params = params;
|
|
1580
|
-
this.queryParams = queryParams;
|
|
1581
|
-
this.fragment = fragment;
|
|
1582
|
-
this.data = data;
|
|
1583
|
-
this.outlet = outlet;
|
|
1584
|
-
this.component = component;
|
|
1585
|
-
this.routeConfig = routeConfig;
|
|
1586
|
-
this._urlSegment = urlSegment;
|
|
1587
|
-
this._lastPathIndex = lastPathIndex;
|
|
1588
|
-
this._correctedLastPathIndex = correctedLastPathIndex !== null && correctedLastPathIndex !== void 0 ? correctedLastPathIndex : lastPathIndex;
|
|
1589
|
-
this._resolve = resolve;
|
|
1711
|
+
parent(t) {
|
|
1712
|
+
const p = this.pathFromRoot(t);
|
|
1713
|
+
return p.length > 1 ? p[p.length - 2] : null;
|
|
1590
1714
|
}
|
|
1591
|
-
/**
|
|
1592
|
-
|
|
1593
|
-
|
|
1715
|
+
/**
|
|
1716
|
+
* @internal
|
|
1717
|
+
*/
|
|
1718
|
+
children(t) {
|
|
1719
|
+
const n = findNode(t, this._root);
|
|
1720
|
+
return n ? n.children.map(t => t.value) : [];
|
|
1594
1721
|
}
|
|
1595
|
-
/**
|
|
1596
|
-
|
|
1597
|
-
|
|
1722
|
+
/**
|
|
1723
|
+
* @internal
|
|
1724
|
+
*/
|
|
1725
|
+
firstChild(t) {
|
|
1726
|
+
const n = findNode(t, this._root);
|
|
1727
|
+
return n && n.children.length > 0 ? n.children[0].value : null;
|
|
1598
1728
|
}
|
|
1599
|
-
/**
|
|
1600
|
-
|
|
1601
|
-
|
|
1729
|
+
/**
|
|
1730
|
+
* @internal
|
|
1731
|
+
*/
|
|
1732
|
+
siblings(t) {
|
|
1733
|
+
const p = findPath(t, this._root);
|
|
1734
|
+
if (p.length < 2)
|
|
1735
|
+
return [];
|
|
1736
|
+
const c = p[p.length - 2].children.map(c => c.value);
|
|
1737
|
+
return c.filter(cc => cc !== t);
|
|
1602
1738
|
}
|
|
1603
|
-
/**
|
|
1604
|
-
|
|
1605
|
-
|
|
1739
|
+
/**
|
|
1740
|
+
* @internal
|
|
1741
|
+
*/
|
|
1742
|
+
pathFromRoot(t) {
|
|
1743
|
+
return findPath(t, this._root).map(s => s.value);
|
|
1606
1744
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1745
|
+
}
|
|
1746
|
+
// DFS for the node matching the value
|
|
1747
|
+
function findNode(value, node) {
|
|
1748
|
+
if (value === node.value)
|
|
1749
|
+
return node;
|
|
1750
|
+
for (const child of node.children) {
|
|
1751
|
+
const node = findNode(value, child);
|
|
1752
|
+
if (node)
|
|
1753
|
+
return node;
|
|
1610
1754
|
}
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1755
|
+
return null;
|
|
1756
|
+
}
|
|
1757
|
+
// Return the path to the node with the given value using DFS
|
|
1758
|
+
function findPath(value, node) {
|
|
1759
|
+
if (value === node.value)
|
|
1760
|
+
return [node];
|
|
1761
|
+
for (const child of node.children) {
|
|
1762
|
+
const path = findPath(value, child);
|
|
1763
|
+
if (path.length) {
|
|
1764
|
+
path.unshift(node);
|
|
1765
|
+
return path;
|
|
1614
1766
|
}
|
|
1615
|
-
return this._paramMap;
|
|
1616
1767
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1768
|
+
return [];
|
|
1769
|
+
}
|
|
1770
|
+
class TreeNode {
|
|
1771
|
+
constructor(value, children) {
|
|
1772
|
+
this.value = value;
|
|
1773
|
+
this.children = children;
|
|
1622
1774
|
}
|
|
1623
1775
|
toString() {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1776
|
+
return `TreeNode(${this.value})`;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
// Return the list of T indexed by outlet name
|
|
1780
|
+
function nodeChildrenAsMap(node) {
|
|
1781
|
+
const map = {};
|
|
1782
|
+
if (node) {
|
|
1783
|
+
node.children.forEach(child => map[child.value.outlet] = child);
|
|
1627
1784
|
}
|
|
1785
|
+
return map;
|
|
1628
1786
|
}
|
|
1787
|
+
|
|
1629
1788
|
/**
|
|
1630
|
-
* @
|
|
1789
|
+
* @license
|
|
1790
|
+
* Copyright Google LLC All Rights Reserved.
|
|
1631
1791
|
*
|
|
1632
|
-
*
|
|
1792
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
1793
|
+
* found in the LICENSE file at https://angular.io/license
|
|
1794
|
+
*/
|
|
1795
|
+
/**
|
|
1796
|
+
* Represents the state of the router as a tree of activated routes.
|
|
1633
1797
|
*
|
|
1634
|
-
*
|
|
1635
|
-
*
|
|
1798
|
+
* @usageNotes
|
|
1799
|
+
*
|
|
1800
|
+
* Every node in the route tree is an `ActivatedRoute` instance
|
|
1801
|
+
* that knows about the "consumed" URL segments, the extracted parameters,
|
|
1802
|
+
* and the resolved data.
|
|
1803
|
+
* Use the `ActivatedRoute` properties to traverse the tree from any node.
|
|
1636
1804
|
*
|
|
1637
|
-
* The following
|
|
1638
|
-
*
|
|
1805
|
+
* The following fragment shows how a component gets the root node
|
|
1806
|
+
* of the current state to establish its own route tree:
|
|
1639
1807
|
*
|
|
1640
1808
|
* ```
|
|
1641
1809
|
* @Component({templateUrl:'template.html'})
|
|
1642
1810
|
* class MyComponent {
|
|
1643
1811
|
* constructor(router: Router) {
|
|
1644
1812
|
* const state: RouterState = router.routerState;
|
|
1645
|
-
* const
|
|
1646
|
-
* const root: ActivatedRouteSnapshot = snapshot.root;
|
|
1813
|
+
* const root: ActivatedRoute = state.root;
|
|
1647
1814
|
* const child = root.firstChild;
|
|
1648
1815
|
* const id: Observable<string> = child.params.map(p => p.id);
|
|
1649
1816
|
* //...
|
|
@@ -1651,425 +1818,419 @@ class ActivatedRouteSnapshot {
|
|
|
1651
1818
|
* }
|
|
1652
1819
|
* ```
|
|
1653
1820
|
*
|
|
1821
|
+
* @see `ActivatedRoute`
|
|
1822
|
+
* @see [Getting route information](guide/router#getting-route-information)
|
|
1823
|
+
*
|
|
1654
1824
|
* @publicApi
|
|
1655
1825
|
*/
|
|
1656
|
-
class
|
|
1826
|
+
class RouterState extends Tree {
|
|
1657
1827
|
/** @internal */
|
|
1658
|
-
constructor(
|
|
1659
|
-
/** The
|
|
1660
|
-
|
|
1828
|
+
constructor(root,
|
|
1829
|
+
/** The current snapshot of the router state */
|
|
1830
|
+
snapshot) {
|
|
1661
1831
|
super(root);
|
|
1662
|
-
this.
|
|
1832
|
+
this.snapshot = snapshot;
|
|
1663
1833
|
setRouterState(this, root);
|
|
1664
1834
|
}
|
|
1665
1835
|
toString() {
|
|
1666
|
-
return
|
|
1836
|
+
return this.snapshot.toString();
|
|
1667
1837
|
}
|
|
1668
1838
|
}
|
|
1669
|
-
function
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
const
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
* So we push new values into the observables only when they are not the initial values.
|
|
1680
|
-
* And we detect that by checking if the snapshot field is set.
|
|
1681
|
-
*/
|
|
1682
|
-
function advanceActivatedRoute(route) {
|
|
1683
|
-
if (route.snapshot) {
|
|
1684
|
-
const currentSnapshot = route.snapshot;
|
|
1685
|
-
const nextSnapshot = route._futureSnapshot;
|
|
1686
|
-
route.snapshot = nextSnapshot;
|
|
1687
|
-
if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
|
|
1688
|
-
route.queryParams.next(nextSnapshot.queryParams);
|
|
1689
|
-
}
|
|
1690
|
-
if (currentSnapshot.fragment !== nextSnapshot.fragment) {
|
|
1691
|
-
route.fragment.next(nextSnapshot.fragment);
|
|
1692
|
-
}
|
|
1693
|
-
if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
|
|
1694
|
-
route.params.next(nextSnapshot.params);
|
|
1695
|
-
}
|
|
1696
|
-
if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
|
|
1697
|
-
route.url.next(nextSnapshot.url);
|
|
1698
|
-
}
|
|
1699
|
-
if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
|
|
1700
|
-
route.data.next(nextSnapshot.data);
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
else {
|
|
1704
|
-
route.snapshot = route._futureSnapshot;
|
|
1705
|
-
// this is for resolved data
|
|
1706
|
-
route.data.next(route._futureSnapshot.data);
|
|
1707
|
-
}
|
|
1839
|
+
function createEmptyState(urlTree, rootComponent) {
|
|
1840
|
+
const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
|
|
1841
|
+
const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
|
|
1842
|
+
const emptyParams = new BehaviorSubject({});
|
|
1843
|
+
const emptyData = new BehaviorSubject({});
|
|
1844
|
+
const emptyQueryParams = new BehaviorSubject({});
|
|
1845
|
+
const fragment = new BehaviorSubject('');
|
|
1846
|
+
const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
|
|
1847
|
+
activated.snapshot = snapshot.root;
|
|
1848
|
+
return new RouterState(new TreeNode(activated, []), snapshot);
|
|
1708
1849
|
}
|
|
1709
|
-
function
|
|
1710
|
-
const
|
|
1711
|
-
const
|
|
1712
|
-
|
|
1713
|
-
|
|
1850
|
+
function createEmptyStateSnapshot(urlTree, rootComponent) {
|
|
1851
|
+
const emptyParams = {};
|
|
1852
|
+
const emptyData = {};
|
|
1853
|
+
const emptyQueryParams = {};
|
|
1854
|
+
const fragment = '';
|
|
1855
|
+
const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
|
|
1856
|
+
return new RouterStateSnapshot('', new TreeNode(activated, []));
|
|
1714
1857
|
}
|
|
1715
|
-
|
|
1716
1858
|
/**
|
|
1717
|
-
*
|
|
1718
|
-
*
|
|
1859
|
+
* Provides access to information about a route associated with a component
|
|
1860
|
+
* that is loaded in an outlet.
|
|
1861
|
+
* Use to traverse the `RouterState` tree and extract information from nodes.
|
|
1719
1862
|
*
|
|
1720
|
-
*
|
|
1721
|
-
*
|
|
1722
|
-
*/
|
|
1723
|
-
function createRouterState(routeReuseStrategy, curr, prevState) {
|
|
1724
|
-
const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
|
|
1725
|
-
return new RouterState(root, curr);
|
|
1726
|
-
}
|
|
1727
|
-
function createNode(routeReuseStrategy, curr, prevState) {
|
|
1728
|
-
// reuse an activated route that is currently displayed on the screen
|
|
1729
|
-
if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
|
|
1730
|
-
const value = prevState.value;
|
|
1731
|
-
value._futureSnapshot = curr.value;
|
|
1732
|
-
const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
|
|
1733
|
-
return new TreeNode(value, children);
|
|
1734
|
-
}
|
|
1735
|
-
else {
|
|
1736
|
-
if (routeReuseStrategy.shouldAttach(curr.value)) {
|
|
1737
|
-
// retrieve an activated route that is used to be displayed, but is not currently displayed
|
|
1738
|
-
const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
|
|
1739
|
-
if (detachedRouteHandle !== null) {
|
|
1740
|
-
const tree = detachedRouteHandle.route;
|
|
1741
|
-
tree.value._futureSnapshot = curr.value;
|
|
1742
|
-
tree.children = curr.children.map(c => createNode(routeReuseStrategy, c));
|
|
1743
|
-
return tree;
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
const value = createActivatedRoute(curr.value);
|
|
1747
|
-
const children = curr.children.map(c => createNode(routeReuseStrategy, c));
|
|
1748
|
-
return new TreeNode(value, children);
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
|
|
1752
|
-
return curr.children.map(child => {
|
|
1753
|
-
for (const p of prevState.children) {
|
|
1754
|
-
if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {
|
|
1755
|
-
return createNode(routeReuseStrategy, child, p);
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
return createNode(routeReuseStrategy, child);
|
|
1759
|
-
});
|
|
1760
|
-
}
|
|
1761
|
-
function createActivatedRoute(c) {
|
|
1762
|
-
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);
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
/**
|
|
1766
|
-
* @license
|
|
1767
|
-
* Copyright Google LLC All Rights Reserved.
|
|
1863
|
+
* The following example shows how to construct a component using information from a
|
|
1864
|
+
* currently activated route.
|
|
1768
1865
|
*
|
|
1769
|
-
*
|
|
1770
|
-
*
|
|
1866
|
+
* Note: the observables in this class only emit when the current and previous values differ based
|
|
1867
|
+
* on shallow equality. For example, changing deeply nested properties in resolved `data` will not
|
|
1868
|
+
* cause the `ActivatedRoute.data` `Observable` to emit a new value.
|
|
1869
|
+
*
|
|
1870
|
+
* {@example router/activated-route/module.ts region="activated-route"
|
|
1871
|
+
* header="activated-route.component.ts"}
|
|
1872
|
+
*
|
|
1873
|
+
* @see [Getting route information](guide/router#getting-route-information)
|
|
1874
|
+
*
|
|
1875
|
+
* @publicApi
|
|
1771
1876
|
*/
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
if (correctedResult.toString() !== result.toString()) {
|
|
1798
|
-
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.`);
|
|
1799
|
-
}
|
|
1877
|
+
class ActivatedRoute {
|
|
1878
|
+
/** @internal */
|
|
1879
|
+
constructor(
|
|
1880
|
+
/** An observable of the URL segments matched by this route. */
|
|
1881
|
+
url,
|
|
1882
|
+
/** An observable of the matrix parameters scoped to this route. */
|
|
1883
|
+
params,
|
|
1884
|
+
/** An observable of the query parameters shared by all the routes. */
|
|
1885
|
+
queryParams,
|
|
1886
|
+
/** An observable of the URL fragment shared by all the routes. */
|
|
1887
|
+
fragment,
|
|
1888
|
+
/** An observable of the static and resolved data of this route. */
|
|
1889
|
+
data,
|
|
1890
|
+
/** The outlet name of the route, a constant. */
|
|
1891
|
+
outlet,
|
|
1892
|
+
/** The component of the route, a constant. */
|
|
1893
|
+
component, futureSnapshot) {
|
|
1894
|
+
this.url = url;
|
|
1895
|
+
this.params = params;
|
|
1896
|
+
this.queryParams = queryParams;
|
|
1897
|
+
this.fragment = fragment;
|
|
1898
|
+
this.data = data;
|
|
1899
|
+
this.outlet = outlet;
|
|
1900
|
+
this.component = component;
|
|
1901
|
+
this._futureSnapshot = futureSnapshot;
|
|
1800
1902
|
}
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
|
|
1805
|
-
}
|
|
1806
|
-
/**
|
|
1807
|
-
* Determines if a given command has an `outlets` map. When we encounter a command
|
|
1808
|
-
* with an outlets k/v map, we need to apply each outlet individually to the existing segment.
|
|
1809
|
-
*/
|
|
1810
|
-
function isCommandWithOutlets(command) {
|
|
1811
|
-
return typeof command === 'object' && command != null && command.outlets;
|
|
1812
|
-
}
|
|
1813
|
-
function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) {
|
|
1814
|
-
let qp = {};
|
|
1815
|
-
if (queryParams) {
|
|
1816
|
-
forEach(queryParams, (value, name) => {
|
|
1817
|
-
qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
|
|
1818
|
-
});
|
|
1903
|
+
/** The configuration used to match this route. */
|
|
1904
|
+
get routeConfig() {
|
|
1905
|
+
return this._futureSnapshot.routeConfig;
|
|
1819
1906
|
}
|
|
1820
|
-
|
|
1821
|
-
|
|
1907
|
+
/** The root of the router state. */
|
|
1908
|
+
get root() {
|
|
1909
|
+
return this._routerState.root;
|
|
1822
1910
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1911
|
+
/** The parent of this route in the router state tree. */
|
|
1912
|
+
get parent() {
|
|
1913
|
+
return this._routerState.parent(this);
|
|
1914
|
+
}
|
|
1915
|
+
/** The first child of this route in the router state tree. */
|
|
1916
|
+
get firstChild() {
|
|
1917
|
+
return this._routerState.firstChild(this);
|
|
1918
|
+
}
|
|
1919
|
+
/** The children of this route in the router state tree. */
|
|
1920
|
+
get children() {
|
|
1921
|
+
return this._routerState.children(this);
|
|
1922
|
+
}
|
|
1923
|
+
/** The path from the root of the router state tree to this route. */
|
|
1924
|
+
get pathFromRoot() {
|
|
1925
|
+
return this._routerState.pathFromRoot(this);
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* An Observable that contains a map of the required and optional parameters
|
|
1929
|
+
* specific to the route.
|
|
1930
|
+
* The map supports retrieving single and multiple values from the same parameter.
|
|
1931
|
+
*/
|
|
1932
|
+
get paramMap() {
|
|
1933
|
+
if (!this._paramMap) {
|
|
1934
|
+
this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
|
|
1845
1935
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1936
|
+
return this._paramMap;
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* An Observable that contains a map of the query parameters available to all routes.
|
|
1940
|
+
* The map supports retrieving single and multiple values from the query parameter.
|
|
1941
|
+
*/
|
|
1942
|
+
get queryParamMap() {
|
|
1943
|
+
if (!this._queryParamMap) {
|
|
1944
|
+
this._queryParamMap =
|
|
1945
|
+
this.queryParams.pipe(map((p) => convertToParamMap(p)));
|
|
1849
1946
|
}
|
|
1947
|
+
return this._queryParamMap;
|
|
1850
1948
|
}
|
|
1851
|
-
|
|
1852
|
-
return this.
|
|
1949
|
+
toString() {
|
|
1950
|
+
return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
|
|
1853
1951
|
}
|
|
1854
1952
|
}
|
|
1855
|
-
/**
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1953
|
+
/**
|
|
1954
|
+
* Returns the inherited params, data, and resolve for a given route.
|
|
1955
|
+
* By default, this only inherits values up to the nearest path-less or component-less route.
|
|
1956
|
+
* @internal
|
|
1957
|
+
*/
|
|
1958
|
+
function inheritedParamsDataResolve(route, paramsInheritanceStrategy = 'emptyOnly') {
|
|
1959
|
+
const pathFromRoot = route.pathFromRoot;
|
|
1960
|
+
let inheritingStartingFrom = 0;
|
|
1961
|
+
if (paramsInheritanceStrategy !== 'always') {
|
|
1962
|
+
inheritingStartingFrom = pathFromRoot.length - 1;
|
|
1963
|
+
while (inheritingStartingFrom >= 1) {
|
|
1964
|
+
const current = pathFromRoot[inheritingStartingFrom];
|
|
1965
|
+
const parent = pathFromRoot[inheritingStartingFrom - 1];
|
|
1966
|
+
// current route is an empty path => inherits its parent's params and data
|
|
1967
|
+
if (current.routeConfig && current.routeConfig.path === '') {
|
|
1968
|
+
inheritingStartingFrom--;
|
|
1969
|
+
// parent is componentless => current route should inherit its params and data
|
|
1870
1970
|
}
|
|
1871
|
-
if (
|
|
1872
|
-
|
|
1971
|
+
else if (!parent.component) {
|
|
1972
|
+
inheritingStartingFrom--;
|
|
1973
|
+
}
|
|
1974
|
+
else {
|
|
1975
|
+
break;
|
|
1873
1976
|
}
|
|
1874
1977
|
}
|
|
1875
|
-
if (!(typeof cmd === 'string')) {
|
|
1876
|
-
return [...res, cmd];
|
|
1877
|
-
}
|
|
1878
|
-
if (cmdIdx === 0) {
|
|
1879
|
-
cmd.split('/').forEach((urlPart, partIndex) => {
|
|
1880
|
-
if (partIndex == 0 && urlPart === '.') {
|
|
1881
|
-
// skip './a'
|
|
1882
|
-
}
|
|
1883
|
-
else if (partIndex == 0 && urlPart === '') { // '/a'
|
|
1884
|
-
isAbsolute = true;
|
|
1885
|
-
}
|
|
1886
|
-
else if (urlPart === '..') { // '../a'
|
|
1887
|
-
numberOfDoubleDots++;
|
|
1888
|
-
}
|
|
1889
|
-
else if (urlPart != '') {
|
|
1890
|
-
res.push(urlPart);
|
|
1891
|
-
}
|
|
1892
|
-
});
|
|
1893
|
-
return res;
|
|
1894
|
-
}
|
|
1895
|
-
return [...res, cmd];
|
|
1896
|
-
}, []);
|
|
1897
|
-
return new Navigation(isAbsolute, numberOfDoubleDots, res);
|
|
1898
|
-
}
|
|
1899
|
-
class Position {
|
|
1900
|
-
constructor(segmentGroup, processChildren, index) {
|
|
1901
|
-
this.segmentGroup = segmentGroup;
|
|
1902
|
-
this.processChildren = processChildren;
|
|
1903
|
-
this.index = index;
|
|
1904
1978
|
}
|
|
1979
|
+
return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
|
|
1905
1980
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
return new Position(segmentGroup, processChildren, 0);
|
|
1916
|
-
}
|
|
1917
|
-
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
|
|
1918
|
-
const index = lastPathIndex + modifier;
|
|
1919
|
-
return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
|
|
1981
|
+
/** @internal */
|
|
1982
|
+
function flattenInherited(pathFromRoot) {
|
|
1983
|
+
return pathFromRoot.reduce((res, curr) => {
|
|
1984
|
+
var _a;
|
|
1985
|
+
const params = Object.assign(Object.assign({}, res.params), curr.params);
|
|
1986
|
+
const data = Object.assign(Object.assign({}, res.data), curr.data);
|
|
1987
|
+
const resolve = Object.assign(Object.assign(Object.assign(Object.assign({}, curr.data), res.resolve), (_a = curr.routeConfig) === null || _a === void 0 ? void 0 : _a.data), curr._resolvedData);
|
|
1988
|
+
return { params, data, resolve };
|
|
1989
|
+
}, { params: {}, data: {}, resolve: {} });
|
|
1920
1990
|
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1991
|
+
/**
|
|
1992
|
+
* @description
|
|
1993
|
+
*
|
|
1994
|
+
* Contains the information about a route associated with a component loaded in an
|
|
1995
|
+
* outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
|
|
1996
|
+
* traverse the router state tree.
|
|
1997
|
+
*
|
|
1998
|
+
* The following example initializes a component with route information extracted
|
|
1999
|
+
* from the snapshot of the root node at the time of creation.
|
|
2000
|
+
*
|
|
2001
|
+
* ```
|
|
2002
|
+
* @Component({templateUrl:'./my-component.html'})
|
|
2003
|
+
* class MyComponent {
|
|
2004
|
+
* constructor(route: ActivatedRoute) {
|
|
2005
|
+
* const id: string = route.snapshot.params.id;
|
|
2006
|
+
* const url: string = route.snapshot.url.join('');
|
|
2007
|
+
* const user = route.snapshot.data.user;
|
|
2008
|
+
* }
|
|
2009
|
+
* }
|
|
2010
|
+
* ```
|
|
2011
|
+
*
|
|
2012
|
+
* @publicApi
|
|
2013
|
+
*/
|
|
2014
|
+
class ActivatedRouteSnapshot {
|
|
2015
|
+
/** @internal */
|
|
2016
|
+
constructor(
|
|
2017
|
+
/** The URL segments matched by this route */
|
|
2018
|
+
url,
|
|
2019
|
+
/**
|
|
2020
|
+
* The matrix parameters scoped to this route.
|
|
2021
|
+
*
|
|
2022
|
+
* You can compute all params (or data) in the router state or to get params outside
|
|
2023
|
+
* of an activated component by traversing the `RouterState` tree as in the following
|
|
2024
|
+
* example:
|
|
2025
|
+
* ```
|
|
2026
|
+
* collectRouteParams(router: Router) {
|
|
2027
|
+
* let params = {};
|
|
2028
|
+
* let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
|
|
2029
|
+
* while (stack.length > 0) {
|
|
2030
|
+
* const route = stack.pop()!;
|
|
2031
|
+
* params = {...params, ...route.params};
|
|
2032
|
+
* stack.push(...route.children);
|
|
2033
|
+
* }
|
|
2034
|
+
* return params;
|
|
2035
|
+
* }
|
|
2036
|
+
* ```
|
|
2037
|
+
*/
|
|
2038
|
+
params,
|
|
2039
|
+
/** The query parameters shared by all the routes */
|
|
2040
|
+
queryParams,
|
|
2041
|
+
/** The URL fragment shared by all the routes */
|
|
2042
|
+
fragment,
|
|
2043
|
+
/** The static and resolved data of this route */
|
|
2044
|
+
data,
|
|
2045
|
+
/** The outlet name of the route */
|
|
2046
|
+
outlet,
|
|
2047
|
+
/** The component of the route */
|
|
2048
|
+
component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
|
|
2049
|
+
this.url = url;
|
|
2050
|
+
this.params = params;
|
|
2051
|
+
this.queryParams = queryParams;
|
|
2052
|
+
this.fragment = fragment;
|
|
2053
|
+
this.data = data;
|
|
2054
|
+
this.outlet = outlet;
|
|
2055
|
+
this.component = component;
|
|
2056
|
+
this.routeConfig = routeConfig;
|
|
2057
|
+
this._urlSegment = urlSegment;
|
|
2058
|
+
this._lastPathIndex = lastPathIndex;
|
|
2059
|
+
this._correctedLastPathIndex = correctedLastPathIndex !== null && correctedLastPathIndex !== void 0 ? correctedLastPathIndex : lastPathIndex;
|
|
2060
|
+
this._resolve = resolve;
|
|
1932
2061
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
if (isCommandWithOutlets(commands[0])) {
|
|
1937
|
-
return commands[0].outlets;
|
|
2062
|
+
/** The root of the router state */
|
|
2063
|
+
get root() {
|
|
2064
|
+
return this._routerState.root;
|
|
1938
2065
|
}
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
if (!segmentGroup) {
|
|
1943
|
-
segmentGroup = new UrlSegmentGroup([], {});
|
|
2066
|
+
/** The parent of this route in the router state tree */
|
|
2067
|
+
get parent() {
|
|
2068
|
+
return this._routerState.parent(this);
|
|
1944
2069
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
2070
|
+
/** The first child of this route in the router state tree */
|
|
2071
|
+
get firstChild() {
|
|
2072
|
+
return this._routerState.firstChild(this);
|
|
1947
2073
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
|
|
1952
|
-
g.children[PRIMARY_OUTLET] =
|
|
1953
|
-
new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
|
|
1954
|
-
return updateSegmentGroupChildren(g, 0, slicedCommands);
|
|
2074
|
+
/** The children of this route in the router state tree */
|
|
2075
|
+
get children() {
|
|
2076
|
+
return this._routerState.children(this);
|
|
1955
2077
|
}
|
|
1956
|
-
|
|
1957
|
-
|
|
2078
|
+
/** The path from the root of the router state tree to this route */
|
|
2079
|
+
get pathFromRoot() {
|
|
2080
|
+
return this._routerState.pathFromRoot(this);
|
|
1958
2081
|
}
|
|
1959
|
-
|
|
1960
|
-
|
|
2082
|
+
get paramMap() {
|
|
2083
|
+
if (!this._paramMap) {
|
|
2084
|
+
this._paramMap = convertToParamMap(this.params);
|
|
2085
|
+
}
|
|
2086
|
+
return this._paramMap;
|
|
1961
2087
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
2088
|
+
get queryParamMap() {
|
|
2089
|
+
if (!this._queryParamMap) {
|
|
2090
|
+
this._queryParamMap = convertToParamMap(this.queryParams);
|
|
2091
|
+
}
|
|
2092
|
+
return this._queryParamMap;
|
|
1964
2093
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
2094
|
+
toString() {
|
|
2095
|
+
const url = this.url.map(segment => segment.toString()).join('/');
|
|
2096
|
+
const matched = this.routeConfig ? this.routeConfig.path : '';
|
|
2097
|
+
return `Route(url:'${url}', path:'${matched}')`;
|
|
1967
2098
|
}
|
|
1968
2099
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
2100
|
+
/**
|
|
2101
|
+
* @description
|
|
2102
|
+
*
|
|
2103
|
+
* Represents the state of the router at a moment in time.
|
|
2104
|
+
*
|
|
2105
|
+
* This is a tree of activated route snapshots. Every node in this tree knows about
|
|
2106
|
+
* the "consumed" URL segments, the extracted parameters, and the resolved data.
|
|
2107
|
+
*
|
|
2108
|
+
* The following example shows how a component is initialized with information
|
|
2109
|
+
* from the snapshot of the root node's state at the time of creation.
|
|
2110
|
+
*
|
|
2111
|
+
* ```
|
|
2112
|
+
* @Component({templateUrl:'template.html'})
|
|
2113
|
+
* class MyComponent {
|
|
2114
|
+
* constructor(router: Router) {
|
|
2115
|
+
* const state: RouterState = router.routerState;
|
|
2116
|
+
* const snapshot: RouterStateSnapshot = state.snapshot;
|
|
2117
|
+
* const root: ActivatedRouteSnapshot = snapshot.root;
|
|
2118
|
+
* const child = root.firstChild;
|
|
2119
|
+
* const id: Observable<string> = child.params.map(p => p.id);
|
|
2120
|
+
* //...
|
|
2121
|
+
* }
|
|
2122
|
+
* }
|
|
2123
|
+
* ```
|
|
2124
|
+
*
|
|
2125
|
+
* @publicApi
|
|
2126
|
+
*/
|
|
2127
|
+
class RouterStateSnapshot extends Tree {
|
|
2128
|
+
/** @internal */
|
|
2129
|
+
constructor(
|
|
2130
|
+
/** The url from which this snapshot was created */
|
|
2131
|
+
url, root) {
|
|
2132
|
+
super(root);
|
|
2133
|
+
this.url = url;
|
|
2134
|
+
setRouterState(this, root);
|
|
1972
2135
|
}
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
const children = {};
|
|
1976
|
-
forEach(outlets, (commands, outlet) => {
|
|
1977
|
-
if (typeof commands === 'string') {
|
|
1978
|
-
commands = [commands];
|
|
1979
|
-
}
|
|
1980
|
-
if (commands !== null) {
|
|
1981
|
-
children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
|
|
1982
|
-
}
|
|
1983
|
-
});
|
|
1984
|
-
forEach(segmentGroup.children, (child, childOutlet) => {
|
|
1985
|
-
if (outlets[childOutlet] === undefined) {
|
|
1986
|
-
children[childOutlet] = child;
|
|
1987
|
-
}
|
|
1988
|
-
});
|
|
1989
|
-
return new UrlSegmentGroup(segmentGroup.segments, children);
|
|
2136
|
+
toString() {
|
|
2137
|
+
return serializeNode(this._root);
|
|
1990
2138
|
}
|
|
1991
2139
|
}
|
|
1992
|
-
function
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
|
|
1996
|
-
while (currentPathIndex < segmentGroup.segments.length) {
|
|
1997
|
-
if (currentCommandIndex >= commands.length)
|
|
1998
|
-
return noMatch;
|
|
1999
|
-
const path = segmentGroup.segments[currentPathIndex];
|
|
2000
|
-
const command = commands[currentCommandIndex];
|
|
2001
|
-
// Do not try to consume command as part of the prefixing if it has outlets because it can
|
|
2002
|
-
// contain outlets other than the one being processed. Consuming the outlets command would
|
|
2003
|
-
// result in other outlets being ignored.
|
|
2004
|
-
if (isCommandWithOutlets(command)) {
|
|
2005
|
-
break;
|
|
2006
|
-
}
|
|
2007
|
-
const curr = `${command}`;
|
|
2008
|
-
const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
|
|
2009
|
-
if (currentPathIndex > 0 && curr === undefined)
|
|
2010
|
-
break;
|
|
2011
|
-
if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
|
|
2012
|
-
if (!compare(curr, next, path))
|
|
2013
|
-
return noMatch;
|
|
2014
|
-
currentCommandIndex += 2;
|
|
2015
|
-
}
|
|
2016
|
-
else {
|
|
2017
|
-
if (!compare(curr, {}, path))
|
|
2018
|
-
return noMatch;
|
|
2019
|
-
currentCommandIndex++;
|
|
2020
|
-
}
|
|
2021
|
-
currentPathIndex++;
|
|
2022
|
-
}
|
|
2023
|
-
return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
|
|
2140
|
+
function setRouterState(state, node) {
|
|
2141
|
+
node.value._routerState = state;
|
|
2142
|
+
node.children.forEach(c => setRouterState(state, c));
|
|
2024
2143
|
}
|
|
2025
|
-
function
|
|
2026
|
-
const
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2144
|
+
function serializeNode(node) {
|
|
2145
|
+
const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';
|
|
2146
|
+
return `${node.value}${c}`;
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* The expectation is that the activate route is created with the right set of parameters.
|
|
2150
|
+
* So we push new values into the observables only when they are not the initial values.
|
|
2151
|
+
* And we detect that by checking if the snapshot field is set.
|
|
2152
|
+
*/
|
|
2153
|
+
function advanceActivatedRoute(route) {
|
|
2154
|
+
if (route.snapshot) {
|
|
2155
|
+
const currentSnapshot = route.snapshot;
|
|
2156
|
+
const nextSnapshot = route._futureSnapshot;
|
|
2157
|
+
route.snapshot = nextSnapshot;
|
|
2158
|
+
if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
|
|
2159
|
+
route.queryParams.next(nextSnapshot.queryParams);
|
|
2033
2160
|
}
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
const p = segmentGroup.segments[startIndex];
|
|
2037
|
-
paths.push(new UrlSegment(p.path, stringify(commands[0])));
|
|
2038
|
-
i++;
|
|
2039
|
-
continue;
|
|
2161
|
+
if (currentSnapshot.fragment !== nextSnapshot.fragment) {
|
|
2162
|
+
route.fragment.next(nextSnapshot.fragment);
|
|
2040
2163
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
if (curr && next && isMatrixParams(next)) {
|
|
2044
|
-
paths.push(new UrlSegment(curr, stringify(next)));
|
|
2045
|
-
i += 2;
|
|
2164
|
+
if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
|
|
2165
|
+
route.params.next(nextSnapshot.params);
|
|
2046
2166
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2167
|
+
if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
|
|
2168
|
+
route.url.next(nextSnapshot.url);
|
|
2169
|
+
}
|
|
2170
|
+
if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
|
|
2171
|
+
route.data.next(nextSnapshot.data);
|
|
2050
2172
|
}
|
|
2051
2173
|
}
|
|
2052
|
-
|
|
2174
|
+
else {
|
|
2175
|
+
route.snapshot = route._futureSnapshot;
|
|
2176
|
+
// this is for resolved data
|
|
2177
|
+
route.data.next(route._futureSnapshot.data);
|
|
2178
|
+
}
|
|
2053
2179
|
}
|
|
2054
|
-
function
|
|
2055
|
-
const
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2180
|
+
function equalParamsAndUrlSegments(a, b) {
|
|
2181
|
+
const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url);
|
|
2182
|
+
const parentsMismatch = !a.parent !== !b.parent;
|
|
2183
|
+
return equalUrlParams && !parentsMismatch &&
|
|
2184
|
+
(!a.parent || equalParamsAndUrlSegments(a.parent, b.parent));
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
/**
|
|
2188
|
+
* @license
|
|
2189
|
+
* Copyright Google LLC All Rights Reserved.
|
|
2190
|
+
*
|
|
2191
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
2192
|
+
* found in the LICENSE file at https://angular.io/license
|
|
2193
|
+
*/
|
|
2194
|
+
function createRouterState(routeReuseStrategy, curr, prevState) {
|
|
2195
|
+
const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
|
|
2196
|
+
return new RouterState(root, curr);
|
|
2197
|
+
}
|
|
2198
|
+
function createNode(routeReuseStrategy, curr, prevState) {
|
|
2199
|
+
// reuse an activated route that is currently displayed on the screen
|
|
2200
|
+
if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
|
|
2201
|
+
const value = prevState.value;
|
|
2202
|
+
value._futureSnapshot = curr.value;
|
|
2203
|
+
const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
|
|
2204
|
+
return new TreeNode(value, children);
|
|
2205
|
+
}
|
|
2206
|
+
else {
|
|
2207
|
+
if (routeReuseStrategy.shouldAttach(curr.value)) {
|
|
2208
|
+
// retrieve an activated route that is used to be displayed, but is not currently displayed
|
|
2209
|
+
const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
|
|
2210
|
+
if (detachedRouteHandle !== null) {
|
|
2211
|
+
const tree = detachedRouteHandle.route;
|
|
2212
|
+
tree.value._futureSnapshot = curr.value;
|
|
2213
|
+
tree.children = curr.children.map(c => createNode(routeReuseStrategy, c));
|
|
2214
|
+
return tree;
|
|
2215
|
+
}
|
|
2059
2216
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2217
|
+
const value = createActivatedRoute(curr.value);
|
|
2218
|
+
const children = curr.children.map(c => createNode(routeReuseStrategy, c));
|
|
2219
|
+
return new TreeNode(value, children);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
|
|
2223
|
+
return curr.children.map(child => {
|
|
2224
|
+
for (const p of prevState.children) {
|
|
2225
|
+
if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {
|
|
2226
|
+
return createNode(routeReuseStrategy, child, p);
|
|
2227
|
+
}
|
|
2062
2228
|
}
|
|
2229
|
+
return createNode(routeReuseStrategy, child);
|
|
2063
2230
|
});
|
|
2064
|
-
return children;
|
|
2065
|
-
}
|
|
2066
|
-
function stringify(params) {
|
|
2067
|
-
const res = {};
|
|
2068
|
-
forEach(params, (v, k) => res[k] = `${v}`);
|
|
2069
|
-
return res;
|
|
2070
2231
|
}
|
|
2071
|
-
function
|
|
2072
|
-
return
|
|
2232
|
+
function createActivatedRoute(c) {
|
|
2233
|
+
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);
|
|
2073
2234
|
}
|
|
2074
2235
|
|
|
2075
2236
|
/**
|
|
@@ -2332,9 +2493,9 @@ class RouterOutlet {
|
|
|
2332
2493
|
this.activateEvents.emit(this.activated.instance);
|
|
2333
2494
|
}
|
|
2334
2495
|
}
|
|
2335
|
-
RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
2336
|
-
RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.
|
|
2337
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
2496
|
+
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 });
|
|
2497
|
+
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 });
|
|
2498
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterOutlet, decorators: [{
|
|
2338
2499
|
type: Directive,
|
|
2339
2500
|
args: [{ selector: 'router-outlet', exportAs: 'outlet' }]
|
|
2340
2501
|
}], ctorParameters: function () {
|
|
@@ -2393,9 +2554,9 @@ function isComponentFactoryResolver(item) {
|
|
|
2393
2554
|
*/
|
|
2394
2555
|
class ɵEmptyOutletComponent {
|
|
2395
2556
|
}
|
|
2396
|
-
ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
2397
|
-
ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.
|
|
2398
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
2557
|
+
ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: ɵEmptyOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2558
|
+
ɵ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"] }] });
|
|
2559
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: ɵEmptyOutletComponent, decorators: [{
|
|
2399
2560
|
type: Component,
|
|
2400
2561
|
args: [{ template: `<router-outlet></router-outlet>` }]
|
|
2401
2562
|
}] });
|
|
@@ -3082,9 +3243,7 @@ class ApplyRedirects {
|
|
|
3082
3243
|
return new Error(`Cannot match any routes. URL Segment: '${e.segmentGroup}'`);
|
|
3083
3244
|
}
|
|
3084
3245
|
createUrlTree(rootCandidate, queryParams, fragment) {
|
|
3085
|
-
const root = rootCandidate
|
|
3086
|
-
new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
|
|
3087
|
-
rootCandidate;
|
|
3246
|
+
const root = createRoot(rootCandidate);
|
|
3088
3247
|
return new UrlTree(root, queryParams, fragment);
|
|
3089
3248
|
}
|
|
3090
3249
|
expandSegmentGroup(injector, routes, segmentGroup, outlet) {
|
|
@@ -3337,39 +3496,6 @@ class ApplyRedirects {
|
|
|
3337
3496
|
return redirectToUrlSegment;
|
|
3338
3497
|
}
|
|
3339
3498
|
}
|
|
3340
|
-
/**
|
|
3341
|
-
* When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
|
|
3342
|
-
*
|
|
3343
|
-
* When a segment group has only one child which is a primary outlet, merges that child into the
|
|
3344
|
-
* parent. That is, the child segment group's segments are merged into the `s` and the child's
|
|
3345
|
-
* children become the children of `s`. Think of this like a 'squash', merging the child segment
|
|
3346
|
-
* group into the parent.
|
|
3347
|
-
*/
|
|
3348
|
-
function mergeTrivialChildren(s) {
|
|
3349
|
-
if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
|
|
3350
|
-
const c = s.children[PRIMARY_OUTLET];
|
|
3351
|
-
return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
|
|
3352
|
-
}
|
|
3353
|
-
return s;
|
|
3354
|
-
}
|
|
3355
|
-
/**
|
|
3356
|
-
* Recursively merges primary segment children into their parents and also drops empty children
|
|
3357
|
-
* (those which have no segments and no children themselves). The latter prevents serializing a
|
|
3358
|
-
* group into something like `/a(aux:)`, where `aux` is an empty child segment.
|
|
3359
|
-
*/
|
|
3360
|
-
function squashSegmentGroup(segmentGroup) {
|
|
3361
|
-
const newChildren = {};
|
|
3362
|
-
for (const childOutlet of Object.keys(segmentGroup.children)) {
|
|
3363
|
-
const child = segmentGroup.children[childOutlet];
|
|
3364
|
-
const childCandidate = squashSegmentGroup(child);
|
|
3365
|
-
// don't add empty children
|
|
3366
|
-
if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
|
|
3367
|
-
newChildren[childOutlet] = childCandidate;
|
|
3368
|
-
}
|
|
3369
|
-
}
|
|
3370
|
-
const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
|
|
3371
|
-
return mergeTrivialChildren(s);
|
|
3372
|
-
}
|
|
3373
3499
|
|
|
3374
3500
|
/**
|
|
3375
3501
|
* @license
|
|
@@ -4200,9 +4326,9 @@ class RouterConfigLoader {
|
|
|
4200
4326
|
}));
|
|
4201
4327
|
}
|
|
4202
4328
|
}
|
|
4203
|
-
RouterConfigLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
4204
|
-
RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
4205
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
4329
|
+
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 });
|
|
4330
|
+
RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader });
|
|
4331
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader, decorators: [{
|
|
4206
4332
|
type: Injectable
|
|
4207
4333
|
}], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.Compiler }]; } });
|
|
4208
4334
|
|
|
@@ -5195,9 +5321,9 @@ class Router {
|
|
|
5195
5321
|
return { navigationId };
|
|
5196
5322
|
}
|
|
5197
5323
|
}
|
|
5198
|
-
Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5199
|
-
Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
5200
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5324
|
+
Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
|
|
5325
|
+
Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router });
|
|
5326
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router, decorators: [{
|
|
5201
5327
|
type: Injectable
|
|
5202
5328
|
}], ctorParameters: function () { return [{ type: i0.Type }, { type: UrlSerializer }, { type: ChildrenOutletContexts }, { type: i3.Location }, { type: i0.Injector }, { type: i0.Compiler }, { type: undefined }]; } });
|
|
5203
5329
|
function validateCommands(commands) {
|
|
@@ -5389,9 +5515,9 @@ class RouterLink {
|
|
|
5389
5515
|
});
|
|
5390
5516
|
}
|
|
5391
5517
|
}
|
|
5392
|
-
RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5393
|
-
RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.
|
|
5394
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5518
|
+
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 });
|
|
5519
|
+
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 });
|
|
5520
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLink, decorators: [{
|
|
5395
5521
|
type: Directive,
|
|
5396
5522
|
args: [{ selector: ':not(a):not(area)[routerLink]' }]
|
|
5397
5523
|
}], ctorParameters: function () {
|
|
@@ -5510,9 +5636,9 @@ class RouterLinkWithHref {
|
|
|
5510
5636
|
});
|
|
5511
5637
|
}
|
|
5512
5638
|
}
|
|
5513
|
-
RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5514
|
-
RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.
|
|
5515
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5639
|
+
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 });
|
|
5640
|
+
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 });
|
|
5641
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkWithHref, decorators: [{
|
|
5516
5642
|
type: Directive,
|
|
5517
5643
|
args: [{ selector: 'a[routerLink],area[routerLink]' }]
|
|
5518
5644
|
}], ctorParameters: function () { return [{ type: Router }, { type: ActivatedRoute }, { type: i3.LocationStrategy }]; }, propDecorators: { target: [{
|
|
@@ -5739,9 +5865,9 @@ class RouterLinkActive {
|
|
|
5739
5865
|
this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
|
|
5740
5866
|
}
|
|
5741
5867
|
}
|
|
5742
|
-
RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5743
|
-
RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.
|
|
5744
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5868
|
+
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 });
|
|
5869
|
+
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 });
|
|
5870
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkActive, decorators: [{
|
|
5745
5871
|
type: Directive,
|
|
5746
5872
|
args: [{
|
|
5747
5873
|
selector: '[routerLinkActive]',
|
|
@@ -5847,9 +5973,9 @@ class DefaultTitleStrategy extends TitleStrategy {
|
|
|
5847
5973
|
}
|
|
5848
5974
|
}
|
|
5849
5975
|
}
|
|
5850
|
-
DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5851
|
-
DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
5852
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
5976
|
+
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 });
|
|
5977
|
+
DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, providedIn: 'root' });
|
|
5978
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, decorators: [{
|
|
5853
5979
|
type: Injectable,
|
|
5854
5980
|
args: [{ providedIn: 'root' }]
|
|
5855
5981
|
}], ctorParameters: function () { return [{ type: i1.Title }]; } });
|
|
@@ -5984,9 +6110,9 @@ class RouterPreloader {
|
|
|
5984
6110
|
});
|
|
5985
6111
|
}
|
|
5986
6112
|
}
|
|
5987
|
-
RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
5988
|
-
RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
5989
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
6113
|
+
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 });
|
|
6114
|
+
RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader });
|
|
6115
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader, decorators: [{
|
|
5990
6116
|
type: Injectable
|
|
5991
6117
|
}], ctorParameters: function () { return [{ type: Router }, { type: i0.Compiler }, { type: i0.EnvironmentInjector }, { type: PreloadingStrategy }, { type: RouterConfigLoader }]; } });
|
|
5992
6118
|
|
|
@@ -6065,9 +6191,9 @@ class RouterScroller {
|
|
|
6065
6191
|
}
|
|
6066
6192
|
}
|
|
6067
6193
|
}
|
|
6068
|
-
RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
6069
|
-
RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.
|
|
6070
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
6194
|
+
RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
|
|
6195
|
+
RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller });
|
|
6196
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller, decorators: [{
|
|
6071
6197
|
type: Injectable
|
|
6072
6198
|
}], ctorParameters: function () { return [{ type: Router }, { type: i3.ViewportScroller }, { type: undefined }]; } });
|
|
6073
6199
|
|
|
@@ -6209,10 +6335,10 @@ class RouterModule {
|
|
|
6209
6335
|
return { ngModule: RouterModule, providers: [provideRoutes(routes)] };
|
|
6210
6336
|
}
|
|
6211
6337
|
}
|
|
6212
|
-
RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.
|
|
6213
|
-
RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.
|
|
6214
|
-
RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.
|
|
6215
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.
|
|
6338
|
+
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 });
|
|
6339
|
+
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] });
|
|
6340
|
+
RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule });
|
|
6341
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, decorators: [{
|
|
6216
6342
|
type: NgModule,
|
|
6217
6343
|
args: [{
|
|
6218
6344
|
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
|