@angular/router 12.1.0-next.4 → 12.1.1

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v12.1.0-next.4
2
+ * @license Angular v12.1.1
3
3
  * (c) 2010-2021 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -969,12 +969,14 @@ function serializeMatrixParams(params) {
969
969
  .join('');
970
970
  }
971
971
  function serializeQueryParams(params) {
972
- const strParams = Object.keys(params).map((name) => {
972
+ const strParams = Object.keys(params)
973
+ .map((name) => {
973
974
  const value = params[name];
974
975
  return Array.isArray(value) ?
975
976
  value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') :
976
977
  `${encodeUriQuery(name)}=${encodeUriQuery(value)}`;
977
- });
978
+ })
979
+ .filter(s => !!s);
978
980
  return strParams.length ? `?${strParams.join('&')}` : '';
979
981
  }
980
982
  const SEGMENT_RE = /^[^\/()?;=#]+/;
@@ -3874,6 +3876,11 @@ class Router {
3874
3876
  */
3875
3877
  this.lastLocationChangeInfo = null;
3876
3878
  this.navigationId = 0;
3879
+ /**
3880
+ * The id of the currently active page in the router.
3881
+ * Updated to the transition's target id on a successful navigation.
3882
+ */
3883
+ this.currentPageId = 0;
3877
3884
  this.isNgZoneEnabled = false;
3878
3885
  /**
3879
3886
  * An event stream for routing events in this NgModule.
@@ -3915,8 +3922,16 @@ class Router {
3915
3922
  this.routeReuseStrategy = new DefaultRouteReuseStrategy();
3916
3923
  /**
3917
3924
  * How to handle a navigation request to the current URL. One of:
3925
+ *
3918
3926
  * - `'ignore'` : The router ignores the request.
3919
3927
  * - `'reload'` : The router reloads the URL. Use to implement a "refresh" feature.
3928
+ *
3929
+ * Note that this only configures whether the Route reprocesses the URL and triggers related
3930
+ * action and events like redirects, guards, and resolvers. By default, the router re-uses a
3931
+ * component instance when it re-navigates to the same component type without visiting a different
3932
+ * component first. This behavior is configured by the `RouteReuseStrategy`. In order to reload
3933
+ * routed components on same url navigation, you need to set `onSameUrlNavigation` to `'reload'`
3934
+ * _and_ provide a `RouteReuseStrategy` which returns `false` for `shouldReuseRoute`.
3920
3935
  */
3921
3936
  this.onSameUrlNavigation = 'ignore';
3922
3937
  /**
@@ -3942,6 +3957,24 @@ class Router {
3942
3957
  * @see `RouterModule`
3943
3958
  */
3944
3959
  this.relativeLinkResolution = 'corrected';
3960
+ /**
3961
+ * Configures how the Router attempts to restore state when a navigation is cancelled.
3962
+ *
3963
+ * 'replace' - Always uses `location.replaceState` to set the browser state to the state of the
3964
+ * router before the navigation started.
3965
+ *
3966
+ * 'computed' - Will always return to the same state that corresponds to the actual Angular route
3967
+ * when the navigation gets cancelled right after triggering a `popstate` event.
3968
+ *
3969
+ * The default value is `replace`
3970
+ *
3971
+ * @internal
3972
+ */
3973
+ // TODO(atscott): Determine how/when/if to make this public API
3974
+ // This shouldn’t be an option at all but may need to be in order to allow migration without a
3975
+ // breaking change. We need to determine if it should be made into public api (or if we forgo
3976
+ // the option and release as a breaking change bug fix in a major version).
3977
+ this.canceledNavigationResolution = 'replace';
3945
3978
  const onLoadStart = (r) => this.triggerEvent(new RouteConfigLoadStart(r));
3946
3979
  const onLoadEnd = (r) => this.triggerEvent(new RouteConfigLoadEnd(r));
3947
3980
  this.ngModule = injector.get(NgModuleRef);
@@ -3956,6 +3989,7 @@ class Router {
3956
3989
  this.routerState = createEmptyState(this.currentUrlTree, this.rootComponentType);
3957
3990
  this.transitions = new BehaviorSubject({
3958
3991
  id: 0,
3992
+ targetPageId: 0,
3959
3993
  currentUrlTree: this.currentUrlTree,
3960
3994
  currentRawUrl: this.currentUrlTree,
3961
3995
  extractedUrl: this.urlHandlingStrategy.extract(this.currentUrlTree),
@@ -4028,7 +4062,7 @@ class Router {
4028
4062
  tap(t => {
4029
4063
  if (this.urlUpdateStrategy === 'eager') {
4030
4064
  if (!t.extras.skipLocationChange) {
4031
- this.setBrowserUrl(t.urlAfterRedirects, !!t.extras.replaceUrl, t.id, t.extras.state);
4065
+ this.setBrowserUrl(t.urlAfterRedirects, t);
4032
4066
  }
4033
4067
  this.browserUrlTree = t.urlAfterRedirects;
4034
4068
  }
@@ -4088,10 +4122,7 @@ class Router {
4088
4122
  this.triggerEvent(guardsEnd);
4089
4123
  }), filter(t => {
4090
4124
  if (!t.guardsResult) {
4091
- this.resetUrlToCurrentUrlTree();
4092
- const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), '');
4093
- eventsSubject.next(navCancel);
4094
- t.resolve(false);
4125
+ this.cancelNavigationTransition(t, '');
4095
4126
  return false;
4096
4127
  }
4097
4128
  return true;
@@ -4108,9 +4139,7 @@ class Router {
4108
4139
  next: () => dataResolved = true,
4109
4140
  complete: () => {
4110
4141
  if (!dataResolved) {
4111
- const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), `At least one route resolver didn't emit any value.`);
4112
- eventsSubject.next(navCancel);
4113
- t.resolve(false);
4142
+ this.cancelNavigationTransition(t, `At least one route resolver didn't emit any value.`);
4114
4143
  }
4115
4144
  }
4116
4145
  }));
@@ -4147,7 +4176,7 @@ class Router {
4147
4176
  this.routerState = t.targetRouterState;
4148
4177
  if (this.urlUpdateStrategy === 'deferred') {
4149
4178
  if (!t.extras.skipLocationChange) {
4150
- this.setBrowserUrl(this.rawUrlTree, !!t.extras.replaceUrl, t.id, t.extras.state);
4179
+ this.setBrowserUrl(this.rawUrlTree, t);
4151
4180
  }
4152
4181
  this.browserUrlTree = t.urlAfterRedirects;
4153
4182
  }
@@ -4174,10 +4203,7 @@ class Router {
4174
4203
  // sync code which looks for a value here in order to determine whether or
4175
4204
  // not to handle a given popstate event or to leave it to the Angular
4176
4205
  // router.
4177
- this.resetUrlToCurrentUrlTree();
4178
- const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), `Navigation ID ${t.id} is not equal to the current navigation id ${this.navigationId}`);
4179
- eventsSubject.next(navCancel);
4180
- t.resolve(false);
4206
+ this.cancelNavigationTransition(t, `Navigation ID ${t.id} is not equal to the current navigation id ${this.navigationId}`);
4181
4207
  }
4182
4208
  // currentNavigation should always be reset to null here. If navigation was
4183
4209
  // successful, lastSuccessfulTransition will have already been set. Therefore
@@ -4289,6 +4315,7 @@ class Router {
4289
4315
  if (state) {
4290
4316
  const stateCopy = Object.assign({}, state);
4291
4317
  delete stateCopy.navigationId;
4318
+ delete stateCopy.ɵrouterPageId;
4292
4319
  if (Object.keys(stateCopy).length !== 0) {
4293
4320
  extras.state = stateCopy;
4294
4321
  }
@@ -4483,7 +4510,14 @@ class Router {
4483
4510
  }
4484
4511
  const urlTree = isUrlTree(url) ? url : this.parseUrl(url);
4485
4512
  const mergedTree = this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree);
4486
- return this.scheduleNavigation(mergedTree, 'imperative', null, extras);
4513
+ let restoredState = null;
4514
+ if (this.canceledNavigationResolution === 'computed') {
4515
+ const isInitialPage = this.currentPageId === 0;
4516
+ if (isInitialPage || extras.skipLocationChange || extras.replaceUrl) {
4517
+ restoredState = this.location.getState();
4518
+ }
4519
+ }
4520
+ return this.scheduleNavigation(mergedTree, 'imperative', restoredState, extras);
4487
4521
  }
4488
4522
  /**
4489
4523
  * Navigate based on the provided array of commands and a starting point.
@@ -4564,6 +4598,7 @@ class Router {
4564
4598
  this.navigations.subscribe(t => {
4565
4599
  this.navigated = true;
4566
4600
  this.lastSuccessfulId = t.id;
4601
+ this.currentPageId = t.targetPageId;
4567
4602
  this.events
4568
4603
  .next(new NavigationEnd(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(this.currentUrlTree)));
4569
4604
  this.lastSuccessfulNavigation = this.currentNavigation;
@@ -4614,8 +4649,24 @@ class Router {
4614
4649
  });
4615
4650
  }
4616
4651
  const id = ++this.navigationId;
4652
+ let targetPageId;
4653
+ if (this.canceledNavigationResolution === 'computed') {
4654
+ // If the `ɵrouterPageId` exist in the state then `targetpageId` should have the value of
4655
+ // `ɵrouterPageId`
4656
+ if (restoredState && restoredState.ɵrouterPageId) {
4657
+ targetPageId = restoredState.ɵrouterPageId;
4658
+ }
4659
+ else {
4660
+ targetPageId = this.currentPageId + 1;
4661
+ }
4662
+ }
4663
+ else {
4664
+ // This is unused when `canceledNavigationResolution` is not computed.
4665
+ targetPageId = 0;
4666
+ }
4617
4667
  this.setTransition({
4618
4668
  id,
4669
+ targetPageId,
4619
4670
  source,
4620
4671
  restoredState,
4621
4672
  currentUrlTree: this.currentUrlTree,
@@ -4634,15 +4685,14 @@ class Router {
4634
4685
  return Promise.reject(e);
4635
4686
  });
4636
4687
  }
4637
- setBrowserUrl(url, replaceUrl, id, state) {
4688
+ setBrowserUrl(url, t) {
4638
4689
  const path = this.urlSerializer.serialize(url);
4639
- state = state || {};
4640
- if (this.location.isCurrentPathEqualTo(path) || replaceUrl) {
4641
- // TODO(jasonaden): Remove first `navigationId` and rely on `ng` namespace.
4642
- this.location.replaceState(path, '', Object.assign(Object.assign({}, state), { navigationId: id }));
4690
+ const state = Object.assign(Object.assign({}, t.extras.state), this.generateNgRouterState(t.id, t.targetPageId));
4691
+ if (this.location.isCurrentPathEqualTo(path) || !!t.extras.replaceUrl) {
4692
+ this.location.replaceState(path, '', state);
4643
4693
  }
4644
4694
  else {
4645
- this.location.go(path, '', Object.assign(Object.assign({}, state), { navigationId: id }));
4695
+ this.location.go(path, '', state);
4646
4696
  }
4647
4697
  }
4648
4698
  resetStateAndUrl(storedState, storedUrl, rawUrl) {
@@ -4652,7 +4702,43 @@ class Router {
4652
4702
  this.resetUrlToCurrentUrlTree();
4653
4703
  }
4654
4704
  resetUrlToCurrentUrlTree() {
4655
- this.location.replaceState(this.urlSerializer.serialize(this.rawUrlTree), '', { navigationId: this.lastSuccessfulId });
4705
+ this.location.replaceState(this.urlSerializer.serialize(this.rawUrlTree), '', this.generateNgRouterState(this.lastSuccessfulId, this.currentPageId));
4706
+ }
4707
+ /**
4708
+ * Responsible for handling the cancellation of a navigation:
4709
+ * - performs the necessary rollback action to restore the browser URL to the
4710
+ * state before the transition
4711
+ * - triggers the `NavigationCancel` event
4712
+ * - resolves the transition promise with `false`
4713
+ */
4714
+ cancelNavigationTransition(t, reason) {
4715
+ if (this.canceledNavigationResolution === 'computed') {
4716
+ // The navigator change the location before triggered the browser event,
4717
+ // so we need to go back to the current url if the navigation is canceled.
4718
+ // Also, when navigation gets cancelled while using url update strategy eager, then we need to
4719
+ // go back. Because, when `urlUpdateSrategy` is `eager`; `setBrowserUrl` method is called
4720
+ // before any verification.
4721
+ if (t.source === 'popstate' || this.urlUpdateStrategy === 'eager') {
4722
+ const targetPagePosition = this.currentPageId - t.targetPageId;
4723
+ this.location.historyGo(targetPagePosition);
4724
+ }
4725
+ else {
4726
+ // If update is not 'eager' and the transition navigation source isn't 'popstate', then the
4727
+ // navigation was cancelled before any browser url change so nothing needs to be restored.
4728
+ }
4729
+ }
4730
+ else {
4731
+ this.resetUrlToCurrentUrlTree();
4732
+ }
4733
+ const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), reason);
4734
+ this.triggerEvent(navCancel);
4735
+ t.resolve(false);
4736
+ }
4737
+ generateNgRouterState(navigationId, routerPageId) {
4738
+ if (this.canceledNavigationResolution === 'computed') {
4739
+ return { navigationId, ɵrouterPageId: routerPageId };
4740
+ }
4741
+ return { navigationId };
4656
4742
  }
