fix(router): NavigatonError and NavigationCancel should be emitted after resetting the URL (#20803)
PR Close #20803
This commit is contained in:
@ -745,12 +745,14 @@ export class Router {
|
|||||||
},
|
},
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
if (isNavigationCancelingError(e)) {
|
if (isNavigationCancelingError(e)) {
|
||||||
this.resetUrlToCurrentUrlTree();
|
|
||||||
this.navigated = true;
|
this.navigated = true;
|
||||||
|
this.resetStateAndUrl(storedState, storedUrl, rawUrl);
|
||||||
(this.events as Subject<Event>)
|
(this.events as Subject<Event>)
|
||||||
.next(new NavigationCancel(id, this.serializeUrl(url), e.message));
|
.next(new NavigationCancel(id, this.serializeUrl(url), e.message));
|
||||||
|
|
||||||
resolvePromise(false);
|
resolvePromise(false);
|
||||||
} else {
|
} else {
|
||||||
|
this.resetStateAndUrl(storedState, storedUrl, rawUrl);
|
||||||
(this.events as Subject<Event>)
|
(this.events as Subject<Event>)
|
||||||
.next(new NavigationError(id, this.serializeUrl(url), e));
|
.next(new NavigationError(id, this.serializeUrl(url), e));
|
||||||
try {
|
try {
|
||||||
@ -759,15 +761,17 @@ export class Router {
|
|||||||
rejectPromise(ee);
|
rejectPromise(ee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(this as{routerState: RouterState}).routerState = storedState;
|
|
||||||
this.currentUrlTree = storedUrl;
|
|
||||||
this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, rawUrl);
|
|
||||||
this.resetUrlToCurrentUrlTree();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resetStateAndUrl(storedState: RouterState, storedUrl: UrlTree, rawUrl: UrlTree): void {
|
||||||
|
(this as{routerState: RouterState}).routerState = storedState;
|
||||||
|
this.currentUrlTree = storedUrl;
|
||||||
|
this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, rawUrl);
|
||||||
|
this.resetUrlToCurrentUrlTree();
|
||||||
|
}
|
||||||
|
|
||||||
private resetUrlToCurrentUrlTree(): void {
|
private resetUrlToCurrentUrlTree(): void {
|
||||||
this.location.replaceState(this.urlSerializer.serialize(this.rawUrlTree));
|
this.location.replaceState(this.urlSerializer.serialize(this.rawUrlTree));
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import {Observable} from 'rxjs/Observable';
|
|||||||
import {Observer} from 'rxjs/Observer';
|
import {Observer} from 'rxjs/Observer';
|
||||||
import {of } from 'rxjs/observable/of';
|
import {of } from 'rxjs/observable/of';
|
||||||
import {map} from 'rxjs/operator/map';
|
import {map} from 'rxjs/operator/map';
|
||||||
|
import {log} from 'util';
|
||||||
|
|
||||||
import {forEach} from '../src/utils/collection';
|
import {forEach} from '../src/utils/collection';
|
||||||
import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing';
|
import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing';
|
||||||
@ -840,6 +841,92 @@ describe('Integration', () => {
|
|||||||
]);
|
]);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
it('should dispatch NavigationError after the url has been reset back', fakeAsync(() => {
|
||||||
|
const router = TestBed.get(Router);
|
||||||
|
const location = TestBed.get(Location);
|
||||||
|
const fixture = createRoot(router, RootCmp);
|
||||||
|
|
||||||
|
router.resetConfig(
|
||||||
|
[{path: 'simple', component: SimpleCmp}, {path: 'throwing', component: ThrowingCmp}]);
|
||||||
|
|
||||||
|
router.navigateByUrl('/simple');
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
let routerUrlBeforeEmittingError;
|
||||||
|
let locationUrlBeforeEmittingError;
|
||||||
|
router.events.forEach((e: any) => {
|
||||||
|
if (e instanceof NavigationError) {
|
||||||
|
routerUrlBeforeEmittingError = router.url;
|
||||||
|
locationUrlBeforeEmittingError = location.path();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.navigateByUrl('/throwing').catch(() => null);
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
expect(routerUrlBeforeEmittingError).toEqual('/simple');
|
||||||
|
expect(locationUrlBeforeEmittingError).toEqual('/simple');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not trigger another navigation when resetting the url back due to a NavigationError',
|
||||||
|
fakeAsync(() => {
|
||||||
|
const router = TestBed.get(Router);
|
||||||
|
router.onSameUrlNavigation = 'reload';
|
||||||
|
|
||||||
|
const fixture = createRoot(router, RootCmp);
|
||||||
|
|
||||||
|
router.resetConfig(
|
||||||
|
[{path: 'simple', component: SimpleCmp}, {path: 'throwing', component: ThrowingCmp}]);
|
||||||
|
|
||||||
|
const events: any[] = [];
|
||||||
|
router.events.forEach((e: any) => {
|
||||||
|
if (e instanceof NavigationStart) {
|
||||||
|
events.push(e.url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.navigateByUrl('/simple');
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
router.navigateByUrl('/throwing').catch(() => null);
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
// we do not trigger another navigation to /simple
|
||||||
|
expect(events).toEqual(['/simple', '/throwing']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should dispatch NavigationCancel after the url has been reset back', fakeAsync(() => {
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{providers: [{provide: 'returnsFalse', useValue: () => false}]});
|
||||||
|
|
||||||
|
const router = TestBed.get(Router);
|
||||||
|
const location = TestBed.get(Location);
|
||||||
|
|
||||||
|
const fixture = createRoot(router, RootCmp);
|
||||||
|
|
||||||
|
router.resetConfig([
|
||||||
|
{path: 'simple', component: SimpleCmp},
|
||||||
|
{path: 'throwing', loadChildren: 'doesnotmatter', canLoad: ['returnsFalse']}
|
||||||
|
]);
|
||||||
|
|
||||||
|
router.navigateByUrl('/simple');
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
let routerUrlBeforeEmittingError;
|
||||||
|
let locationUrlBeforeEmittingError;
|
||||||
|
router.events.forEach((e: any) => {
|
||||||
|
if (e instanceof NavigationCancel) {
|
||||||
|
routerUrlBeforeEmittingError = router.url;
|
||||||
|
locationUrlBeforeEmittingError = location.path();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
(<any>location).simulateHashChange('/throwing');
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
expect(locationUrlBeforeEmittingError).toEqual('/simple');
|
||||||
|
}));
|
||||||
|
|
||||||
it('should support custom error handlers', fakeAsync(inject([Router], (router: Router) => {
|
it('should support custom error handlers', fakeAsync(inject([Router], (router: Router) => {
|
||||||
router.errorHandler = (error) => 'resolvedValue';
|
router.errorHandler = (error) => 'resolvedValue';
|
||||||
const fixture = createRoot(router, RootCmp);
|
const fixture = createRoot(router, RootCmp);
|
||||||
@ -3915,6 +4002,12 @@ class RootCmpWithOnInit {
|
|||||||
class RootCmpWithTwoOutlets {
|
class RootCmpWithTwoOutlets {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'throwing-cmp', template: ''})
|
||||||
|
class ThrowingCmp {
|
||||||
|
constructor() { throw new Error('Throwing Cmp'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function advance(fixture: ComponentFixture<any>): void {
|
function advance(fixture: ComponentFixture<any>): void {
|
||||||
tick();
|
tick();
|
||||||
@ -3955,6 +4048,7 @@ function createRoot(router: Router, type: any): ComponentFixture<any> {
|
|||||||
RelativeLinkInIfCmp,
|
RelativeLinkInIfCmp,
|
||||||
RootCmpWithTwoOutlets,
|
RootCmpWithTwoOutlets,
|
||||||
EmptyQueryParamsCmp,
|
EmptyQueryParamsCmp,
|
||||||
|
ThrowingCmp
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
||||||
@ -3982,6 +4076,7 @@ function createRoot(router: Router, type: any): ComponentFixture<any> {
|
|||||||
RelativeLinkInIfCmp,
|
RelativeLinkInIfCmp,
|
||||||
RootCmpWithTwoOutlets,
|
RootCmpWithTwoOutlets,
|
||||||
EmptyQueryParamsCmp,
|
EmptyQueryParamsCmp,
|
||||||
|
ThrowingCmp
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
||||||
@ -4010,6 +4105,7 @@ function createRoot(router: Router, type: any): ComponentFixture<any> {
|
|||||||
RelativeLinkInIfCmp,
|
RelativeLinkInIfCmp,
|
||||||
RootCmpWithTwoOutlets,
|
RootCmpWithTwoOutlets,
|
||||||
EmptyQueryParamsCmp,
|
EmptyQueryParamsCmp,
|
||||||
|
ThrowingCmp
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
class TestModule {
|
class TestModule {
|
||||||
|
Reference in New Issue
Block a user