@angular/router 14.0.0-next.9 → 14.0.0-rc.2

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.
Files changed (41) hide show
  1. package/esm2020/src/apply_redirects.mjs +52 -48
  2. package/esm2020/src/components/empty_outlet.mjs +3 -3
  3. package/esm2020/src/create_url_tree.mjs +25 -11
  4. package/esm2020/src/directives/router_link.mjs +6 -6
  5. package/esm2020/src/directives/router_link_active.mjs +22 -4
  6. package/esm2020/src/directives/router_outlet.mjs +25 -16
  7. package/esm2020/src/events.mjs +57 -1
  8. package/esm2020/src/index.mjs +1 -1
  9. package/esm2020/src/models.mjs +2 -7
  10. package/esm2020/src/operators/activate_routes.mjs +7 -14
  11. package/esm2020/src/operators/apply_redirects.mjs +3 -3
  12. package/esm2020/src/operators/resolve_data.mjs +14 -24
  13. package/esm2020/src/page_title_strategy.mjs +3 -3
  14. package/esm2020/src/recognize.mjs +24 -6
  15. package/esm2020/src/router.mjs +43 -25
  16. package/esm2020/src/router_config_loader.mjs +74 -27
  17. package/esm2020/src/router_module.mjs +14 -14
  18. package/esm2020/src/router_outlet_context.mjs +6 -1
  19. package/esm2020/src/router_preloader.mjs +48 -32
  20. package/esm2020/src/router_scroller.mjs +3 -3
  21. package/esm2020/src/router_state.mjs +4 -4
  22. package/esm2020/src/url_tree.mjs +1 -1
  23. package/esm2020/src/utils/config.mjs +86 -11
  24. package/esm2020/src/utils/config_matching.mjs +4 -1
  25. package/esm2020/src/utils/preactivation.mjs +5 -14
  26. package/esm2020/src/version.mjs +1 -1
  27. package/esm2020/testing/src/router_testing_module.mjs +4 -4
  28. package/fesm2015/router.mjs +927 -670
  29. package/fesm2015/router.mjs.map +1 -1
  30. package/fesm2015/testing.mjs +5 -5
  31. package/fesm2015/upgrade.mjs +1 -1
  32. package/fesm2020/router.mjs +912 -673
  33. package/fesm2020/router.mjs.map +1 -1
  34. package/fesm2020/testing.mjs +5 -5
  35. package/fesm2020/upgrade.mjs +1 -1
  36. package/{router.d.ts → index.d.ts} +3858 -3730
  37. package/package.json +9 -9
  38. package/testing/{testing.d.ts → index.d.ts} +65 -64
  39. package/upgrade/{upgrade.d.ts → index.d.ts} +51 -50
  40. package/testing/package.json +0 -9
  41. package/upgrade/package.json +0 -10
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v14.0.0-next.9
2
+ * @license Angular v14.0.0-rc.2
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -7,9 +7,9 @@
7
7
  import * as i3 from '@angular/common';
8
8
  import { Location, LocationStrategy, PlatformLocation, APP_BASE_HREF, ViewportScroller, HashLocationStrategy, PathLocationStrategy, LOCATION_INITIALIZED } from '@angular/common';
9
9
  import * as i0 from '@angular/core';
10
- import { ɵisObservable, ɵisPromise, EventEmitter, Directive, Attribute, Output, Component, NgModuleRef, InjectionToken, InjectFlags, NgModuleFactory, ɵConsole, NgZone, Injectable, ɵcoerceToBoolean, Input, HostListener, HostBinding, Optional, ContentChildren, Injector, Compiler, NgProbeToken, ANALYZE_FOR_ENTRY_COMPONENTS, SkipSelf, Inject, APP_INITIALIZER, APP_BOOTSTRAP_LISTENER, NgModule, ApplicationRef, Version } from '@angular/core';
10
+ import { ɵisObservable, ɵisPromise, EventEmitter, Directive, Attribute, Output, Component, createEnvironmentInjector, ɵisStandalone, ComponentFactoryResolver, InjectionToken, InjectFlags, NgModuleFactory, Injectable, NgModuleRef, ɵConsole, NgZone, ɵcoerceToBoolean, Input, HostListener, HostBinding, Optional, ContentChildren, Injector, Compiler, NgProbeToken, ANALYZE_FOR_ENTRY_COMPONENTS, SkipSelf, Inject, APP_INITIALIZER, APP_BOOTSTRAP_LISTENER, NgModule, ApplicationRef, Version } from '@angular/core';
11
11
  import { from, of, BehaviorSubject, combineLatest, throwError, EmptyError, concat, defer, Observable, EMPTY, ConnectableObservable, Subject } from 'rxjs';
12
- import { map, switchMap, take, startWith, scan, filter, catchError, concatMap, last as last$1, first, mergeMap, tap, takeLast, refCount, finalize, mergeAll } from 'rxjs/operators';
12
+ import { map, switchMap, take, startWith, scan, filter, catchError, concatMap, last as last$1, first, mergeMap, tap, takeLast, mapTo, finalize, refCount, defaultIfEmpty, mergeAll } from 'rxjs/operators';
13
13
  import * as i1 from '@angular/platform-browser';
14
14
 
15
15
  /**
@@ -69,6 +69,7 @@ class NavigationStart extends RouterEvent {
69
69
  /** @docsNotRequired */
70
70
  restoredState = null) {
71
71
  super(id, url);
72
+ this.type = 0 /* EventType.NavigationStart */;
72
73
  this.navigationTrigger = navigationTrigger;
73
74
  this.restoredState = restoredState;
74
75
  }
@@ -96,6 +97,7 @@ class NavigationEnd extends RouterEvent {
96
97
  urlAfterRedirects) {
97
98
  super(id, url);
98
99
  this.urlAfterRedirects = urlAfterRedirects;
100
+ this.type = 1 /* EventType.NavigationEnd */;
99
101
  }
100
102
  /** @docsNotRequired */