4657
4743
  }
4658
4744
  Router.decorators = [
@@ -5794,11 +5880,16 @@ class RouterInitializer {
5794
5880
  constructor(injector) {
5795
5881
  this.injector = injector;
5796
5882
  this.initNavigation = false;
5883
+ this.destroyed = false;
5797
5884
  this.resultOfPreactivationDone = new Subject();
5798
5885
  }
5799
5886
  appInitializer() {
5800
5887
  const p = this.injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
5801
5888
  return p.then(() => {
5889
+ // If the injector was destroyed, the DI lookups below will fail.
5890
+ if (this.destroyed) {
5891
+ return Promise.resolve(true);
5892
+ }
5802
5893
  let resolve = null;
5803
5894
  const res = new Promise(r => resolve = r);
5804
5895
  const router = this.injector.get(Router);
@@ -5849,6 +5940,9 @@ class RouterInitializer {
5849
5940
  this.resultOfPreactivationDone.next(null);
5850
5941
  this.resultOfPreactivationDone.complete();
5851
5942
  }
5943
+ ngOnDestroy() {
5944
+ this.destroyed = true;
5945
+ }
5852
5946
  }
5853
5947
  RouterInitializer.decorators = [
5854
5948
  { type: Injectable }
@@ -5893,7 +5987,7 @@ function provideRouterInitializer() {
5893
5987
  /**
5894
5988
  * @publicApi
5895
5989
  */
5896
- const VERSION = new Version('12.1.0-next.4');
5990
+ const VERSION = new Version('12.1.1');
5897
5991
 
5898
5992
  /**
5899
5993
  * @license