101
103
  toString() {
@@ -123,6 +125,7 @@ class NavigationCancel extends RouterEvent {
123
125
  reason) {
124
126
  super(id, url);
125
127
  this.reason = reason;
128
+ this.type = 2 /* EventType.NavigationCancel */;
126
129
  }
127
130
  /** @docsNotRequired */
128
131
  toString() {
@@ -148,6 +151,7 @@ class NavigationError extends RouterEvent {
148
151
  error) {
149
152
  super(id, url);
150
153
  this.error = error;
154
+ this.type = 3 /* EventType.NavigationError */;
151
155
  }
152
156
  /** @docsNotRequired */
153
157
  toString() {
@@ -172,6 +176,7 @@ class RoutesRecognized extends RouterEvent {
172
176
  super(id, url);
173
177
  this.urlAfterRedirects = urlAfterRedirects;
174
178
  this.state = state;
179
+ this.type = 4 /* EventType.RoutesRecognized */;
175
180
  }
176
181
  /** @docsNotRequired */
177
182
  toString() {
@@ -198,6 +203,7 @@ class GuardsCheckStart extends RouterEvent {
198
203
  super(id, url);
199
204
  this.urlAfterRedirects = urlAfterRedirects;
200
205
  this.state = state;
206
+ this.type = 7 /* EventType.GuardsCheckStart */;
201
207
  }
202
208
  toString() {
203
209
  return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
@@ -226,6 +232,7 @@ class GuardsCheckEnd extends RouterEvent {
226
232
  this.urlAfterRedirects = urlAfterRedirects;
227
233
  this.state = state;
228
234
  this.shouldActivate = shouldActivate;
235
+ this.type = 8 /* EventType.GuardsCheckEnd */;
229
236
  }
230
237
  toString() {
231
238
  return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
@@ -254,6 +261,7 @@ class ResolveStart extends RouterEvent {
254
261
  super(id, url);
255
262
  this.urlAfterRedirects = urlAfterRedirects;
256
263
  this.state = state;
264
+ this.type = 5 /* EventType.ResolveStart */;
257
265
  }
258
266
  toString() {
259
267
  return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
@@ -278,6 +286,7 @@ class ResolveEnd extends RouterEvent {
278
286
  super(id, url);
279
287
  this.urlAfterRedirects = urlAfterRedirects;
280
288
  this.state = state;
289
+ this.type = 6 /* EventType.ResolveEnd */;
281
290
  }
282
291
  toString() {
283
292
  return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
@@ -295,6 +304,7 @@ class RouteConfigLoadStart {
295
304
  /** @docsNotRequired */
296
305
  route) {
297
306
  this.route = route;
307
+ this.type = 9 /* EventType.RouteConfigLoadStart */;
298
308
  }
299
309
  toString() {
300
310
  return `RouteConfigLoadStart(path: ${this.route.path})`;
@@ -312,6 +322,7 @@ class RouteConfigLoadEnd {
312
322
  /** @docsNotRequired */
313
323
  route) {
314
324
  this.route = route;
325
+ this.type = 10 /* EventType.RouteConfigLoadEnd */;
315
326
  }
316
327
  toString() {
317
328
  return `RouteConfigLoadEnd(path: ${this.route.path})`;
@@ -330,6 +341,7 @@ class ChildActivationStart {
330
341
  /** @docsNotRequired */
331
342
  snapshot) {
332
343
  this.snapshot = snapshot;
344
+ this.type = 11 /* EventType.ChildActivationStart */;
333
345
  }
334
346
  toString() {
335
347
  const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
@@ -348,6 +360,7 @@ class ChildActivationEnd {
348
360
  /** @docsNotRequired */
349
361
  snapshot) {
350
362
  this.snapshot = snapshot;
363
+ this.type = 12 /* EventType.ChildActivationEnd */;
351
364
  }
352
365
  toString() {
353
366
  const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
@@ -367,6 +380,7 @@ class ActivationStart {
367
380
  /** @docsNotRequired */
368
381
  snapshot) {
369
382
  this.snapshot = snapshot;
383
+ this.type = 13 /* EventType.ActivationStart */;
370
384
  }
371
385
  toString() {
372
386
  const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
@@ -386,6 +400,7 @@ class ActivationEnd {
386
400
  /** @docsNotRequired */
387
401
  snapshot) {
388
402
  this.snapshot = snapshot;
403
+ this.type = 14 /* EventType.ActivationEnd */;
389
404
  }
390
405
  toString() {
391
406
  const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
@@ -408,12 +423,53 @@ class Scroll {
408
423
  this.routerEvent = routerEvent;
409
424
  this.position = position;
410
425
  this.anchor = anchor;
426
+ this.type = 15 /* EventType.Scroll */;
411
427
  }
412
428
  toString() {
413
429
  const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
414
430
  return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
415
431
  }
416
432
  }
433
+ function stringifyEvent(routerEvent) {
434
+ if (!('type' in routerEvent)) {
435
+ return `Unknown Router Event: ${routerEvent.constructor.name}`;
436
+ }
437
+ switch (routerEvent.type) {
438
+ case 14 /* EventType.ActivationEnd */:
439
+ return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
440
+ case 13 /* EventType.ActivationStart */:
441
+ return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
442
+ case 12 /* EventType.ChildActivationEnd */:
443
+ return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
444
+ case 11 /* EventType.ChildActivationStart */:
445
+ return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`;
446
+ case 8 /* EventType.GuardsCheckEnd */:
447
+ return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
448
+ case 7 /* EventType.GuardsCheckStart */:
449
+ return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
450
+ case 2 /* EventType.NavigationCancel */:
451
+ return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
452
+ case 1 /* EventType.NavigationEnd */:
453
+ return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
454
+ case 3 /* EventType.NavigationError */:
455
+ return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
456
+ case 0 /* EventType.NavigationStart */:
457
+ return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
458
+ case 6 /* EventType.ResolveEnd */:
459
+ return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
460
+ case 5 /* EventType.ResolveStart */:
461
+ return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
462
+ case 10 /* EventType.RouteConfigLoadEnd */:
463
+ return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
464
+ case 9 /* EventType.RouteConfigLoadStart */:
465
+ return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
466
+ case 4 /* EventType.RoutesRecognized */:
467
+ return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
468
+ case 15 /* EventType.Scroll */:
469
+ const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
470
+ return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
471
+ }
472
+ }
417
473
 
418
474
  /**
419
475
  * @license
@@ -1362,7 +1418,6 @@ class ActivatedRoute {
1362
1418
  /** The outlet name of the route, a constant. */
1363
1419
  outlet,
1364
1420
  /** The component of the route, a constant. */
1365
- // TODO(vsavkin): remove |string
1366
1421
  component, futureSnapshot) {
1367
1422
  this.url = url;
1368
1423
  this.params = params;
@@ -1456,7 +1511,7 @@ function flattenInherited(pathFromRoot) {
1456
1511
  return pathFromRoot.reduce((res, curr) => {
1457
1512
  const params = { ...res.params, ...curr.params };
1458
1513
  const data = { ...res.data, ...curr.data };
1459
- const resolve = { ...res.resolve, ...curr._resolvedData };
1514
+ const resolve = { ...curr.data, ...res.resolve, ...curr.routeConfig?.data, ...curr._resolvedData };
1460
1515
  return { params, data, resolve };
1461
1516
  }, { params: {}, data: {}, resolve: {} });
1462
1517
  }
@@ -1517,7 +1572,7 @@ class ActivatedRouteSnapshot {
1517
1572
  /** The outlet name of the route */
1518
1573
  outlet,
1519
1574
  /** The component of the route */
1520
- component, routeConfig, urlSegment, lastPathIndex, resolve) {
1575
+ component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
1521
1576
  this.url = url;
1522
1577
  this.params = params;
1523
1578
  this.queryParams = queryParams;
@@ -1528,6 +1583,7 @@ class ActivatedRouteSnapshot {
1528
1583
  this.routeConfig = routeConfig;
1529
1584
  this._urlSegment = urlSegment;
1530
1585
  this._lastPathIndex = lastPathIndex;
1586
+ this._correctedLastPathIndex = correctedLastPathIndex ?? lastPathIndex;
1531
1587
  this._resolve = resolve;
1532
1588
  }
1533
1589
  /** The root of the router state */
@@ -1719,11 +1775,26 @@ function createUrlTree(route, urlTree, commands, queryParams, fragment) {
1719
1775
  if (nav.toRoot()) {
1720
1776
  return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment);
1721
1777
  }
1722
- const startingPosition = findStartingPosition(nav, urlTree, route);
1723
- const segmentGroup = startingPosition.processChildren ?
1724
- updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
1725
- updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
1726
- return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
1778
+ function createTreeUsingPathIndex(lastPathIndex) {
1779
+ const startingPosition = findStartingPosition(nav, urlTree, route.snapshot?._urlSegment, lastPathIndex);
1780
+ const segmentGroup = startingPosition.processChildren ?
1781
+ updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
1782
+ updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
1783
+ return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
1784
+ }
1785
+ // Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this
1786
+ // may be the case. Since we try to access it at an earlier point before the refactor to add the
1787
+ // warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it
1788
+ // didn't before.
1789
+ const result = createTreeUsingPathIndex(route.snapshot?._lastPathIndex);
1790
+ // Check if application is relying on `relativeLinkResolution: 'legacy'`
1791
+ if (typeof ngDevMode === 'undefined' || !!ngDevMode) {
1792
+ const correctedResult = createTreeUsingPathIndex(route.snapshot?._correctedLastPathIndex);
1793
+ if (correctedResult.toString() !== result.toString()) {
1794
+ 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.`);
1795
+ }
1796
+ }
1797
+ return result;
1727
1798
  }
1728
1799
  function isMatrixParams(command) {
1729
1800
  return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
@@ -1828,12 +1899,11 @@ class Position {
1828
1899
  this.index = index;
1829
1900
  }
1830
1901
  }
1831
- function findStartingPosition(nav, tree, route) {
1902
+ function findStartingPosition(nav, tree, segmentGroup, lastPathIndex) {
1832
1903
  if (nav.isAbsolute) {
1833
1904
  return new Position(tree.root, true, 0);
1834
1905
  }
1835
- if (route.snapshot._lastPathIndex === -1) {
1836
- const segmentGroup = route.snapshot._urlSegment;
1906
+ if (lastPathIndex === -1) {
1837
1907
  // Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
1838
1908
  // see issue #26224, #13011, #35687
1839
1909
  // However, if the ActivatedRoute is the root we should process children like above.
@@ -1841,8 +1911,8 @@ function findStartingPosition(nav, tree, route) {
1841
1911
  return new Position(segmentGroup, processChildren, 0);
1842
1912
  }
1843
1913
  const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
1844
- const index = route.snapshot._lastPathIndex + modifier;
1845
- return createPositionApplyingDoubleDots(route.snapshot._urlSegment, index, nav.numberOfDoubleDots);
1914
+ const index = lastPathIndex + modifier;
1915
+ return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
1846
1916
  }
1847
1917
  function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) {
1848
1918
  let g = group;
@@ -2005,181 +2075,76 @@ function compare(path, params, segment) {
2005
2075
  * Use of this source code is governed by an MIT-style license that can be
2006
2076
  * found in the LICENSE file at https://angular.io/license
2007
2077
  */
2008
- const activateRoutes = (rootContexts, routeReuseStrategy, forwardEvent) => map(t => {
2009
- new ActivateRoutes(routeReuseStrategy, t.targetRouterState, t.currentRouterState, forwardEvent)
2010
- .activate(rootContexts);
2011
- return t;
2012
- });
2013
- class ActivateRoutes {
2014
- constructor(routeReuseStrategy, futureState, currState, forwardEvent) {
2015
- this.routeReuseStrategy = routeReuseStrategy;
2016
- this.futureState = futureState;
2017
- this.currState = currState;
2018
- this.forwardEvent = forwardEvent;
2019
- }
2020
- activate(parentContexts) {
2021
- const futureRoot = this.futureState._root;
2022
- const currRoot = this.currState ? this.currState._root : null;
2023
- this.deactivateChildRoutes(futureRoot, currRoot, parentContexts);
2024
- advanceActivatedRoute(this.futureState.root);
2025
- this.activateChildRoutes(futureRoot, currRoot, parentContexts);
2026
- }
2027
- // De-activate the child route that are not re-used for the future state
2028
- deactivateChildRoutes(futureNode, currNode, contexts) {
2029
- const children = nodeChildrenAsMap(currNode);
2030
- // Recurse on the routes active in the future state to de-activate deeper children
2031
- futureNode.children.forEach(futureChild => {
2032
- const childOutletName = futureChild.value.outlet;
2033
- this.deactivateRoutes(futureChild, children[childOutletName], contexts);
2034
- delete children[childOutletName];
2035
- });
2036
- // De-activate the routes that will not be re-used
2037
- forEach(children, (v, childName) => {
2038
- this.deactivateRouteAndItsChildren(v, contexts);
2039
- });
2040
- }
2041
- deactivateRoutes(futureNode, currNode, parentContext) {
2042
- const future = futureNode.value;
2043
- const curr = currNode ? currNode.value : null;
2044
- if (future === curr) {
2045
- // Reusing the node, check to see if the children need to be de-activated
2046
- if (future.component) {
2047
- // If we have a normal route, we need to go through an outlet.
2048
- const context = parentContext.getContext(future.outlet);
2049
- if (context) {
2050
- this.deactivateChildRoutes(futureNode, currNode, context.children);
2051
- }
2052
- }
2053
- else {
2054
- // if we have a componentless route, we recurse but keep the same outlet map.
2055
- this.deactivateChildRoutes(futureNode, currNode, parentContext);
2056
- }
2057
- }
2058
- else {
2059
- if (curr) {
2060
- // Deactivate the current route which will not be re-used
2061
- this.deactivateRouteAndItsChildren(currNode, parentContext);
2062
- }
2063
- }
2078
+ /**
2079
+ * Store contextual information about a `RouterOutlet`
2080
+ *
2081
+ * @publicApi
2082
+ */
2083
+ class OutletContext {
2084
+ constructor() {
2085
+ this.outlet = null;
2086
+ this.route = null;
2087
+ /**
2088
+ * @deprecated Passing a resolver to retrieve a component factory is not required and is
2089
+ * deprecated since v14.
2090
+ */
2091
+ this.resolver = null;
2092
+ this.injector = null;
2093
+ this.children = new ChildrenOutletContexts();
2094
+ this.attachRef = null;
2064
2095
  }
2065
- deactivateRouteAndItsChildren(route, parentContexts) {
2066
- // If there is no component, the Route is never attached to an outlet (because there is no
2067
- // component to attach).
2068
- if (route.value.component && this.routeReuseStrategy.shouldDetach(route.value.snapshot)) {
2069
- this.detachAndStoreRouteSubtree(route, parentContexts);
2070
- }
2071
- else {
2072
- this.deactivateRouteAndOutlet(route, parentContexts);
2073
- }
2096
+ }
2097
+ /**
2098
+ * Store contextual information about the children (= nested) `RouterOutlet`
2099
+ *
2100
+ * @publicApi
2101
+ */
2102
+ class ChildrenOutletContexts {
2103
+ constructor() {
2104
+ // contexts for child outlets, by name.
2105
+ this.contexts = new Map();
2074
2106
  }
2075
- detachAndStoreRouteSubtree(route, parentContexts) {
2076
- const context = parentContexts.getContext(route.value.outlet);
2077
- const contexts = context && route.value.component ? context.children : parentContexts;
2078
- const children = nodeChildrenAsMap(route);
2079
- for (const childOutlet of Object.keys(children)) {
2080
- this.deactivateRouteAndItsChildren(children[childOutlet], contexts);
2081
- }
2082
- if (context && context.outlet) {
2083
- const componentRef = context.outlet.detach();
2084
- const contexts = context.children.onOutletDeactivated();
2085
- this.routeReuseStrategy.store(route.value.snapshot, { componentRef, route, contexts });
2086
- }
2107
+ /** Called when a `RouterOutlet` directive is instantiated */
2108
+ onChildOutletCreated(childName, outlet) {
2109
+ const context = this.getOrCreateContext(childName);
2110
+ context.outlet = outlet;
2111
+ this.contexts.set(childName, context);
2087
2112
  }
2088
- deactivateRouteAndOutlet(route, parentContexts) {
2089
- const context = parentContexts.getContext(route.value.outlet);
2090
- // The context could be `null` if we are on a componentless route but there may still be
2091
- // children that need deactivating.
2092
- const contexts = context && route.value.component ? context.children : parentContexts;
2093
- const children = nodeChildrenAsMap(route);
2094
- for (const childOutlet of Object.keys(children)) {
2095
- this.deactivateRouteAndItsChildren(children[childOutlet], contexts);
2096
- }
2097
- if (context && context.outlet) {
2098
- // Destroy the component
2099
- context.outlet.deactivate();
2100
- // Destroy the contexts for all the outlets that were in the component
2101
- context.children.onOutletDeactivated();
2102
- // Clear the information about the attached component on the context but keep the reference to
2103
- // the outlet.
2113
+ /**
2114
+ * Called when a `RouterOutlet` directive is destroyed.
2115
+ * We need to keep the context as the outlet could be destroyed inside a NgIf and might be
2116
+ * re-created later.
2117
+ */
2118
+ onChildOutletDestroyed(childName) {
2119
+ const context = this.getContext(childName);
2120
+ if (context) {
2121
+ context.outlet = null;
2104
2122
  context.attachRef = null;
2105
- context.resolver = null;
2106
- context.route = null;
2107
2123
  }
2108
2124
  }
2109
- activateChildRoutes(futureNode, currNode, contexts) {
2110
- const children = nodeChildrenAsMap(currNode);
2111
- futureNode.children.forEach(c => {
2112
- this.activateRoutes(c, children[c.value.outlet], contexts);
2113
- this.forwardEvent(new ActivationEnd(c.value.snapshot));
2114
- });
2115
- if (futureNode.children.length) {
2116
- this.forwardEvent(new ChildActivationEnd(futureNode.value.snapshot));
2117
- }
2125
+ /**
2126
+ * Called when the corresponding route is deactivated during navigation.
2127
+ * Because the component get destroyed, all children outlet are destroyed.
2128
+ */
2129
+ onOutletDeactivated() {
2130
+ const contexts = this.contexts;
2131
+ this.contexts = new Map();
2132
+ return contexts;
2118
2133
  }
2119
- activateRoutes(futureNode, currNode, parentContexts) {
2120
- const future = futureNode.value;
2121
- const curr = currNode ? currNode.value : null;
2122
- advanceActivatedRoute(future);
2123
- // reusing the node
2124
- if (future === curr) {
2125
- if (future.component) {
2126
- // If we have a normal route, we need to go through an outlet.
2127
- const context = parentContexts.getOrCreateContext(future.outlet);
2128
- this.activateChildRoutes(futureNode, currNode, context.children);
2129
- }
2130
- else {
2131
- // if we have a componentless route, we recurse but keep the same outlet map.
2132
- this.activateChildRoutes(futureNode, currNode, parentContexts);
2133
- }
2134
- }
2135
- else {
2136
- if (future.component) {
2137
- // if we have a normal route, we need to place the component into the outlet and recurse.
2138
- const context = parentContexts.getOrCreateContext(future.outlet);
2139
- if (this.routeReuseStrategy.shouldAttach(future.snapshot)) {
2140
- const stored = this.routeReuseStrategy.retrieve(future.snapshot);
2141
- this.routeReuseStrategy.store(future.snapshot, null);
2142
- context.children.onOutletReAttached(stored.contexts);
2143
- context.attachRef = stored.componentRef;
2144
- context.route = stored.route.value;
2145
- if (context.outlet) {
2146
- // Attach right away when the outlet has already been instantiated
2147
- // Otherwise attach from `RouterOutlet.ngOnInit` when it is instantiated
2148
- context.outlet.attach(stored.componentRef, stored.route.value);
2149
- }
2150
- advanceActivatedRoute(stored.route.value);
2151
- this.activateChildRoutes(futureNode, null, context.children);
2152
- }
2153
- else {
2154
- const config = parentLoadedConfig(future.snapshot);
2155
- const cmpFactoryResolver = config ? config.module.componentFactoryResolver : null;
2156
- context.attachRef = null;
2157
- context.route = future;
2158
- context.resolver = cmpFactoryResolver;
2159
- if (context.outlet) {
2160
- // Activate the outlet when it has already been instantiated
2161
- // Otherwise it will get activated from its `ngOnInit` when instantiated
2162
- context.outlet.activateWith(future, cmpFactoryResolver);
2163
- }
2164
- this.activateChildRoutes(futureNode, null, context.children);
2165
- }
2166
- }
2167
- else {
2168
- // if we have a componentless route, we recurse but keep the same outlet map.
2169
- this.activateChildRoutes(futureNode, null, parentContexts);
2170
- }
2134
+ onOutletReAttached(contexts) {
2135
+ this.contexts = contexts;
2136
+ }
2137
+ getOrCreateContext(childName) {
2138
+ let context = this.getContext(childName);
2139
+ if (!context) {
2140
+ context = new OutletContext();
2141
+ this.contexts.set(childName, context);
2171
2142
  }
2143
+ return context;
2172
2144
  }
2173
- }
2174
- function parentLoadedConfig(snapshot) {
2175
- for (let s = snapshot.parent; s; s = s.parent) {
2176
- const route = s.routeConfig;
2177
- if (route && route._loadedConfig)
2178
- return route._loadedConfig;
2179
- if (route && route.component)
2180
- return null;
2145
+ getContext(childName) {
2146
+ return this.contexts.get(childName) || null;
2181
2147
  }
2182
- return null;
2183
2148
  }
2184
2149
 
2185
2150
  /**
@@ -2189,182 +2154,13 @@ function parentLoadedConfig(snapshot) {
2189
2154
  * Use of this source code is governed by an MIT-style license that can be
2190
2155
  * found in the LICENSE file at https://angular.io/license
2191
2156
  */
2192
- class LoadedRouterConfig {
2193
- constructor(routes, module) {
2194
- this.routes = routes;
2195
- this.module = module;
2196
- }
2197
- }
2198
-
2199
2157
  /**
2200
- * @license
2201
- * Copyright Google LLC All Rights Reserved.
2158
+ * @description
2202
2159
  *
2203
- * Use of this source code is governed by an MIT-style license that can be
2204
- * found in the LICENSE file at https://angular.io/license
2205
- */
2206
- /**
2207
- * Simple function check, but generic so type inference will flow. Example:
2160
+ * Acts as a placeholder that Angular dynamically fills based on the current router state.
2208
2161
  *
2209
- * function product(a: number, b: number) {
2210
- * return a * b;
2211
- * }
2212
- *
2213
- * if (isFunction<product>(fn)) {
2214
- * return fn(1, 2);
2215
- * } else {
2216
- * throw "Must provide the `product` function";
2217
- * }
2218
- */
2219
- function isFunction(v) {
2220
- return typeof v === 'function';
2221
- }
2222
- function isBoolean(v) {
2223
- return typeof v === 'boolean';
2224
- }
2225
- function isUrlTree(v) {
2226
- return v instanceof UrlTree;
2227
- }
2228
- function isCanLoad(guard) {
2229
- return guard && isFunction(guard.canLoad);
2230
- }
2231
- function isCanActivate(guard) {
2232
- return guard && isFunction(guard.canActivate);
2233
- }
2234
- function isCanActivateChild(guard) {
2235
- return guard && isFunction(guard.canActivateChild);
2236
- }
2237
- function isCanDeactivate(guard) {
2238
- return guard && isFunction(guard.canDeactivate);
2239
- }
2240
-
2241
- /**
2242
- * @license
2243
- * Copyright Google LLC All Rights Reserved.
2244
- *
2245
- * Use of this source code is governed by an MIT-style license that can be
2246
- * found in the LICENSE file at https://angular.io/license
2247
- */
2248
- const INITIAL_VALUE = Symbol('INITIAL_VALUE');
2249
- function prioritizedGuardValue() {
2250
- return switchMap(obs => {
2251
- return combineLatest(obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE))))
2252
- .pipe(scan((acc, list) => {
2253
- let isPending = false;
2254
- return list.reduce((innerAcc, val, i) => {
2255
- if (innerAcc !== INITIAL_VALUE)
2256
- return innerAcc;
2257
- // Toggle pending flag if any values haven't been set yet
2258
- if (val === INITIAL_VALUE)
2259
- isPending = true;
2260
- // Any other return values are only valid if we haven't yet hit a pending
2261
- // call. This guarantees that in the case of a guard at the bottom of the
2262
- // tree that returns a redirect, we will wait for the higher priority
2263
- // guard at the top to finish before performing the redirect.
2264
- if (!isPending) {
2265
- // Early return when we hit a `false` value as that should always
2266
- // cancel navigation
2267
- if (val === false)
2268
- return val;
2269
- if (i === list.length - 1 || isUrlTree(val)) {
2270
- return val;
2271
- }
2272
- }
2273
- return innerAcc;
2274
- }, acc);
2275
- }, INITIAL_VALUE), filter(item => item !== INITIAL_VALUE), map(item => isUrlTree(item) ? item : item === true), //
2276
- take(1));
2277
- });
2278
- }
2279
-
2280
- /**
2281
- * @license
2282
- * Copyright Google LLC All Rights Reserved.
2283
- *
2284
- * Use of this source code is governed by an MIT-style license that can be
2285
- * found in the LICENSE file at https://angular.io/license
2286
- */
2287
- /**
2288
- * Store contextual information about a `RouterOutlet`
2289
- *
2290
- * @publicApi
2291
- */
2292
- class OutletContext {
2293
- constructor() {
2294
- this.outlet = null;
2295
- this.route = null;
2296
- this.resolver = null;
2297
- this.children = new ChildrenOutletContexts();
2298
- this.attachRef = null;
2299
- }
2300
- }
2301
- /**
2302
- * Store contextual information about the children (= nested) `RouterOutlet`
2303
- *
2304
- * @publicApi
2305
- */
2306
- class ChildrenOutletContexts {
2307
- constructor() {
2308
- // contexts for child outlets, by name.
2309
- this.contexts = new Map();
2310
- }
2311
- /** Called when a `RouterOutlet` directive is instantiated */
2312
- onChildOutletCreated(childName, outlet) {
2313
- const context = this.getOrCreateContext(childName);
2314
- context.outlet = outlet;
2315
- this.contexts.set(childName, context);
2316
- }
2317
- /**
2318
- * Called when a `RouterOutlet` directive is destroyed.
2319
- * We need to keep the context as the outlet could be destroyed inside a NgIf and might be
2320
- * re-created later.
2321
- */
2322
- onChildOutletDestroyed(childName) {
2323
- const context = this.getContext(childName);
2324
- if (context) {
2325
- context.outlet = null;
2326
- context.attachRef = null;
2327
- }
2328
- }
2329
- /**
2330
- * Called when the corresponding route is deactivated during navigation.
2331
- * Because the component get destroyed, all children outlet are destroyed.
2332
- */
2333
- onOutletDeactivated() {
2334
- const contexts = this.contexts;
2335
- this.contexts = new Map();
2336
- return contexts;
2337
- }
2338
- onOutletReAttached(contexts) {
2339
- this.contexts = contexts;
2340
- }
2341
- getOrCreateContext(childName) {
2342
- let context = this.getContext(childName);
2343
- if (!context) {
2344
- context = new OutletContext();
2345
- this.contexts.set(childName, context);
2346
- }
2347
- return context;
2348
- }
2349
- getContext(childName) {
2350
- return this.contexts.get(childName) || null;
2351
- }
2352
- }
2353
-
2354
- /**
2355
- * @license
2356
- * Copyright Google LLC All Rights Reserved.
2357
- *
2358
- * Use of this source code is governed by an MIT-style license that can be
2359
- * found in the LICENSE file at https://angular.io/license
2360
- */
2361
- /**
2362
- * @description
2363
- *
2364
- * Acts as a placeholder that Angular dynamically fills based on the current router state.
2365
- *
2366
- * Each outlet can have a unique name, determined by the optional `name` attribute.
2367
- * The name cannot be set or changed dynamically. If not set, default value is "primary".
2162
+ * Each outlet can have a unique name, determined by the optional `name` attribute.
2163
+ * The name cannot be set or changed dynamically. If not set, default value is "primary".
2368
2164
  *
2369
2165
  * ```
2370
2166
  * <router-outlet></router-outlet>
@@ -2410,11 +2206,11 @@ class ChildrenOutletContexts {
2410
2206
  * @publicApi
2411
2207
  */
2412
2208
  class RouterOutlet {
2413
- constructor(parentContexts, location, resolver, name, changeDetector) {
2209
+ constructor(parentContexts, location, name, changeDetector, environmentInjector) {
2414
2210
  this.parentContexts = parentContexts;
2415
2211
  this.location = location;
2416
- this.resolver = resolver;
2417
2212
  this.changeDetector = changeDetector;
2213
+ this.environmentInjector = environmentInjector;
2418
2214
  this.activated = null;
2419
2215
  this._activatedRoute = null;
2420
2216
  this.activateEvents = new EventEmitter();
@@ -2449,7 +2245,7 @@ class RouterOutlet {
2449
2245
  }
2450
2246
  else {
2451
2247
  // otherwise the component defined in the configuration is created
2452
- this.activateWith(context.route, context.resolver || null);
2248
+ this.activateWith(context.route, context.injector);
2453
2249
  }
2454
2250
  }
2455
2251
  }
@@ -2508,33 +2304,39 @@ class RouterOutlet {
2508
2304
  this.deactivateEvents.emit(c);
2509
2305
  }
2510
2306
  }
2511
- activateWith(activatedRoute, resolver) {
2307
+ activateWith(activatedRoute, resolverOrInjector) {
2512
2308
  if (this.isActivated) {
2513
2309
  throw new Error('Cannot activate an already activated outlet');
2514
2310
  }
2515
2311
  this._activatedRoute = activatedRoute;
2312
+ const location = this.location;
2516
2313
  const snapshot = activatedRoute._futureSnapshot;
2517
- const component = snapshot.routeConfig.component;
2518
- resolver = resolver || this.resolver;
2519
- const factory = resolver.resolveComponentFactory(component);
2314
+ const component = snapshot.component;
2520
2315
  const childContexts = this.parentContexts.getOrCreateContext(this.name).children;
2521
- const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector);
2522
- this.activated = this.location.createComponent(factory, this.location.length, injector);
2316
+ const injector = new OutletInjector(activatedRoute, childContexts, location.injector);
2317
+ if (resolverOrInjector && isComponentFactoryResolver(resolverOrInjector)) {
2318
+ const factory = resolverOrInjector.resolveComponentFactory(component);
2319
+ this.activated = location.createComponent(factory, location.length, injector);
2320
+ }
2321
+ else {
2322
+ const environmentInjector = resolverOrInjector ?? this.environmentInjector;
2323
+ this.activated = location.createComponent(component, { index: location.length, injector, environmentInjector });
2324
+ }
2523
2325
  // Calling `markForCheck` to make sure we will run the change detection when the
2524
2326
  // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
2525
2327
  this.changeDetector.markForCheck();
2526
2328
  this.activateEvents.emit(this.activated.instance);
2527
2329
  }
2528
2330
  }
2529
- RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterOutlet, deps: [{ token: ChildrenOutletContexts }, { token: i0.ViewContainerRef }, { token: i0.ComponentFactoryResolver }, { token: 'name', attribute: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
2530
- RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: RouterOutlet, selector: "router-outlet", outputs: { activateEvents: "activate", deactivateEvents: "deactivate", attachEvents: "attach", detachEvents: "detach" }, exportAs: ["outlet"], ngImport: i0 });
2531
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterOutlet, decorators: [{
2331
+ RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterOutlet, deps: [{ token: ChildrenOutletContexts }, { token: i0.ViewContainerRef }, { token: 'name', attribute: true }, { token: i0.ChangeDetectorRef }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Directive });
2332
+ RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.0-rc.2", type: RouterOutlet, selector: "router-outlet", outputs: { activateEvents: "activate", deactivateEvents: "deactivate", attachEvents: "attach", detachEvents: "detach" }, exportAs: ["outlet"], ngImport: i0 });
2333
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterOutlet, decorators: [{
2532
2334
  type: Directive,
2533
2335
  args: [{ selector: 'router-outlet', exportAs: 'outlet' }]
2534
- }], ctorParameters: function () { return [{ type: ChildrenOutletContexts }, { type: i0.ViewContainerRef }, { type: i0.ComponentFactoryResolver }, { type: undefined, decorators: [{
2336
+ }], ctorParameters: function () { return [{ type: ChildrenOutletContexts }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
2535
2337
  type: Attribute,
2536
2338
  args: ['name']
2537
- }] }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { activateEvents: [{
2339
+ }] }, { type: i0.ChangeDetectorRef }, { type: i0.EnvironmentInjector }]; }, propDecorators: { activateEvents: [{
2538
2340
  type: Output,
2539
2341
  args: ['activate']
2540
2342
  }], deactivateEvents: [{
@@ -2560,7 +2362,400 @@ class OutletInjector {
2560
2362
  if (token === ChildrenOutletContexts) {
2561
2363
  return this.childContexts;
2562
2364
  }
2563
- return this.parent.get(token, notFoundValue);
2365
+ return this.parent.get(token, notFoundValue);
2366
+ }
2367
+ }
2368
+ function isComponentFactoryResolver(item) {
2369
+ return !!item.resolveComponentFactory;
2370
+ }
2371
+
2372
+ /**
2373
+ * @license
2374
+ * Copyright Google LLC All Rights Reserved.
2375
+ *
2376
+ * Use of this source code is governed by an MIT-style license that can be
2377
+ * found in the LICENSE file at https://angular.io/license
2378
+ */
2379
+ /**
2380
+ * This component is used internally within the router to be a placeholder when an empty
2381
+ * router-outlet is needed. For example, with a config such as:
2382
+ *
2383
+ * `{path: 'parent', outlet: 'nav', children: [...]}`
2384
+ *
2385
+ * In order to render, there needs to be a component on this config, which will default
2386
+ * to this `EmptyOutletComponent`.
2387
+ */
2388
+ class ɵEmptyOutletComponent {
2389
+ }
2390
+ ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: ɵEmptyOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2391
+ ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.0-rc.2", 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"] }] });
2392
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: ɵEmptyOutletComponent, decorators: [{
2393
+ type: Component,
2394
+ args: [{ template: `<router-outlet></router-outlet>` }]
2395
+ }] });
2396
+
2397
+ /**
2398
+ * @license
2399
+ * Copyright Google LLC All Rights Reserved.
2400
+ *
2401
+ * Use of this source code is governed by an MIT-style license that can be
2402
+ * found in the LICENSE file at https://angular.io/license
2403
+ */
2404
+ /**
2405
+ * Creates an `EnvironmentInjector` if the `Route` has providers and one does not already exist
2406
+ * and returns the injector. Otherwise, if the `Route` does not have `providers`, returns the
2407
+ * `currentInjector`.
2408
+ *
2409
+ * @param route The route that might have providers
2410
+ * @param currentInjector The parent injector of the `Route`
2411
+ */
2412
+ function getOrCreateRouteInjectorIfNeeded(route, currentInjector) {
2413
+ if (route.providers && !route._injector) {
2414
+ route._injector =
2415
+ createEnvironmentInjector(route.providers, currentInjector, `Route: ${route.path}`);
2416
+ }
2417
+ return route._injector ?? currentInjector;
2418
+ }
2419
+ function getLoadedRoutes(route) {
2420
+ return route._loadedRoutes;
2421
+ }
2422
+ function getLoadedInjector(route) {
2423
+ return route._loadedInjector;
2424
+ }
2425
+ function getLoadedComponent(route) {
2426
+ return route._loadedComponent;
2427
+ }
2428
+ function getProvidersInjector(route) {
2429
+ return route._injector;
2430
+ }
2431
+ function validateConfig(config, parentPath = '', requireStandaloneComponents = false) {
2432
+ // forEach doesn't iterate undefined values
2433
+ for (let i = 0; i < config.length; i++) {
2434
+ const route = config[i];
2435
+ const fullPath = getFullPath(parentPath, route);
2436
+ validateNode(route, fullPath, requireStandaloneComponents);
2437
+ }
2438
+ }
2439
+ function assertStandalone(fullPath, component) {
2440
+ if (component && !ɵisStandalone(component)) {
2441
+ throw new Error(`Invalid configuration of route '${fullPath}'. The component must be standalone.`);
2442
+ }
2443
+ }
2444
+ function validateNode(route, fullPath, requireStandaloneComponents) {
2445
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
2446
+ if (!route) {
2447
+ throw new Error(`
2448
+ Invalid configuration of route '${fullPath}': Encountered undefined route.
2449
+ The reason might be an extra comma.
2450
+
2451
+ Example:
2452
+ const routes: Routes = [
2453
+ { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
2454
+ { path: 'dashboard', component: DashboardComponent },, << two commas
2455
+ { path: 'detail/:id', component: HeroDetailComponent }
2456
+ ];
2457
+ `);
2458
+ }
2459
+ if (Array.isArray(route)) {
2460
+ throw new Error(`Invalid configuration of route '${fullPath}': Array cannot be specified`);
2461
+ }
2462
+ if (!route.component && !route.loadComponent && !route.children && !route.loadChildren &&
2463
+ (route.outlet && route.outlet !== PRIMARY_OUTLET)) {
2464
+ throw new Error(`Invalid configuration of route '${fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`);
2465
+ }
2466
+ if (route.redirectTo && route.children) {
2467
+ throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`);
2468
+ }
2469
+ if (route.redirectTo && route.loadChildren) {
2470
+ throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`);
2471
+ }
2472
+ if (route.children && route.loadChildren) {
2473
+ throw new Error(`Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`);
2474
+ }
2475
+ if (route.redirectTo && (route.component || route.loadComponent)) {
2476
+ throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and component/loadComponent cannot be used together`);
2477
+ }
2478
+ if (route.component && route.loadComponent) {
2479
+ throw new Error(`Invalid configuration of route '${fullPath}': component and loadComponent cannot be used together`);
2480
+ }
2481
+ if (route.redirectTo && route.canActivate) {
2482
+ throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and canActivate cannot be used together. Redirects happen before activation ` +
2483
+ `so canActivate will never be executed.`);
2484
+ }
2485
+ if (route.path && route.matcher) {
2486
+ throw new Error(`Invalid configuration of route '${fullPath}': path and matcher cannot be used together`);
2487
+ }
2488
+ if (route.redirectTo === void 0 && !route.component && !route.loadComponent &&
2489
+ !route.children && !route.loadChildren) {
2490
+ throw new Error(`Invalid configuration of route '${fullPath}'. One of the following must be provided: component, loadComponent, redirectTo, children or loadChildren`);
2491
+ }
2492
+ if (route.path === void 0 && route.matcher === void 0) {
2493
+ throw new Error(`Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`);
2494
+ }
2495
+ if (typeof route.path === 'string' && route.path.charAt(0) === '/') {
2496
+ throw new Error(`Invalid configuration of route '${fullPath}': path cannot start with a slash`);
2497
+ }
2498
+ if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) {
2499
+ const exp = `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`;
2500
+ throw new Error(`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`);
2501
+ }
2502
+ if (requireStandaloneComponents) {
2503
+ assertStandalone(fullPath, route.component);
2504
+ }
2505
+ }
2506
+ if (route.children) {
2507
+ validateConfig(route.children, fullPath, requireStandaloneComponents);
2508
+ }
2509
+ }
2510
+ function getFullPath(parentPath, currentRoute) {
2511
+ if (!currentRoute) {
2512
+ return parentPath;
2513
+ }
2514
+ if (!parentPath && !currentRoute.path) {
2515
+ return '';
2516
+ }
2517
+ else if (parentPath && !currentRoute.path) {
2518
+ return `${parentPath}/`;
2519
+ }
2520
+ else if (!parentPath && currentRoute.path) {
2521
+ return currentRoute.path;
2522
+ }
2523
+ else {
2524
+ return `${parentPath}/${currentRoute.path}`;
2525
+ }
2526
+ }
2527
+ /**
2528
+ * Makes a copy of the config and adds any default required properties.
2529
+ */
2530
+ function standardizeConfig(r) {
2531
+ const children = r.children && r.children.map(standardizeConfig);
2532
+ const c = children ? { ...r, children } : { ...r };
2533
+ if ((!c.component && !c.loadComponent) && (children || c.loadChildren) &&
2534
+ (c.outlet && c.outlet !== PRIMARY_OUTLET)) {
2535
+ c.component = ɵEmptyOutletComponent;
2536
+ }
2537
+ return c;
2538
+ }
2539
+ /** Returns the `route.outlet` or PRIMARY_OUTLET if none exists. */
2540
+ function getOutlet(route) {
2541
+ return route.outlet || PRIMARY_OUTLET;
2542
+ }
2543
+ /**
2544
+ * Sorts the `routes` such that the ones with an outlet matching `outletName` come first.
2545
+ * The order of the configs is otherwise preserved.
2546
+ */
2547
+ function sortByMatchingOutlets(routes, outletName) {
2548
+ const sortedConfig = routes.filter(r => getOutlet(r) === outletName);
2549
+ sortedConfig.push(...routes.filter(r => getOutlet(r) !== outletName));
2550
+ return sortedConfig;
2551
+ }
2552
+ /**
2553
+ * Gets the first injector in the snapshot's parent tree.
2554
+ *
2555
+ * If the `Route` has a static list of providers, the returned injector will be the one created from
2556
+ * those. If it does not exist, the returned injector may come from the parents, which may be from a
2557
+ * loaded config or their static providers.
2558
+ *
2559
+ * Returns `null` if there is neither this nor any parents have a stored injector.
2560
+ *
2561
+ * Generally used for retrieving the injector to use for getting tokens for guards/resolvers and
2562
+ * also used for getting the correct injector to use for creating components.
2563
+ */
2564
+ function getClosestRouteInjector(snapshot) {
2565
+ if (!snapshot)
2566
+ return null;
2567
+ // If the current route has its own injector, which is created from the static providers on the
2568
+ // route itself, we should use that. Otherwise, we start at the parent since we do not want to
2569
+ // include the lazy loaded injector from this route.
2570
+ if (snapshot.routeConfig?._injector) {
2571
+ return snapshot.routeConfig._injector;
2572
+ }
2573
+ for (let s = snapshot.parent; s; s = s.parent) {
2574
+ const route = s.routeConfig;
2575
+ // Note that the order here is important. `_loadedInjector` stored on the route with
2576
+ // `loadChildren: () => NgModule` so it applies to child routes with priority. The `_injector`
2577
+ // is created from the static providers on that parent route, so it applies to the children as
2578
+ // well, but only if there is no lazy loaded NgModuleRef injector.
2579
+ if (route?._loadedInjector)
2580
+ return route._loadedInjector;
2581
+ if (route?._injector)
2582
+ return route._injector;
2583
+ }
2584
+ return null;
2585
+ }
2586
+
2587
+ /**
2588
+ * @license
2589
+ * Copyright Google LLC All Rights Reserved.
2590
+ *
2591
+ * Use of this source code is governed by an MIT-style license that can be
2592
+ * found in the LICENSE file at https://angular.io/license
2593
+ */
2594
+ const activateRoutes = (rootContexts, routeReuseStrategy, forwardEvent) => map(t => {
2595
+ new ActivateRoutes(routeReuseStrategy, t.targetRouterState, t.currentRouterState, forwardEvent)
2596
+ .activate(rootContexts);
2597
+ return t;
2598
+ });
2599
+ class ActivateRoutes {
2600
+ constructor(routeReuseStrategy, futureState, currState, forwardEvent) {
2601
+ this.routeReuseStrategy = routeReuseStrategy;
2602
+ this.futureState = futureState;
2603
+ this.currState = currState;
2604
+ this.forwardEvent = forwardEvent;
2605
+ }
2606
+ activate(parentContexts) {
2607
+ const futureRoot = this.futureState._root;
2608
+ const currRoot = this.currState ? this.currState._root : null;
2609
+ this.deactivateChildRoutes(futureRoot, currRoot, parentContexts);
2610
+ advanceActivatedRoute(this.futureState.root);
2611
+ this.activateChildRoutes(futureRoot, currRoot, parentContexts);
2612
+ }
2613
+ // De-activate the child route that are not re-used for the future state
2614
+ deactivateChildRoutes(futureNode, currNode, contexts) {
2615
+ const children = nodeChildrenAsMap(currNode);
2616
+ // Recurse on the routes active in the future state to de-activate deeper children
2617
+ futureNode.children.forEach(futureChild => {
2618
+ const childOutletName = futureChild.value.outlet;
2619
+ this.deactivateRoutes(futureChild, children[childOutletName], contexts);
2620
+ delete children[childOutletName];
2621
+ });
2622
+ // De-activate the routes that will not be re-used
2623
+ forEach(children, (v, childName) => {
2624
+ this.deactivateRouteAndItsChildren(v, contexts);
2625
+ });
2626
+ }
2627
+ deactivateRoutes(futureNode, currNode, parentContext) {
2628
+ const future = futureNode.value;
2629
+ const curr = currNode ? currNode.value : null;
2630
+ if (future === curr) {
2631
+ // Reusing the node, check to see if the children need to be de-activated
2632
+ if (future.component) {
2633
+ // If we have a normal route, we need to go through an outlet.
2634
+ const context = parentContext.getContext(future.outlet);
2635
+ if (context) {
2636
+ this.deactivateChildRoutes(futureNode, currNode, context.children);
2637
+ }
2638
+ }
2639
+ else {
2640
+ // if we have a componentless route, we recurse but keep the same outlet map.
2641
+ this.deactivateChildRoutes(futureNode, currNode, parentContext);
2642
+ }
2643
+ }
2644
+ else {
2645
+ if (curr) {
2646
+ // Deactivate the current route which will not be re-used
2647
+ this.deactivateRouteAndItsChildren(currNode, parentContext);
2648
+ }
2649
+ }
2650
+ }
2651
+ deactivateRouteAndItsChildren(route, parentContexts) {
2652
+ // If there is no component, the Route is never attached to an outlet (because there is no
2653
+ // component to attach).
2654
+ if (route.value.component && this.routeReuseStrategy.shouldDetach(route.value.snapshot)) {
2655
+ this.detachAndStoreRouteSubtree(route, parentContexts);
2656
+ }
2657
+ else {
2658
+ this.deactivateRouteAndOutlet(route, parentContexts);
2659
+ }
2660
+ }
2661
+ detachAndStoreRouteSubtree(route, parentContexts) {
2662
+ const context = parentContexts.getContext(route.value.outlet);
2663
+ const contexts = context && route.value.component ? context.children : parentContexts;
2664
+ const children = nodeChildrenAsMap(route);
2665
+ for (const childOutlet of Object.keys(children)) {
2666
+ this.deactivateRouteAndItsChildren(children[childOutlet], contexts);
2667
+ }
2668
+ if (context && context.outlet) {
2669
+ const componentRef = context.outlet.detach();
2670
+ const contexts = context.children.onOutletDeactivated();
2671
+ this.routeReuseStrategy.store(route.value.snapshot, { componentRef, route, contexts });
2672
+ }
2673
+ }
2674
+ deactivateRouteAndOutlet(route, parentContexts) {
2675
+ const context = parentContexts.getContext(route.value.outlet);
2676
+ // The context could be `null` if we are on a componentless route but there may still be
2677
+ // children that need deactivating.
2678
+ const contexts = context && route.value.component ? context.children : parentContexts;
2679
+ const children = nodeChildrenAsMap(route);
2680
+ for (const childOutlet of Object.keys(children)) {
2681
+ this.deactivateRouteAndItsChildren(children[childOutlet], contexts);
2682
+ }
2683
+ if (context && context.outlet) {
2684
+ // Destroy the component
2685
+ context.outlet.deactivate();
2686
+ // Destroy the contexts for all the outlets that were in the component
2687
+ context.children.onOutletDeactivated();
2688
+ // Clear the information about the attached component on the context but keep the reference to
2689
+ // the outlet.
2690
+ context.attachRef = null;
2691
+ context.resolver = null;
2692
+ context.route = null;
2693
+ }
2694
+ }
2695
+ activateChildRoutes(futureNode, currNode, contexts) {
2696
+ const children = nodeChildrenAsMap(currNode);
2697
+ futureNode.children.forEach(c => {
2698
+ this.activateRoutes(c, children[c.value.outlet], contexts);
2699
+ this.forwardEvent(new ActivationEnd(c.value.snapshot));
2700
+ });
2701
+ if (futureNode.children.length) {
2702
+ this.forwardEvent(new ChildActivationEnd(futureNode.value.snapshot));
2703
+ }
2704
+ }
2705
+ activateRoutes(futureNode, currNode, parentContexts) {
2706
+ const future = futureNode.value;
2707
+ const curr = currNode ? currNode.value : null;
2708
+ advanceActivatedRoute(future);
2709
+ // reusing the node
2710
+ if (future === curr) {
2711
+ if (future.component) {
2712
+ // If we have a normal route, we need to go through an outlet.
2713
+ const context = parentContexts.getOrCreateContext(future.outlet);
2714
+ this.activateChildRoutes(futureNode, currNode, context.children);
2715
+ }
2716
+ else {
2717
+ // if we have a componentless route, we recurse but keep the same outlet map.
2718
+ this.activateChildRoutes(futureNode, currNode, parentContexts);
2719
+ }
2720
+ }
2721
+ else {
2722
+ if (future.component) {
2723
+ // if we have a normal route, we need to place the component into the outlet and recurse.
2724
+ const context = parentContexts.getOrCreateContext(future.outlet);
2725
+ if (this.routeReuseStrategy.shouldAttach(future.snapshot)) {
2726
+ const stored = this.routeReuseStrategy.retrieve(future.snapshot);
2727
+ this.routeReuseStrategy.store(future.snapshot, null);
2728
+ context.children.onOutletReAttached(stored.contexts);
2729
+ context.attachRef = stored.componentRef;
2730
+ context.route = stored.route.value;
2731
+ if (context.outlet) {
2732
+ // Attach right away when the outlet has already been instantiated
2733
+ // Otherwise attach from `RouterOutlet.ngOnInit` when it is instantiated
2734
+ context.outlet.attach(stored.componentRef, stored.route.value);
2735
+ }
2736
+ advanceActivatedRoute(stored.route.value);
2737
+ this.activateChildRoutes(futureNode, null, context.children);
2738
+ }
2739
+ else {
2740
+ const injector = getClosestRouteInjector(future.snapshot);
2741
+ const cmpFactoryResolver = injector?.get(ComponentFactoryResolver) ?? null;
2742
+ context.attachRef = null;
2743
+ context.route = future;
2744
+ context.resolver = cmpFactoryResolver;
2745
+ context.injector = injector;
2746
+ if (context.outlet) {
2747
+ // Activate the outlet when it has already been instantiated
2748
+ // Otherwise it will get activated from its `ngOnInit` when instantiated
2749
+ context.outlet.activateWith(future, context.injector);
2750
+ }
2751
+ this.activateChildRoutes(futureNode, null, context.children);
2752
+ }
2753
+ }
2754
+ else {
2755
+ // if we have a componentless route, we recurse but keep the same outlet map.
2756
+ this.activateChildRoutes(futureNode, null, parentContexts);
2757
+ }
2758
+ }
2564
2759
  }
2565
2760
  }
2566
2761
 
@@ -2572,22 +2767,39 @@ class OutletInjector {
2572
2767
  * found in the LICENSE file at https://angular.io/license
2573
2768
  */
2574
2769
  /**
2575
- * This component is used internally within the router to be a placeholder when an empty
2576
- * router-outlet is needed. For example, with a config such as:
2770
+ * Simple function check, but generic so type inference will flow. Example:
2577
2771
  *
2578
- * `{path: 'parent', outlet: 'nav', children: [...]}`
2772
+ * function product(a: number, b: number) {
2773
+ * return a * b;
2774
+ * }
2579
2775
  *
2580
- * In order to render, there needs to be a component on this config, which will default
2581
- * to this `EmptyOutletComponent`.
2776
+ * if (isFunction<product>(fn)) {
2777
+ * return fn(1, 2);
2778
+ * } else {
2779
+ * throw "Must provide the `product` function";
2780
+ * }
2582
2781
  */
2583
- class ɵEmptyOutletComponent {
2782
+ function isFunction(v) {
2783
+ return typeof v === 'function';
2784
+ }
2785
+ function isBoolean(v) {
2786
+ return typeof v === 'boolean';
2787
+ }
2788
+ function isUrlTree(v) {
2789
+ return v instanceof UrlTree;
2790
+ }
2791
+ function isCanLoad(guard) {
2792
+ return guard && isFunction(guard.canLoad);
2793
+ }
2794
+ function isCanActivate(guard) {
2795
+ return guard && isFunction(guard.canActivate);
2796
+ }
2797
+ function isCanActivateChild(guard) {
2798
+ return guard && isFunction(guard.canActivateChild);
2799
+ }
2800
+ function isCanDeactivate(guard) {
2801
+ return guard && isFunction(guard.canDeactivate);
2584
2802
  }
2585
- ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: ɵEmptyOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2586
- ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "14.0.0-next.9", type: ɵEmptyOutletComponent, selector: "ng-component", ngImport: i0, template: `<router-outlet></router-outlet>`, isInline: true, directives: [{ type: RouterOutlet, selector: "router-outlet", outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
2587
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: ɵEmptyOutletComponent, decorators: [{
2588
- type: Component,
2589
- args: [{ template: `<router-outlet></router-outlet>` }]
2590
- }] });
2591
2803
 
2592
2804
  /**
2593
2805
  * @license
@@ -2596,113 +2808,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9",
2596
2808
  * Use of this source code is governed by an MIT-style license that can be
2597
2809
  * found in the LICENSE file at https://angular.io/license
2598
2810
  */
2599
- function validateConfig(config, parentPath = '') {
2600
- // forEach doesn't iterate undefined values
2601
- for (let i = 0; i < config.length; i++) {
2602
- const route = config[i];
2603
- const fullPath = getFullPath(parentPath, route);
2604
- validateNode(route, fullPath);
2605
- }
2606
- }
2607
- function validateNode(route, fullPath) {
2608
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
2609
- if (!route) {
2610
- throw new Error(`
2611
- Invalid configuration of route '${fullPath}': Encountered undefined route.
2612
- The reason might be an extra comma.
2613
-
2614
- Example:
2615
- const routes: Routes = [
2616
- { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
2617
- { path: 'dashboard', component: DashboardComponent },, << two commas
2618
- { path: 'detail/:id', component: HeroDetailComponent }
2619
- ];
2620
- `);
2621
- }
2622
- if (Array.isArray(route)) {
2623
- throw new Error(`Invalid configuration of route '${fullPath}': Array cannot be specified`);
2624
- }
2625
- if (!route.component && !route.children && !route.loadChildren &&
2626
- (route.outlet && route.outlet !== PRIMARY_OUTLET)) {
2627
- throw new Error(`Invalid configuration of route '${fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`);
2628
- }
2629
- if (route.redirectTo && route.children) {
2630
- throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`);
2631
- }
2632
- if (route.redirectTo && route.loadChildren) {
2633
- throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`);
2634
- }
2635
- if (route.children && route.loadChildren) {
2636
- throw new Error(`Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`);
2637
- }
2638
- if (route.redirectTo && route.component) {
2639
- throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and component cannot be used together`);
2640
- }
2641
- if (route.redirectTo && route.canActivate) {
2642
- throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and canActivate cannot be used together. Redirects happen before activation ` +
2643
- `so canActivate will never be executed.`);
2644
- }
2645
- if (route.path && route.matcher) {
2646
- throw new Error(`Invalid configuration of route '${fullPath}': path and matcher cannot be used together`);
2647
- }
2648
- if (route.redirectTo === void 0 && !route.component && !route.children && !route.loadChildren) {
2649
- throw new Error(`Invalid configuration of route '${fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`);
2650
- }
2651
- if (route.path === void 0 && route.matcher === void 0) {
2652
- throw new Error(`Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`);
2653
- }
2654
- if (typeof route.path === 'string' && route.path.charAt(0) === '/') {
2655
- throw new Error(`Invalid configuration of route '${fullPath}': path cannot start with a slash`);
2656
- }
2657
- if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) {
2658
- const exp = `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`;
2659
- throw new Error(`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`);
2660
- }
2661
- }
2662
- if (route.children) {
2663
- validateConfig(route.children, fullPath);
2664
- }
2665
- }
2666
- function getFullPath(parentPath, currentRoute) {
2667
- if (!currentRoute) {
2668
- return parentPath;
2669
- }
2670
- if (!parentPath && !currentRoute.path) {
2671
- return '';
2672
- }
2673
- else if (parentPath && !currentRoute.path) {
2674
- return `${parentPath}/`;
2675
- }
2676
- else if (!parentPath && currentRoute.path) {
2677
- return currentRoute.path;
2678
- }
2679
- else {
2680
- return `${parentPath}/${currentRoute.path}`;
2681
- }
2682
- }
2683
- /**
2684
- * Makes a copy of the config and adds any default required properties.
2685
- */
2686
- function standardizeConfig(r) {
2687
- const children = r.children && r.children.map(standardizeConfig);
2688
- const c = children ? { ...r, children } : { ...r };
2689
- if (!c.component && (children || c.loadChildren) && (c.outlet && c.outlet !== PRIMARY_OUTLET)) {
2690
- c.component = ɵEmptyOutletComponent;
2691
- }
2692
- return c;
2693
- }
2694
- /** Returns the `route.outlet` or PRIMARY_OUTLET if none exists. */
2695
- function getOutlet(route) {
2696
- return route.outlet || PRIMARY_OUTLET;
2697
- }
2698
- /**
2699
- * Sorts the `routes` such that the ones with an outlet matching `outletName` come first.
2700
- * The order of the configs is otherwise preserved.
2701
- */
2702
- function sortByMatchingOutlets(routes, outletName) {
2703
- const sortedConfig = routes.filter(r => getOutlet(r) === outletName);
2704
- sortedConfig.push(...routes.filter(r => getOutlet(r) !== outletName));
2705
- return sortedConfig;
2811
+ const INITIAL_VALUE = Symbol('INITIAL_VALUE');
2812
+ function prioritizedGuardValue() {
2813
+ return switchMap(obs => {
2814
+ return combineLatest(obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE))))
2815
+ .pipe(scan((acc, list) => {
2816
+ let isPending = false;
2817
+ return list.reduce((innerAcc, val, i) => {
2818
+ if (innerAcc !== INITIAL_VALUE)
2819
+ return innerAcc;
2820
+ // Toggle pending flag if any values haven't been set yet
2821
+ if (val === INITIAL_VALUE)
2822
+ isPending = true;
2823
+ // Any other return values are only valid if we haven't yet hit a pending
2824
+ // call. This guarantees that in the case of a guard at the bottom of the
2825
+ // tree that returns a redirect, we will wait for the higher priority
2826
+ // guard at the top to finish before performing the redirect.
2827
+ if (!isPending) {
2828
+ // Early return when we hit a `false` value as that should always
2829
+ // cancel navigation
2830
+ if (val === false)
2831
+ return val;
2832
+ if (i === list.length - 1 || isUrlTree(val)) {
2833
+ return val;
2834
+ }
2835
+ }
2836
+ return innerAcc;
2837
+ }, acc);
2838
+ }, INITIAL_VALUE), filter(item => item !== INITIAL_VALUE), map(item => isUrlTree(item) ? item : item === true), //
2839
+ take(1));
2840
+ });
2706
2841
  }
2707
2842
 
2708
2843
  /**
@@ -2780,6 +2915,9 @@ function addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedS
2780
2915
  s._sourceSegment = segmentGroup;
2781
2916
  if (relativeLinkResolution === 'legacy') {
2782
2917
  s._segmentIndexShift = segmentGroup.segments.length;
2918
+ if (typeof ngDevMode === 'undefined' || !!ngDevMode) {
2919
+ s._segmentIndexShiftCorrected = consumedSegments.length;
2920
+ }
2783
2921
  }
2784
2922
  else {
2785
2923
  s._segmentIndexShift = consumedSegments.length;
@@ -2880,17 +3018,17 @@ function canLoadFails(route) {
2880
3018
  *
2881
3019
  * Lazy modules are loaded along the way.
2882
3020
  */
2883
- function applyRedirects$1(moduleInjector, configLoader, urlSerializer, urlTree, config) {
2884
- return new ApplyRedirects(moduleInjector, configLoader, urlSerializer, urlTree, config).apply();
3021
+ function applyRedirects$1(injector, configLoader, urlSerializer, urlTree, config) {
3022
+ return new ApplyRedirects(injector, configLoader, urlSerializer, urlTree, config).apply();
2885
3023
  }
2886
3024
  class ApplyRedirects {
2887
- constructor(moduleInjector, configLoader, urlSerializer, urlTree, config) {
3025
+ constructor(injector, configLoader, urlSerializer, urlTree, config) {
3026
+ this.injector = injector;
2888
3027
  this.configLoader = configLoader;
2889
3028
  this.urlSerializer = urlSerializer;
2890
3029
  this.urlTree = urlTree;
2891
3030
  this.config = config;
2892
3031
  this.allowRedirects = true;
2893
- this.ngModule = moduleInjector.get(NgModuleRef);
2894
3032
  }
2895
3033
  apply() {
2896
3034
  const splitGroup = split(this.urlTree.root, [], [], this.config).segmentGroup;
@@ -2901,7 +3039,7 @@ class ApplyRedirects {
2901
3039
  // them. We should be able to remove this logic as a "breaking change" but should do some more
2902
3040
  // investigation into the failures first.
2903
3041
  const rootSegmentGroup = new UrlSegmentGroup(splitGroup.segments, splitGroup.children);
2904
- const expanded$ = this.expandSegmentGroup(this.ngModule, this.config, rootSegmentGroup, PRIMARY_OUTLET);
3042
+ const expanded$ = this.expandSegmentGroup(this.injector, this.config, rootSegmentGroup, PRIMARY_OUTLET);
2905
3043
  const urlTrees$ = expanded$.pipe(map((rootSegmentGroup) => {
2906
3044
  return this.createUrlTree(squashSegmentGroup(rootSegmentGroup), this.urlTree.queryParams, this.urlTree.fragment);
2907
3045
  }));
@@ -2920,7 +3058,7 @@ class ApplyRedirects {
2920
3058
  }));
2921
3059
  }
2922
3060
  match(tree) {
2923
- const expanded$ = this.expandSegmentGroup(this.ngModule, this.config, tree.root, PRIMARY_OUTLET);
3061
+ const expanded$ = this.expandSegmentGroup(this.injector, this.config, tree.root, PRIMARY_OUTLET);
2924
3062
  const mapped$ = expanded$.pipe(map((rootSegmentGroup) => {
2925
3063
  return this.createUrlTree(squashSegmentGroup(rootSegmentGroup), tree.queryParams, tree.fragment);
2926
3064
  }));
@@ -2940,15 +3078,15 @@ class ApplyRedirects {
2940
3078
  rootCandidate;
2941
3079
  return new UrlTree(root, queryParams, fragment);
2942
3080
  }
2943
- expandSegmentGroup(ngModule, routes, segmentGroup, outlet) {
3081
+ expandSegmentGroup(injector, routes, segmentGroup, outlet) {
2944
3082
  if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
2945
- return this.expandChildren(ngModule, routes, segmentGroup)
3083
+ return this.expandChildren(injector, routes, segmentGroup)
2946
3084
  .pipe(map((children) => new UrlSegmentGroup([], children)));
2947
3085
  }
2948
- return this.expandSegment(ngModule, segmentGroup, routes, segmentGroup.segments, outlet, true);
3086
+ return this.expandSegment(injector, segmentGroup, routes, segmentGroup.segments, outlet, true);
2949
3087
  }
2950
3088
  // Recursively expand segment groups for all the child outlets
2951
- expandChildren(ngModule, routes, segmentGroup) {
3089
+ expandChildren(injector, routes, segmentGroup) {
2952
3090
  // Expand outlets one at a time, starting with the primary outlet. We need to do it this way
2953
3091
  // because an absolute redirect from the primary outlet takes precedence.
2954
3092
  const childOutlets = [];
@@ -2967,16 +3105,16 @@ class ApplyRedirects {
2967
3105
  // first, followed by routes for other outlets, which might match if they have an
2968
3106
  // empty path.
2969
3107
  const sortedRoutes = sortByMatchingOutlets(routes, childOutlet);
2970
- return this.expandSegmentGroup(ngModule, sortedRoutes, child, childOutlet)
3108
+ return this.expandSegmentGroup(injector, sortedRoutes, child, childOutlet)
2971
3109
  .pipe(map(s => ({ segment: s, outlet: childOutlet })));
2972
3110
  }), scan((children, expandedChild) => {
2973
3111
  children[expandedChild.outlet] = expandedChild.segment;
2974
3112
  return children;
2975
3113
  }, {}), last$1());
2976
3114
  }
2977
- expandSegment(ngModule, segmentGroup, routes, segments, outlet, allowRedirects) {
2978
- return from(routes).pipe(concatMap((r) => {
2979
- const expanded$ = this.expandSegmentAgainstRoute(ngModule, segmentGroup, routes, r, segments, outlet, allowRedirects);
3115
+ expandSegment(injector, segmentGroup, routes, segments, outlet, allowRedirects) {
3116
+ return from(routes).pipe(concatMap(r => {
3117
+ const expanded$ = this.expandSegmentAgainstRoute(injector, segmentGroup, routes, r, segments, outlet, allowRedirects);
2980
3118
  return expanded$.pipe(catchError((e) => {
2981
3119
  if (e instanceof NoMatch$1) {
2982
3120
  return of(null);
@@ -2993,35 +3131,35 @@ class ApplyRedirects {
2993
3131
  throw e;
2994
3132
  }));
2995
3133
  }
2996
- expandSegmentAgainstRoute(ngModule, segmentGroup, routes, route, paths, outlet, allowRedirects) {
3134
+ expandSegmentAgainstRoute(injector, segmentGroup, routes, route, paths, outlet, allowRedirects) {
2997
3135
  if (!isImmediateMatch(route, segmentGroup, paths, outlet)) {
2998
3136
  return noMatch(segmentGroup);
2999
3137
  }
3000
3138
  if (route.redirectTo === undefined) {
3001
- return this.matchSegmentAgainstRoute(ngModule, segmentGroup, route, paths, outlet);
3139
+ return this.matchSegmentAgainstRoute(injector, segmentGroup, route, paths, outlet);
3002
3140
  }
3003
3141
  if (allowRedirects && this.allowRedirects) {
3004
- return this.expandSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, paths, outlet);
3142
+ return this.expandSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, paths, outlet);
3005
3143
  }
3006
3144
  return noMatch(segmentGroup);
3007
3145
  }
3008
- expandSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet) {
3146
+ expandSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, segments, outlet) {
3009
3147
  if (route.path === '**') {
3010
- return this.expandWildCardWithParamsAgainstRouteUsingRedirect(ngModule, routes, route, outlet);
3148
+ return this.expandWildCardWithParamsAgainstRouteUsingRedirect(injector, routes, route, outlet);
3011
3149
  }
3012
- return this.expandRegularSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet);
3150
+ return this.expandRegularSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, segments, outlet);
3013
3151
  }
3014
- expandWildCardWithParamsAgainstRouteUsingRedirect(ngModule, routes, route, outlet) {
3152
+ expandWildCardWithParamsAgainstRouteUsingRedirect(injector, routes, route, outlet) {
3015
3153
  const newTree = this.applyRedirectCommands([], route.redirectTo, {});
3016
3154
  if (route.redirectTo.startsWith('/')) {
3017
3155
  return absoluteRedirect(newTree);
3018
3156
  }
3019
3157
  return this.lineralizeSegments(route, newTree).pipe(mergeMap((newSegments) => {
3020
3158
  const group = new UrlSegmentGroup(newSegments, {});
3021
- return this.expandSegment(ngModule, group, routes, newSegments, outlet, false);
3159
+ return this.expandSegment(injector, group, routes, newSegments, outlet, false);
3022
3160
  }));
3023
3161
  }
3024
- expandRegularSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet) {
3162
+ expandRegularSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, segments, outlet) {
3025
3163
  const { matched, consumedSegments, remainingSegments, positionalParamSegments } = match(segmentGroup, route, segments);
3026
3164
  if (!matched)
3027
3165
  return noMatch(segmentGroup);
@@ -3030,16 +3168,20 @@ class ApplyRedirects {
3030
3168
  return absoluteRedirect(newTree);
3031
3169
  }
3032
3170
  return this.lineralizeSegments(route, newTree).pipe(mergeMap((newSegments) => {
3033
- return this.expandSegment(ngModule, segmentGroup, routes, newSegments.concat(remainingSegments), outlet, false);
3171
+ return this.expandSegment(injector, segmentGroup, routes, newSegments.concat(remainingSegments), outlet, false);
3034
3172
  }));
3035
3173
  }
3036
- matchSegmentAgainstRoute(ngModule, rawSegmentGroup, route, segments, outlet) {
3174
+ matchSegmentAgainstRoute(injector, rawSegmentGroup, route, segments, outlet) {
3037
3175
  if (route.path === '**') {
3176
+ // Only create the Route's `EnvironmentInjector` if it matches the attempted navigation
3177
+ injector = getOrCreateRouteInjectorIfNeeded(route, injector);
3038
3178
  if (route.loadChildren) {
3039
- const loaded$ = route._loadedConfig ? of(route._loadedConfig) :
3040
- this.configLoader.load(ngModule.injector, route);
3179
+ const loaded$ = route._loadedRoutes ?
3180
+ of({ routes: route._loadedRoutes, injector: route._loadedInjector }) :
3181
+ this.configLoader.loadChildren(injector, route);
3041
3182
  return loaded$.pipe(map((cfg) => {
3042
- route._loadedConfig = cfg;
3183
+ route._loadedRoutes = cfg.routes;
3184
+ route._loadedInjector = cfg.injector;
3043
3185
  return new UrlSegmentGroup(segments, {});
3044
3186
  }));
3045
3187
  }
@@ -3048,55 +3190,57 @@ class ApplyRedirects {
3048
3190
  const { matched, consumedSegments, remainingSegments } = match(rawSegmentGroup, route, segments);
3049
3191
  if (!matched)
3050
3192
  return noMatch(rawSegmentGroup);
3051
- const childConfig$ = this.getChildConfig(ngModule, route, segments);
3193
+ // Only create the Route's `EnvironmentInjector` if it matches the attempted navigation
3194
+ injector = getOrCreateRouteInjectorIfNeeded(route, injector);
3195
+ const childConfig$ = this.getChildConfig(injector, route, segments);
3052
3196
  return childConfig$.pipe(mergeMap((routerConfig) => {
3053
- const childModule = routerConfig.module;
3197
+ const childInjector = routerConfig.injector ?? injector;
3054
3198
  const childConfig = routerConfig.routes;
3055
3199
  const { segmentGroup: splitSegmentGroup, slicedSegments } = split(rawSegmentGroup, consumedSegments, remainingSegments, childConfig);
3056
3200
  // See comment on the other call to `split` about why this is necessary.
3057
3201
  const segmentGroup = new UrlSegmentGroup(splitSegmentGroup.segments, splitSegmentGroup.children);
3058
3202
  if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
3059
- const expanded$ = this.expandChildren(childModule, childConfig, segmentGroup);
3203
+ const expanded$ = this.expandChildren(childInjector, childConfig, segmentGroup);
3060
3204
  return expanded$.pipe(map((children) => new UrlSegmentGroup(consumedSegments, children)));
3061
3205
  }
3062
3206
  if (childConfig.length === 0 && slicedSegments.length === 0) {
3063
3207
  return of(new UrlSegmentGroup(consumedSegments, {}));
3064
3208
  }
3065
3209
  const matchedOnOutlet = getOutlet(route) === outlet;
3066
- const expanded$ = this.expandSegment(childModule, segmentGroup, childConfig, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet, true);
3210
+ const expanded$ = this.expandSegment(childInjector, segmentGroup, childConfig, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet, true);
3067
3211
  return expanded$.pipe(map((cs) => new UrlSegmentGroup(consumedSegments.concat(cs.segments), cs.children)));
3068
3212
  }));
3069
3213
  }
3070
- getChildConfig(ngModule, route, segments) {
3214
+ getChildConfig(injector, route, segments) {
3071
3215
  if (route.children) {
3072
3216
  // The children belong to the same module
3073
- return of(new LoadedRouterConfig(route.children, ngModule));
3217
+ return of({ routes: route.children, injector });
3074
3218
  }
3075
3219
  if (route.loadChildren) {
3076
3220
  // lazy children belong to the loaded module
3077
- if (route._loadedConfig !== undefined) {
3078
- return of(route._loadedConfig);
3221
+ if (route._loadedRoutes !== undefined) {
3222
+ return of({ routes: route._loadedRoutes, injector: route._loadedInjector });
3079
3223
  }
3080
- return this.runCanLoadGuards(ngModule.injector, route, segments)
3224
+ return this.runCanLoadGuards(injector, route, segments)
3081
3225
  .pipe(mergeMap((shouldLoadResult) => {
3082
3226
  if (shouldLoadResult) {
3083
- return this.configLoader.load(ngModule.injector, route)
3084
- .pipe(map((cfg) => {
3085
- route._loadedConfig = cfg;
3086
- return cfg;
3227
+ return this.configLoader.loadChildren(injector, route)
3228
+ .pipe(tap((cfg) => {
3229
+ route._loadedRoutes = cfg.routes;
3230
+ route._loadedInjector = cfg.injector;
3087
3231
  }));
3088
3232
  }
3089
3233
  return canLoadFails(route);
3090
3234
  }));
3091
3235
  }
3092
- return of(new LoadedRouterConfig([], ngModule));
3236
+ return of({ routes: [], injector });
3093
3237
  }
3094
- runCanLoadGuards(moduleInjector, route, segments) {
3238
+ runCanLoadGuards(injector, route, segments) {
3095
3239
  const canLoad = route.canLoad;
3096
3240
  if (!canLoad || canLoad.length === 0)
3097
3241
  return of(true);
3098
3242
  const canLoadObservables = canLoad.map((injectionToken) => {
3099
- const guard = moduleInjector.get(injectionToken);
3243
+ const guard = injector.get(injectionToken);
3100
3244
  let guardVal;
3101
3245
  if (isCanLoad(guard)) {
3102
3246
  guardVal = guard.canLoad(route, segments);
@@ -3224,8 +3368,8 @@ function squashSegmentGroup(segmentGroup) {
3224
3368
  * Use of this source code is governed by an MIT-style license that can be
3225
3369
  * found in the LICENSE file at https://angular.io/license
3226
3370
  */
3227
- function applyRedirects(moduleInjector, configLoader, urlSerializer, config) {
3228
- return switchMap(t => applyRedirects$1(moduleInjector, configLoader, urlSerializer, t.extractedUrl, config)
3371
+ function applyRedirects(environmentInjector, configLoader, urlSerializer, config) {
3372
+ return switchMap(t => applyRedirects$1(environmentInjector, configLoader, urlSerializer, t.extractedUrl, config)
3229
3373
  .pipe(map(urlAfterRedirects => ({ ...t, urlAfterRedirects }))));
3230
3374
  }
3231
3375
 
@@ -3259,21 +3403,11 @@ function getCanActivateChild(p) {
3259
3403
  return null;
3260
3404
  return { node: p, guards: canActivateChild };
3261
3405
  }
3262
- function getToken(token, snapshot, moduleInjector) {
3263
- const config = getClosestLoadedConfig(snapshot);
3264
- const injector = config ? config.module.injector : moduleInjector;
3406
+ function getToken(token, snapshot, fallbackInjector) {
3407
+ const routeInjector = getClosestRouteInjector(snapshot);
3408
+ const injector = routeInjector ?? fallbackInjector;
3265
3409
  return injector.get(token);
3266
3410
  }
3267
- function getClosestLoadedConfig(snapshot) {
3268
- if (!snapshot)
3269
- return null;
3270
- for (let s = snapshot.parent; s; s = s.parent) {
3271
- const route = s.routeConfig;
3272
- if (route && route._loadedConfig)
3273
- return route._loadedConfig;
3274
- }
3275
- return null;
3276
- }
3277
3411
  function getChildRouteGuards(futureNode, currNode, contexts, futurePath, checks = {
3278
3412
  canDeactivateChecks: [],
3279
3413
  canActivateChecks: []
@@ -3517,6 +3651,7 @@ function runCanDeactivate(component, currARS, currRSS, futureRSS, moduleInjector
3517
3651
  * Use of this source code is governed by an MIT-style license that can be
3518
3652
  * found in the LICENSE file at https://angular.io/license
3519
3653
  */
3654
+ const NG_DEV_MODE$2 = typeof ngDevMode === 'undefined' || !!ngDevMode;
3520
3655
  class NoMatch {
3521
3656
  }
3522
3657
  function newObservableError(e) {
@@ -3632,7 +3767,13 @@ class Recognizer {
3632
3767
  let remainingSegments = [];
3633
3768
  if (route.path === '**') {
3634
3769
  const params = segments.length > 0 ? last(segments).parameters : {};
3635
- snapshot = new ActivatedRouteSnapshot(segments, params, Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, getData(route), getOutlet(route), route.component, route, getSourceSegmentGroup(rawSegment), getPathIndexShift(rawSegment) + segments.length, getResolve(route));
3770
+ const pathIndexShift = getPathIndexShift(rawSegment) + segments.length;
3771
+ snapshot = new ActivatedRouteSnapshot(segments, params, Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, getData(route), getOutlet(route), route.component ?? route._loadedComponent ?? null, route, getSourceSegmentGroup(rawSegment), pathIndexShift, getResolve(route),
3772
+ // NG_DEV_MODE is used to prevent the getCorrectedPathIndexShift function from affecting
3773
+ // production bundle size. This value is intended only to surface a warning to users
3774
+ // depending on `relativeLinkResolution: 'legacy'` in dev mode.
3775
+ (NG_DEV_MODE$2 ? getCorrectedPathIndexShift(rawSegment) + segments.length :
3776
+ pathIndexShift));
3636
3777
  }
3637
3778
  else {
3638
3779
  const result = match(rawSegment, route, segments);
@@ -3641,7 +3782,9 @@ class Recognizer {
3641
3782
  }
3642
3783
  consumedSegments = result.consumedSegments;
3643
3784
  remainingSegments = result.remainingSegments;
3644
- snapshot = new ActivatedRouteSnapshot(consumedSegments, result.parameters, Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, getData(route), getOutlet(route), route.component, route, getSourceSegmentGroup(rawSegment), getPathIndexShift(rawSegment) + consumedSegments.length, getResolve(route));
3785
+ const pathIndexShift = getPathIndexShift(rawSegment) + consumedSegments.length;
3786
+ snapshot = new ActivatedRouteSnapshot(consumedSegments, result.parameters, Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, getData(route), getOutlet(route), route.component ?? route._loadedComponent ?? null, route, getSourceSegmentGroup(rawSegment), pathIndexShift, getResolve(route), (NG_DEV_MODE$2 ? getCorrectedPathIndexShift(rawSegment) + consumedSegments.length :
3787
+ pathIndexShift));
3645
3788
  }
3646
3789
  const childConfig = getChildConfig(route);
3647
3790
  const { segmentGroup, slicedSegments } = split(rawSegment, consumedSegments, remainingSegments,
@@ -3689,7 +3832,7 @@ function getChildConfig(route) {
3689
3832
  return route.children;
3690
3833
  }
3691
3834
  if (route.loadChildren) {
3692
- return route._loadedConfig.routes;
3835
+ return route._loadedRoutes;
3693
3836
  }
3694
3837
  return [];
3695
3838
  }
@@ -3751,10 +3894,19 @@ function getSourceSegmentGroup(segmentGroup) {
3751
3894
  }
3752
3895
  function getPathIndexShift(segmentGroup) {
3753
3896
  let s = segmentGroup;
3754
- let res = (s._segmentIndexShift ? s._segmentIndexShift : 0);
3897
+ let res = s._segmentIndexShift ?? 0;
3898
+ while (s._sourceSegment) {
3899
+ s = s._sourceSegment;
3900
+ res += s._segmentIndexShift ?? 0;
3901
+ }
3902
+ return res - 1;
3903
+ }
3904
+ function getCorrectedPathIndexShift(segmentGroup) {
3905
+ let s = segmentGroup;
3906
+ let res = s._segmentIndexShiftCorrected ?? s._segmentIndexShift ?? 0;
3755
3907
  while (s._sourceSegment) {
3756
3908
  s = s._sourceSegment;
3757
- res += (s._segmentIndexShift ? s._segmentIndexShift : 0);
3909
+ res += s._segmentIndexShiftCorrected ?? s._segmentIndexShift ?? 0;
3758
3910
  }
3759
3911
  return res - 1;
3760
3912
  }
@@ -3804,22 +3956,16 @@ function resolveData(paramsInheritanceStrategy, moduleInjector) {
3804
3956
  function runResolve(futureARS, futureRSS, paramsInheritanceStrategy, moduleInjector) {
3805
3957
  const config = futureARS.routeConfig;
3806
3958
  const resolve = futureARS._resolve;
3807
- const data = { ...futureARS.data };
3808
- if (config?.title !== undefined) {
3809
- if (typeof config.title === 'string' || config.title === null) {
3810
- data[RouteTitle] = config.title;
3811
- }
3812
- else {
3813
- resolve[RouteTitle] = config.title;
3814
- }
3959
+ if (config?.title !== undefined && !hasStaticTitle(config)) {
3960
+ resolve[RouteTitle] = config.title;
3815
3961
  }
3816
3962
  return resolveNode(resolve, futureARS, futureRSS, moduleInjector)
3817
3963
  .pipe(map((resolvedData) => {
3818
3964
  futureARS._resolvedData = resolvedData;
3819
- futureARS.data = {
3820
- ...data,
3821
- ...inheritedParamsDataResolve(futureARS, paramsInheritanceStrategy).resolve
3822
- };
3965
+ futureARS.data = inheritedParamsDataResolve(futureARS, paramsInheritanceStrategy).resolve;
3966
+ if (config && hasStaticTitle(config)) {
3967
+ futureARS.data[RouteTitle] = config.title;
3968
+ }
3823
3969
  return null;
3824
3970
  }));
3825
3971
  }
@@ -3830,16 +3976,9 @@ function resolveNode(resolve, futureARS, futureRSS, moduleInjector) {
3830
3976
  }
3831
3977
  const data = {};
3832
3978
  return from(keys).pipe(mergeMap(key => getResolver(resolve[key], futureARS, futureRSS, moduleInjector)
3833
- .pipe(take(1), tap((value) => {
3979
+ .pipe(first(), tap((value) => {
3834
3980
  data[key] = value;
3835
- }))), takeLast(1), mergeMap(() => {
3836
- // Ensure all resolvers returned values, otherwise don't emit any "next" and just complete
3837
- // the chain which will cancel navigation
3838
- if (getDataKeys(data).length === keys.length) {
3839
- return of(data);
3840
- }
3841
- return EMPTY;
3842
- }));
3981
+ }))), takeLast(1), mapTo(data), catchError((e) => e instanceof EmptyError ? EMPTY : throwError(e)));
3843
3982
  }
3844
3983
  function getDataKeys(obj) {
3845
3984
  return [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)];
@@ -3849,6 +3988,9 @@ function getResolver(injectionToken, futureARS, futureRSS, moduleInjector) {
3849
3988
  return resolver.resolve ? wrapIntoObservable(resolver.resolve(futureARS, futureRSS)) :
3850
3989
  wrapIntoObservable(resolver(futureARS, futureRSS));
3851
3990
  }
3991
+ function hasStaticTitle(config) {
3992
+ return typeof config.title === 'string' || config.title === null;
3993
+ }
3852
3994
 
3853
3995
  /**
3854
3996
  * @license
@@ -3945,6 +4087,7 @@ class DefaultRouteReuseStrategy extends BaseRouteReuseStrategy {
3945
4087
  * Use of this source code is governed by an MIT-style license that can be
3946
4088
  * found in the LICENSE file at https://angular.io/license
3947
4089
  */
4090
+ const NG_DEV_MODE$1 = typeof ngDevMode === 'undefined' || !!ngDevMode;
3948
4091
  /**
3949
4092
  * The [DI token](guide/glossary/#di-token) for a router configuration.
3950
4093
  *
@@ -3957,43 +4100,84 @@ class DefaultRouteReuseStrategy extends BaseRouteReuseStrategy {
3957
4100
  */
3958
4101
  const ROUTES = new InjectionToken('ROUTES');
3959
4102
  class RouterConfigLoader {
3960
- constructor(injector, compiler, onLoadStartListener, onLoadEndListener) {
4103
+ constructor(injector, compiler) {
3961
4104
  this.injector = injector;
3962
4105
  this.compiler = compiler;
3963
- this.onLoadStartListener = onLoadStartListener;
3964
- this.onLoadEndListener = onLoadEndListener;
4106
+ this.componentLoaders = new WeakMap();
4107
+ this.childrenLoaders = new WeakMap();
4108
+ }
4109
+ loadComponent(route) {
4110
+ if (this.componentLoaders.get(route)) {
4111
+ return this.componentLoaders.get(route);
4112
+ }
4113
+ else if (route._loadedComponent) {
4114
+ return of(route._loadedComponent);
4115
+ }
4116
+ if (this.onLoadStartListener) {
4117
+ this.onLoadStartListener(route);
4118
+ }
4119
+ const loadRunner = wrapIntoObservable(route.loadComponent())
4120
+ .pipe(tap(component => {
4121
+ if (this.onLoadEndListener) {
4122
+ this.onLoadEndListener(route);
4123
+ }
4124
+ NG_DEV_MODE$1 && assertStandalone(route.path ?? '', component);
4125
+ route._loadedComponent = component;
4126
+ }), finalize(() => {
4127
+ this.componentLoaders.delete(route);
4128
+ }));
4129
+ // Use custom ConnectableObservable as share in runners pipe increasing the bundle size too much
4130
+ const loader = new ConnectableObservable(loadRunner, () => new Subject()).pipe(refCount());
4131
+ this.componentLoaders.set(route, loader);
4132
+ return loader;
3965
4133
  }
3966
- load(parentInjector, route) {
3967
- if (route._loader$) {
3968
- return route._loader$;
4134
+ loadChildren(parentInjector, route) {
4135
+ if (this.childrenLoaders.get(route)) {
4136
+ return this.childrenLoaders.get(route);
4137
+ }
4138
+ else if (route._loadedRoutes) {
4139
+ return of({ routes: route._loadedRoutes, injector: route._loadedInjector });
3969
4140
  }
3970
4141
  if (this.onLoadStartListener) {
3971
4142
  this.onLoadStartListener(route);
3972
4143
  }
3973
- const moduleFactory$ = this.loadModuleFactory(route.loadChildren);
3974
- const loadRunner = moduleFactory$.pipe(map((factory) => {
4144
+ const moduleFactoryOrRoutes$ = this.loadModuleFactoryOrRoutes(route.loadChildren);
4145
+ const loadRunner = moduleFactoryOrRoutes$.pipe(map((factoryOrRoutes) => {
3975
4146
  if (this.onLoadEndListener) {
3976
4147
  this.onLoadEndListener(route);
3977
4148
  }
3978
- const module = factory.create(parentInjector);
3979
- // When loading a module that doesn't provide `RouterModule.forChild()` preloader
3980
- // will get stuck in an infinite loop. The child module's Injector will look to
3981
- // its parent `Injector` when it doesn't find any ROUTES so it will return routes
3982
- // for it's parent module instead.
3983
- return new LoadedRouterConfig(flatten(module.injector.get(ROUTES, undefined, InjectFlags.Self | InjectFlags.Optional))
3984
- .map(standardizeConfig), module);
3985
- }), catchError((err) => {
3986
- route._loader$ = undefined;
3987
- throw err;
4149
+ // This injector comes from the `NgModuleRef` when lazy loading an `NgModule`. There is no
4150
+ // injector associated with lazy loading a `Route` array.
4151
+ let injector;
4152
+ let rawRoutes;
4153
+ let requireStandaloneComponents = false;
4154
+ if (Array.isArray(factoryOrRoutes)) {
4155
+ rawRoutes = factoryOrRoutes;
4156
+ requireStandaloneComponents = true;
4157
+ }
4158
+ else {
4159
+ injector = factoryOrRoutes.create(parentInjector).injector;
4160
+ // When loading a module that doesn't provide `RouterModule.forChild()` preloader
4161
+ // will get stuck in an infinite loop. The child module's Injector will look to
4162
+ // its parent `Injector` when it doesn't find any ROUTES so it will return routes
4163
+ // for it's parent module instead.
4164
+ rawRoutes = flatten(injector.get(ROUTES, [], InjectFlags.Self | InjectFlags.Optional));
4165
+ }
4166
+ const routes = rawRoutes.map(standardizeConfig);
4167
+ NG_DEV_MODE$1 && validateConfig(routes, route.path, requireStandaloneComponents);
4168
+ return { routes, injector };
4169
+ }), finalize(() => {
4170
+ this.childrenLoaders.delete(route);
3988
4171
  }));
3989
4172
  // Use custom ConnectableObservable as share in runners pipe increasing the bundle size too much
3990
- route._loader$ = new ConnectableObservable(loadRunner, () => new Subject())
4173
+ const loader = new ConnectableObservable(loadRunner, () => new Subject())
3991
4174
  .pipe(refCount());
3992
- return route._loader$;
4175
+ this.childrenLoaders.set(route, loader);
4176
+ return loader;
3993
4177
  }
3994
- loadModuleFactory(loadChildren) {
4178
+ loadModuleFactoryOrRoutes(loadChildren) {
3995
4179
  return wrapIntoObservable(loadChildren()).pipe(mergeMap((t) => {
3996
- if (t instanceof NgModuleFactory) {
4180
+ if (t instanceof NgModuleFactory || Array.isArray(t)) {
3997
4181
  return of(t);
3998
4182
  }
3999
4183
  else {
@@ -4002,6 +4186,11 @@ class RouterConfigLoader {
4002
4186
  }));
4003
4187
  }
4004
4188
  }
4189
+ RouterConfigLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterConfigLoader, deps: [{ token: i0.Injector }, { token: i0.Compiler }], target: i0.ɵɵFactoryTarget.Injectable });
4190
+ RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterConfigLoader });
4191
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterConfigLoader, decorators: [{
4192
+ type: Injectable
4193
+ }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.Compiler }]; } });
4005
4194
 
4006
4195
  /**
4007
4196
  * @license
@@ -4041,6 +4230,7 @@ class DefaultUrlHandlingStrategy {
4041
4230
  * Use of this source code is governed by an MIT-style license that can be
4042
4231
  * found in the LICENSE file at https://angular.io/license
4043
4232
  */
4233
+ const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
4044
4234
  function defaultErrorHandler(error) {
4045
4235
  throw error;
4046
4236
  }
@@ -4183,6 +4373,8 @@ class Router {
4183
4373
  /**
4184
4374
  * Enables a bug fix that corrects relative link resolution in components with empty paths.
4185
4375
  * @see `RouterModule`
4376
+ *
4377
+ * @deprecated
4186
4378
  */
4187
4379
  this.relativeLinkResolution = 'corrected';
4188
4380
  /**
@@ -4210,6 +4402,9 @@ class Router {
4210
4402
  this.canceledNavigationResolution = 'replace';
4211
4403
  const onLoadStart = (r) => this.triggerEvent(new RouteConfigLoadStart(r));
4212
4404
  const onLoadEnd = (r) => this.triggerEvent(new RouteConfigLoadEnd(r));
4405
+ this.configLoader = injector.get(RouterConfigLoader);
4406
+ this.configLoader.onLoadEndListener = onLoadEnd;
4407
+ this.configLoader.onLoadStartListener = onLoadStart;
4213
4408
  this.ngModule = injector.get(NgModuleRef);
4214
4409
  this.console = injector.get(ɵConsole);
4215
4410
  const ngZone = injector.get(NgZone);
@@ -4218,7 +4413,6 @@ class Router {
4218
4413
  this.currentUrlTree = createEmptyUrlTree();
4219
4414
  this.rawUrlTree = this.currentUrlTree;
4220
4415
  this.browserUrlTree = this.currentUrlTree;
4221
- this.configLoader = new RouterConfigLoader(injector, compiler, onLoadStart, onLoadEnd);
4222
4416
  this.routerState = createEmptyState(this.currentUrlTree, this.rootComponentType);
4223
4417
  this.transitions = new BehaviorSubject({
4224
4418
  id: 0,
@@ -4427,6 +4621,25 @@ class Router {
4427
4621
  skipLocationChange: !!skipLocationChange,
4428
4622
  replaceUrl: !!replaceUrl,
4429
4623
  });
4624
+ }),
4625
+ // --- LOAD COMPONENTS ---
4626
+ switchTap((t) => {
4627
+ const loadComponents = (route) => {
4628
+ const loaders = [];
4629
+ if (route.routeConfig?.loadComponent &&
4630
+ !route.routeConfig._loadedComponent) {
4631
+ loaders.push(this.configLoader.loadComponent(route.routeConfig)
4632
+ .pipe(tap(loadedComponent => {
4633
+ route.component = loadedComponent;
4634
+ }), map(() => void 0)));
4635
+ }
4636
+ for (const child of route.children) {
4637
+ loaders.push(...loadComponents(child));
4638
+ }
4639
+ return loaders;
4640
+ };
4641
+ return combineLatest(loadComponents(t.targetSnapshot.root))
4642
+ .pipe(defaultIfEmpty(), take(1));
4430
4643
  }), map((t) => {
4431
4644
  const targetRouterState = createRouterState(this.routeReuseStrategy, t.targetSnapshot, t.currentRouterState);
4432
4645
  return ({ ...t, targetRouterState });
@@ -4505,23 +4718,17 @@ class Router {
4505
4718
  t.resolve(false);
4506
4719
  }
4507
4720
  else {
4508
- // setTimeout is required so this navigation finishes with
4509
- // the return EMPTY below. If it isn't allowed to finish
4510
- // processing, there can be multiple navigations to the same
4511
- // URL.
4512
- setTimeout(() => {
4513
- const mergedTree = this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
4514
- const extras = {
4515
- skipLocationChange: t.extras.skipLocationChange,
4516
- // The URL is already updated at this point if we have 'eager' URL
4517
- // updates or if the navigation was triggered by the browser (back
4518
- // button, URL bar, etc). We want to replace that item in history if
4519
- // the navigation is rejected.
4520
- replaceUrl: this.urlUpdateStrategy === 'eager' ||
4521
- isBrowserTriggeredNavigation(t.source)
4522
- };
4523
- this.scheduleNavigation(mergedTree, 'imperative', null, extras, { resolve: t.resolve, reject: t.reject, promise: t.promise });
4524
- }, 0);
4721
+ const mergedTree = this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
4722
+ const extras = {
4723
+ skipLocationChange: t.extras.skipLocationChange,
4724
+ // The URL is already updated at this point if we have 'eager' URL
4725
+ // updates or if the navigation was triggered by the browser (back
4726
+ // button, URL bar, etc). We want to replace that item in history if
4727
+ // the navigation is rejected.
4728
+ replaceUrl: this.urlUpdateStrategy === 'eager' ||
4729
+ isBrowserTriggeredNavigation(t.source)
4730
+ };
4731
+ this.scheduleNavigation(mergedTree, 'imperative', null, extras, { resolve: t.resolve, reject: t.reject, promise: t.promise });
4525
4732
  }
4526
4733
  /* All other errors should reset to the router's internal URL reference to
4527
4734
  * the pre-error state. */
@@ -4631,7 +4838,7 @@ class Router {
4631
4838
  * ```
4632
4839
  */
4633
4840
  resetConfig(config) {
4634
- validateConfig(config);
4841
+ NG_DEV_MODE && validateConfig(config);
4635
4842
  this.config = config.map(standardizeConfig);
4636
4843
  this.navigated = false;
4637
4844
  this.lastSuccessfulId = -1;
@@ -4986,9 +5193,9 @@ class Router {
4986
5193
  return { navigationId };
4987
5194
  }
4988
5195
  }
4989
- Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: Router, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
4990
- Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: Router });
4991
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: Router, decorators: [{
5196
+ Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: Router, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
5197
+ Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: Router });
5198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: Router, decorators: [{
4992
5199
  type: Injectable
4993
5200
  }], ctorParameters: function () { return [{ type: i0.Type }, { type: UrlSerializer }, { type: ChildrenOutletContexts }, { type: i3.Location }, { type: i0.Injector }, { type: i0.Compiler }, { type: undefined }]; } });
4994
5201
  function validateCommands(commands) {
@@ -5187,9 +5394,9 @@ class RouterLink {
5187
5394
  });
5188
5395
  }
5189
5396
  }
5190
- RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterLink, deps: [{ token: Router }, { token: ActivatedRoute }, { token: 'tabindex', attribute: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
5191
- RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", 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 });
5192
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterLink, decorators: [{
5397
+ RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterLink, deps: [{ token: Router }, { token: ActivatedRoute }, { token: 'tabindex', attribute: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
5398
+ RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.0-rc.2", 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 });
5399
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterLink, decorators: [{
5193
5400
  type: Directive,
5194
5401
  args: [{ selector: ':not(a):not(area)[routerLink]' }]
5195
5402
  }], ctorParameters: function () { return [{ type: Router }, { type: ActivatedRoute }, { type: undefined, decorators: [{
@@ -5306,9 +5513,9 @@ class RouterLinkWithHref {
5306
5513
  });
5307
5514
  }
5308
5515
  }
5309
- RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterLinkWithHref, deps: [{ token: Router }, { token: ActivatedRoute }, { token: i3.LocationStrategy }], target: i0.ɵɵFactoryTarget.Directive });
5310
- RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", 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 });
5311
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterLinkWithHref, decorators: [{
5516
+ RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterLinkWithHref, deps: [{ token: Router }, { token: ActivatedRoute }, { token: i3.LocationStrategy }], target: i0.ɵɵFactoryTarget.Directive });
5517
+ RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.0-rc.2", 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 });
5518
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterLinkWithHref, decorators: [{
5312
5519
  type: Directive,
5313
5520
  args: [{ selector: 'a[routerLink],area[routerLink]' }]
5314
5521
  }], ctorParameters: function () { return [{ type: Router }, { type: ActivatedRoute }, { type: i3.LocationStrategy }]; }, propDecorators: { target: [{
@@ -5405,6 +5612,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9",
5405
5612
  * </div>
5406
5613
  * ```
5407
5614
  *
5615
+ * The `RouterLinkActive` directive can also be used to set the aria-current attribute
5616
+ * to provide an alternative distinction for active elements to visually impaired users.
5617
+ *
5618
+ * For example, the following code adds the 'active' class to the Home Page link when it is
5619
+ * indeed active and in such case also sets its aria-current attribute to 'page':
5620
+ *
5621
+ * ```
5622
+ * <a routerLink="/" routerLinkActive="active" ariaCurrentWhenActive="page">Home Page</a>
5623
+ * ```
5624
+ *
5408
5625
  * @ngModule RouterModule
5409
5626
  *
5410
5627
  * @publicApi
@@ -5498,6 +5715,12 @@ class RouterLinkActive {
5498
5715
  this.renderer.removeClass(this.element.nativeElement, c);
5499
5716
  }
5500
5717
  });
5718
+ if (hasActiveLinks && this.ariaCurrentWhenActive !== undefined) {
5719
+ this.renderer.setAttribute(this.element.nativeElement, 'aria-current', this.ariaCurrentWhenActive.toString());
5720
+ }
5721
+ else {
5722
+ this.renderer.removeAttribute(this.element.nativeElement, 'aria-current');
5723
+ }
5501
5724
  // Emit on isActiveChange after classes are updated
5502
5725
  this.isActiveChange.emit(hasActiveLinks);
5503
5726
  }
@@ -5517,9 +5740,9 @@ class RouterLinkActive {
5517
5740
  this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
5518
5741
  }
5519
5742
  }
5520
- RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", 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 });
5521
- RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: { routerLinkActiveOptions: "routerLinkActiveOptions", routerLinkActive: "routerLinkActive" }, outputs: { isActiveChange: "isActiveChange" }, queries: [{ propertyName: "links", predicate: RouterLink, descendants: true }, { propertyName: "linksWithHrefs", predicate: RouterLinkWithHref, descendants: true }], exportAs: ["routerLinkActive"], usesOnChanges: true, ngImport: i0 });
5522
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterLinkActive, decorators: [{
5743
+ RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", 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 });
5744
+ RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.0-rc.2", 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 });
5745
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterLinkActive, decorators: [{
5523
5746
  type: Directive,
5524
5747
  args: [{
5525
5748
  selector: '[routerLinkActive]',
@@ -5537,6 +5760,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9",
5537
5760
  args: [RouterLinkWithHref, { descendants: true }]
5538
5761
  }], routerLinkActiveOptions: [{
5539
5762
  type: Input
5763
+ }], ariaCurrentWhenActive: [{
5764
+ type: Input
5540
5765
  }], isActiveChange: [{
5541
5766
  type: Output
5542
5767
  }], routerLinkActive: [{
@@ -5620,9 +5845,9 @@ class DefaultTitleStrategy extends TitleStrategy {
5620
5845
  }
5621
5846
  }
5622
5847
  }
5623
- DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: DefaultTitleStrategy, deps: [{ token: i1.Title }], target: i0.ɵɵFactoryTarget.Injectable });
5624
- DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: DefaultTitleStrategy, providedIn: 'root' });
5625
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: DefaultTitleStrategy, decorators: [{
5848
+ DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: DefaultTitleStrategy, deps: [{ token: i1.Title }], target: i0.ɵɵFactoryTarget.Injectable });
5849
+ DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: DefaultTitleStrategy, providedIn: 'root' });
5850
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: DefaultTitleStrategy, decorators: [{
5626
5851
  type: Injectable,
5627
5852
  args: [{ providedIn: 'root' }]
5628
5853
  }], ctorParameters: function () { return [{ type: i1.Title }]; } });
@@ -5686,13 +5911,11 @@ class NoPreloading {
5686
5911
  * @publicApi
5687
5912
  */
5688
5913
  class RouterPreloader {
5689
- constructor(router, compiler, injector, preloadingStrategy) {
5914
+ constructor(router, compiler, injector, preloadingStrategy, loader) {
5690
5915
  this.router = router;
5691
5916
  this.injector = injector;
5692
5917
  this.preloadingStrategy = preloadingStrategy;
5693
- const onStartLoad = (r) => router.triggerEvent(new RouteConfigLoadStart(r));
5694
- const onEndLoad = (r) => router.triggerEvent(new RouteConfigLoadEnd(r));
5695
- this.loader = new RouterConfigLoader(injector, compiler, onStartLoad, onEndLoad);
5918
+ this.loader = loader;
5696
5919
  }
5697
5920
  setUpPreloading() {
5698
5921
  this.subscription =
@@ -5701,8 +5924,7 @@ class RouterPreloader {
5701
5924
  .subscribe(() => { });
5702
5925
  }
5703
5926
  preload() {
5704
- const ngModule = this.injector.get(NgModuleRef);
5705
- return this.processRoutes(ngModule, this.router.config);
5927
+ return this.processRoutes(this.injector, this.router.config);
5706
5928
  }
5707
5929
  /** @nodoc */
5708
5930
  ngOnDestroy() {
@@ -5710,41 +5932,59 @@ class RouterPreloader {
5710
5932
  this.subscription.unsubscribe();
5711
5933
  }
5712
5934
  }
5713
- processRoutes(ngModule, routes) {
5935
+ processRoutes(injector, routes) {
5714
5936
  const res = [];
5715
5937
  for (const route of routes) {
5716
- // we already have the config loaded, just recurse
5717
- if (route.loadChildren && !route.canLoad && route._loadedConfig) {
5718
- const childConfig = route._loadedConfig;
5719
- res.push(this.processRoutes(childConfig.module, childConfig.routes));
5720
- // no config loaded, fetch the config
5938
+ if (route.providers && !route._injector) {
5939
+ route._injector =
5940
+ createEnvironmentInjector(route.providers, injector, `Route: ${route.path}`);
5721
5941
  }
5722
- else if (route.loadChildren && !route.canLoad) {
5723
- res.push(this.preloadConfig(ngModule, route));
5724
- // recurse into children
5942
+ const injectorForCurrentRoute = route._injector ?? injector;
5943
+ const injectorForChildren = route._loadedInjector ?? injectorForCurrentRoute;
5944
+ if ((route.loadChildren && !route._loadedRoutes) ||
5945
+ (route.loadComponent && !route._loadedComponent)) {
5946
+ res.push(this.preloadConfig(injectorForCurrentRoute, route));
5725
5947
  }
5726
- else if (route.children) {
5727
- res.push(this.processRoutes(ngModule, route.children));
5948
+ else if (route.children || route._loadedRoutes) {
5949
+ res.push(this.processRoutes(injectorForChildren, (route.children ?? route._loadedRoutes)));
5728
5950
  }
5729
5951
  }
5730
- return from(res).pipe(mergeAll(), map((_) => void 0));
5952
+ return from(res).pipe(mergeAll());
5731
5953
  }
5732
- preloadConfig(ngModule, route) {
5954
+ preloadConfig(injector, route) {
5733
5955
  return this.preloadingStrategy.preload(route, () => {
5734
- const loaded$ = route._loadedConfig ? of(route._loadedConfig) :
5735
- this.loader.load(ngModule.injector, route);
5736
- return loaded$.pipe(mergeMap((config) => {
5737
- route._loadedConfig = config;
5738
- return this.processRoutes(config.module, config.routes);
5956
+ let loadedChildren$;
5957
+ if (route.loadChildren && route.canLoad === undefined) {
5958
+ loadedChildren$ = this.loader.loadChildren(injector, route);
5959
+ }
5960
+ else {
5961
+ loadedChildren$ = of(null);
5962
+ }
5963
+ const recursiveLoadChildren$ = loadedChildren$.pipe(mergeMap((config) => {
5964
+ if (config === null) {
5965
+ return of(void 0);
5966
+ }
5967
+ route._loadedRoutes = config.routes;
5968
+ route._loadedInjector = config.injector;
5969
+ // If the loaded config was a module, use that as the module/module injector going
5970
+ // forward. Otherwise, continue using the current module/module injector.
5971
+ return this.processRoutes(config.injector ?? injector, config.routes);
5739
5972
  }));
5973
+ if (route.loadComponent && !route._loadedComponent) {
5974
+ const loadComponent$ = this.loader.loadComponent(route);
5975
+ return from([recursiveLoadChildren$, loadComponent$]).pipe(mergeAll());
5976
+ }
5977
+ else {
5978
+ return recursiveLoadChildren$;
5979
+ }
5740
5980
  });
5741
5981
  }
5742
5982
  }
5743
- RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterPreloader, deps: [{ token: Router }, { token: i0.Compiler }, { token: i0.Injector }, { token: PreloadingStrategy }], target: i0.ɵɵFactoryTarget.Injectable });
5744
- RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterPreloader });
5745
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterPreloader, decorators: [{
5983
+ RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterPreloader, deps: [{ token: Router }, { token: i0.Compiler }, { token: i0.EnvironmentInjector }, { token: PreloadingStrategy }, { token: RouterConfigLoader }], target: i0.ɵɵFactoryTarget.Injectable });
5984
+ RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterPreloader });
5985
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterPreloader, decorators: [{
5746
5986
  type: Injectable
5747
- }], ctorParameters: function () { return [{ type: Router }, { type: i0.Compiler }, { type: i0.Injector }, { type: PreloadingStrategy }]; } });
5987
+ }], ctorParameters: function () { return [{ type: Router }, { type: i0.Compiler }, { type: i0.EnvironmentInjector }, { type: PreloadingStrategy }, { type: RouterConfigLoader }]; } });
5748
5988
 
5749
5989
  /**
5750
5990
  * @license
@@ -5828,9 +6068,9 @@ class RouterScroller {
5828
6068
  }
5829
6069
  }
5830
6070
  }
5831
- RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterScroller, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
5832
- RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterScroller });
5833
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterScroller, decorators: [{
6071
+ RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterScroller, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
6072
+ RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterScroller });
6073
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterScroller, decorators: [{
5834
6074
  type: Injectable
5835
6075
  }], ctorParameters: function () { return [{ type: Router }, { type: i3.ViewportScroller }, { type: undefined }]; } });
5836
6076
 
@@ -5873,6 +6113,7 @@ const ROUTER_PROVIDERS = [
5873
6113
  NoPreloading,
5874
6114
  PreloadAllModules,
5875
6115
  { provide: ROUTER_CONFIGURATION, useValue: { enableTracing: false } },
6116
+ RouterConfigLoader,
5876
6117
  ];
5877
6118
  function routerNgProbeToken() {
5878
6119
  return new NgProbeToken('Router', Router);
@@ -5971,10 +6212,10 @@ class RouterModule {
5971
6212
  return { ngModule: RouterModule, providers: [provideRoutes(routes)] };
5972
6213
  }
5973
6214
  }
5974
- RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterModule, deps: [{ token: ROUTER_FORROOT_GUARD, optional: true }, { token: Router, optional: true }], target: i0.ɵɵFactoryTarget.NgModule });
5975
- RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterModule, declarations: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent], exports: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent] });
5976
- RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterModule });
5977
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterModule, decorators: [{
6215
+ RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterModule, deps: [{ token: ROUTER_FORROOT_GUARD, optional: true }, { token: Router, optional: true }], target: i0.ɵɵFactoryTarget.NgModule });
6216
+ RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterModule, declarations: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent], exports: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent] });
6217
+ RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterModule });
6218
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterModule, decorators: [{
5978
6219
  type: NgModule,
5979
6220
  args: [{
5980
6221
  declarations: ROUTER_DIRECTIVES,
@@ -6036,11 +6277,11 @@ function setupRouter(urlSerializer, contexts, location, injector, compiler, conf
6036
6277
  }
6037
6278
  router.titleStrategy = titleStrategy ?? defaultTitleStrategy;
6038
6279
  assignExtraOptionsToRouter(opts, router);
6039
- if (opts.enableTracing) {
6280
+ if ((typeof ngDevMode === 'undefined' || ngDevMode) && opts.enableTracing) {
6040
6281
  router.events.subscribe((e) => {
6041
6282
  // tslint:disable:no-console
6042
6283
  console.group?.(`Router Event: ${e.constructor.name}`);
6043
- console.log(e.toString());
6284
+ console.log(stringifyEvent(e));
6044
6285
  console.log(e);
6045
6286
  console.groupEnd?.();
6046
6287
  // tslint:enable:no-console
@@ -6107,9 +6348,7 @@ class RouterInitializer {
6107
6348
  router.setUpLocationChangeListener();
6108
6349
  resolve(true);
6109
6350
  }
6110
- else if (
6111
- // TODO: enabled is deprecated as of v11, can be removed in v13
6112
- opts.initialNavigation === 'enabled' || opts.initialNavigation === 'enabledBlocking') {
6351
+ else if (opts.initialNavigation === 'enabledBlocking') {
6113
6352
  router.hooks.afterPreactivation = () => {
6114
6353
  // only the initial navigation should be delayed
6115
6354
  if (!this.initNavigation) {
@@ -6153,9 +6392,9 @@ class RouterInitializer {
6153
6392
  this.destroyed = true;
6154
6393
  }
6155
6394
  }
6156
- RouterInitializer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterInitializer, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
6157
- RouterInitializer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterInitializer });
6158
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: RouterInitializer, decorators: [{
6395
+ RouterInitializer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterInitializer, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
6396
+ RouterInitializer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterInitializer });
6397
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.2", ngImport: i0, type: RouterInitializer, decorators: [{
6159
6398
  type: Injectable
6160
6399
  }], ctorParameters: function () { return [{ type: i0.Injector }]; } });
6161
6400
  function getAppInitializer(r) {
@@ -6195,7 +6434,7 @@ function provideRouterInitializer() {
6195
6434
  /**
6196
6435
  * @publicApi
6197
6436
  */
6198
- const VERSION = new Version('14.0.0-next.9');
6437
+ const VERSION = new Version('14.0.0-rc.2');
6199
6438
 
6200
6439
  /**
6201
6440
  * @license