feat(router): add router-level events for GuardsCheck and Resolve (#17601)
This commit is contained in:
parent
b479ed9407
commit
8a1a989a1c
|
@ -128,18 +128,114 @@ export class RouteConfigLoadEnd {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @whatItDoes Represents a router event.
|
* @whatItDoes Represents the start of the Guard phase of routing.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
export class GuardsCheckStart {
|
||||||
|
constructor(
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public id: number,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public url: string,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public urlAfterRedirects: string,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public state: RouterStateSnapshot) {}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @whatItDoes Represents the end of the Guard phase of routing.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
export class GuardsCheckEnd {
|
||||||
|
constructor(
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public id: number,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public url: string,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public urlAfterRedirects: string,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public state: RouterStateSnapshot,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public shouldActivate: boolean) {}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @whatItDoes Represents the start of the Resolve phase of routing. The timing of this
|
||||||
|
* event may change, thus it's experimental. In the current iteration it will run
|
||||||
|
* in the "resolve" phase whether there's things to resolve or not. In the future this
|
||||||
|
* behavior may change to only run when there are things to be resolved.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
export class ResolveStart {
|
||||||
|
constructor(
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public id: number,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public url: string,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public urlAfterRedirects: string,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public state: RouterStateSnapshot) {}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @whatItDoes Represents the end of the Resolve phase of routing. See note on
|
||||||
|
* {@link ResolveStart} for use of this experimental API.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
export class ResolveEnd {
|
||||||
|
constructor(
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public id: number,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public url: string,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public urlAfterRedirects: string,
|
||||||
|
/** @docsNotRequired */
|
||||||
|
public state: RouterStateSnapshot) {}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @whatItDoes Represents a router event, allowing you to track the lifecycle of the router.
|
||||||
|
*
|
||||||
|
* The sequence of router events is:
|
||||||
*
|
*
|
||||||
* One of:
|
|
||||||
* - {@link NavigationStart},
|
* - {@link NavigationStart},
|
||||||
|
* - {@link RouteConfigLoadStart},
|
||||||
|
* - {@link RouteConfigLoadEnd},
|
||||||
|
* - {@link RoutesRecognized},
|
||||||
|
* - {@link GuardsCheckStart},
|
||||||
|
* - {@link GuardsCheckEnd},
|
||||||
|
* - {@link ResolveStart},
|
||||||
|
* - {@link ResolveEnd},
|
||||||
* - {@link NavigationEnd},
|
* - {@link NavigationEnd},
|
||||||
* - {@link NavigationCancel},
|
* - {@link NavigationCancel},
|
||||||
* - {@link NavigationError},
|
* - {@link NavigationError}
|
||||||
* - {@link RoutesRecognized},
|
|
||||||
* - {@link RouteConfigLoadStart},
|
|
||||||
* - {@link RouteConfigLoadEnd}
|
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError |
|
export type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError |
|
||||||
RoutesRecognized | RouteConfigLoadStart | RouteConfigLoadEnd;
|
RoutesRecognized | RouteConfigLoadStart | RouteConfigLoadEnd | GuardsCheckStart |
|
||||||
|
GuardsCheckEnd | ResolveStart | ResolveEnd;
|
||||||
|
|
|
@ -11,7 +11,7 @@ export {Data, LoadChildren, LoadChildrenCallback, ResolveData, Route, Routes, Ru
|
||||||
export {RouterLink, RouterLinkWithHref} from './directives/router_link';
|
export {RouterLink, RouterLinkWithHref} from './directives/router_link';
|
||||||
export {RouterLinkActive} from './directives/router_link_active';
|
export {RouterLinkActive} from './directives/router_link_active';
|
||||||
export {RouterOutlet} from './directives/router_outlet';
|
export {RouterOutlet} from './directives/router_outlet';
|
||||||
export {Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events';
|
export {Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events';
|
||||||
export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, Resolve} from './interfaces';
|
export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, Resolve} from './interfaces';
|
||||||
export {DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy';
|
export {DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy';
|
||||||
export {NavigationExtras, Router} from './router';
|
export {NavigationExtras, Router} from './router';
|
||||||
|
|
|
@ -25,7 +25,7 @@ import {applyRedirects} from './apply_redirects';
|
||||||
import {LoadedRouterConfig, QueryParamsHandling, ResolveData, Route, Routes, RunGuardsAndResolvers, validateConfig} from './config';
|
import {LoadedRouterConfig, QueryParamsHandling, ResolveData, Route, Routes, RunGuardsAndResolvers, validateConfig} from './config';
|
||||||
import {createRouterState} from './create_router_state';
|
import {createRouterState} from './create_router_state';
|
||||||
import {createUrlTree} from './create_url_tree';
|
import {createUrlTree} from './create_url_tree';
|
||||||
import {Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events';
|
import {Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events';
|
||||||
import {recognize} from './recognize';
|
import {recognize} from './recognize';
|
||||||
import {DefaultRouteReuseStrategy, DetachedRouteHandleInternal, RouteReuseStrategy} from './route_reuse_strategy';
|
import {DefaultRouteReuseStrategy, DetachedRouteHandleInternal, RouteReuseStrategy} from './route_reuse_strategy';
|
||||||
import {RouterConfigLoader} from './router_config_loader';
|
import {RouterConfigLoader} from './router_config_loader';
|
||||||
|
@ -639,20 +639,33 @@ export class Router {
|
||||||
({appliedUrl, snapshot}: {appliedUrl: string, snapshot: RouterStateSnapshot}) => {
|
({appliedUrl, snapshot}: {appliedUrl: string, snapshot: RouterStateSnapshot}) => {
|
||||||
if (this.navigationId !== id) return of (false);
|
if (this.navigationId !== id) return of (false);
|
||||||
|
|
||||||
|
this.triggerEvent(
|
||||||
|
new GuardsCheckStart(id, this.serializeUrl(url), appliedUrl, snapshot));
|
||||||
|
|
||||||
return map.call(preActivation.checkGuards(), (shouldActivate: boolean) => {
|
return map.call(preActivation.checkGuards(), (shouldActivate: boolean) => {
|
||||||
|
this.triggerEvent(new GuardsCheckEnd(
|
||||||
|
id, this.serializeUrl(url), appliedUrl, snapshot, shouldActivate));
|
||||||
return {appliedUrl: appliedUrl, snapshot: snapshot, shouldActivate: shouldActivate};
|
return {appliedUrl: appliedUrl, snapshot: snapshot, shouldActivate: shouldActivate};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const preactivationResolveData$ = mergeMap.call(preactivationCheckGuards$, (p: any) => {
|
const preactivationResolveData$ = mergeMap.call(
|
||||||
if (this.navigationId !== id) return of (false);
|
preactivationCheckGuards$,
|
||||||
|
(p: {appliedUrl: string, snapshot: RouterStateSnapshot, shouldActivate: boolean}) => {
|
||||||
|
if (this.navigationId !== id) return of (false);
|
||||||
|
|
||||||
if (p.shouldActivate) {
|
if (p.shouldActivate && preActivation.isActivating()) {
|
||||||
return map.call(preActivation.resolveData(), () => p);
|
this.triggerEvent(
|
||||||
} else {
|
new ResolveStart(id, this.serializeUrl(url), p.appliedUrl, p.snapshot));
|
||||||
return of (p);
|
return map.call(preActivation.resolveData(), () => {
|
||||||
}
|
this.triggerEvent(
|
||||||
});
|
new ResolveEnd(id, this.serializeUrl(url), p.appliedUrl, p.snapshot));
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return of (p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const preactivationDone$ = mergeMap.call(preactivationResolveData$, (p: any) => {
|
const preactivationDone$ = mergeMap.call(preactivationResolveData$, (p: any) => {
|
||||||
return map.call(this.hooks.afterPreactivation(p.snapshot), () => p);
|
return map.call(this.hooks.afterPreactivation(p.snapshot), () => p);
|
||||||
|
@ -772,8 +785,12 @@ export class PreActivation {
|
||||||
this.traverseChildRoutes(futureRoot, currRoot, parentContexts, [futureRoot.value]);
|
this.traverseChildRoutes(futureRoot, currRoot, parentContexts, [futureRoot.value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(jasonaden): Refactor checkGuards and resolveData so they can collect the checks
|
||||||
|
// and guards before mapping into the observable. Likely remove the observable completely
|
||||||
|
// and make these pure functions so they are more predictable and don't rely on so much
|
||||||
|
// external state.
|
||||||
checkGuards(): Observable<boolean> {
|
checkGuards(): Observable<boolean> {
|
||||||
if (this.canDeactivateChecks.length === 0 && this.canActivateChecks.length === 0) {
|
if (!this.isDeactivating() && !this.isActivating()) {
|
||||||
return of (true);
|
return of (true);
|
||||||
}
|
}
|
||||||
const canDeactivate$ = this.runCanDeactivateChecks();
|
const canDeactivate$ = this.runCanDeactivateChecks();
|
||||||
|
@ -783,13 +800,17 @@ export class PreActivation {
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveData(): Observable<any> {
|
resolveData(): Observable<any> {
|
||||||
if (this.canActivateChecks.length === 0) return of (null);
|
if (!this.isActivating()) return of (null);
|
||||||
const checks$ = from(this.canActivateChecks);
|
const checks$ = from(this.canActivateChecks);
|
||||||
const runningChecks$ =
|
const runningChecks$ =
|
||||||
concatMap.call(checks$, (check: CanActivate) => this.runResolve(check.route));
|
concatMap.call(checks$, (check: CanActivate) => this.runResolve(check.route));
|
||||||
return reduce.call(runningChecks$, (_: any, __: any) => _);
|
return reduce.call(runningChecks$, (_: any, __: any) => _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDeactivating(): boolean { return this.canDeactivateChecks.length !== 0; }
|
||||||
|
|
||||||
|
isActivating(): boolean { return this.canActivateChecks.length !== 0; }
|
||||||
|
|
||||||
private traverseChildRoutes(
|
private traverseChildRoutes(
|
||||||
futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>|null,
|
futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>|null,
|
||||||
contexts: ChildrenOutletContexts|null, futurePath: ActivatedRouteSnapshot[]): void {
|
contexts: ChildrenOutletContexts|null, futurePath: ActivatedRouteSnapshot[]): void {
|
||||||
|
|
|
@ -96,6 +96,9 @@ export class RouterPreloader implements OnDestroy {
|
||||||
return this.processRoutes(ngModule, this.router.config);
|
return this.processRoutes(ngModule, this.router.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(jasonaden): This class relies on code external to the class to call setUpPreloading. If
|
||||||
|
// this hasn't been done, ngOnDestroy will fail as this.subscription will be undefined. This
|
||||||
|
// should be refactored.
|
||||||
ngOnDestroy(): void { this.subscription.unsubscribe(); }
|
ngOnDestroy(): void { this.subscription.unsubscribe(); }
|
||||||
|
|
||||||
private processRoutes(ngModule: NgModuleRef<any>, routes: Routes): Observable<void> {
|
private processRoutes(ngModule: NgModuleRef<any>, routes: Routes): Observable<void> {
|
||||||
|
|
|
@ -82,8 +82,10 @@ describe('bootstrap', () => {
|
||||||
const router = res.injector.get(Router);
|
const router = res.injector.get(Router);
|
||||||
const data = router.routerState.snapshot.root.firstChild !.data;
|
const data = router.routerState.snapshot.root.firstChild !.data;
|
||||||
expect(data['test']).toEqual('test-data');
|
expect(data['test']).toEqual('test-data');
|
||||||
expect(log).toEqual(
|
expect(log).toEqual([
|
||||||
['TestModule', 'NavigationStart', 'RoutesRecognized', 'RootCmp', 'NavigationEnd']);
|
'TestModule', 'NavigationStart', 'RoutesRecognized', 'GuardsCheckStart', 'GuardsCheckEnd',
|
||||||
|
'ResolveStart', 'ResolveEnd', 'RootCmp', 'NavigationEnd'
|
||||||
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -116,8 +118,11 @@ describe('bootstrap', () => {
|
||||||
platformBrowserDynamic([]).bootstrapModule(TestModule).then(res => {
|
platformBrowserDynamic([]).bootstrapModule(TestModule).then(res => {
|
||||||
const router = res.injector.get(Router);
|
const router = res.injector.get(Router);
|
||||||
expect(router.routerState.snapshot.root.firstChild).toBeNull();
|
expect(router.routerState.snapshot.root.firstChild).toBeNull();
|
||||||
// NavigationEnd has not been emitted yet because bootstrap returned too early
|
// ResolveEnd has not been emitted yet because bootstrap returned too early
|
||||||
expect(log).toEqual(['TestModule', 'RootCmp', 'NavigationStart', 'RoutesRecognized']);
|
expect(log).toEqual([
|
||||||
|
'TestModule', 'RootCmp', 'NavigationStart', 'RoutesRecognized', 'GuardsCheckStart',
|
||||||
|
'GuardsCheckEnd', 'ResolveStart'
|
||||||
|
]);
|
||||||
|
|
||||||
router.events.subscribe((e) => {
|
router.events.subscribe((e) => {
|
||||||
if (e instanceof NavigationEnd) {
|
if (e instanceof NavigationEnd) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {ChangeDetectionStrategy, Component, Injectable, NgModule, NgModuleFactor
|
||||||
import {ComponentFixture, TestBed, fakeAsync, inject, tick} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, fakeAsync, inject, tick} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, DetachedRouteHandle, Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, ParamMap, Params, PreloadAllModules, PreloadingStrategy, Resolve, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlTree} from '@angular/router';
|
import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, ParamMap, Params, PreloadAllModules, PreloadingStrategy, Resolve, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlTree} from '@angular/router';
|
||||||
import {Observable} from 'rxjs/Observable';
|
import {Observable} from 'rxjs/Observable';
|
||||||
import {map} from 'rxjs/operator/map';
|
import {map} from 'rxjs/operator/map';
|
||||||
|
|
||||||
|
@ -422,9 +422,13 @@ describe('Integration', () => {
|
||||||
|
|
||||||
expectEvents(recordedEvents, [
|
expectEvents(recordedEvents, [
|
||||||
[NavigationStart, '/team/22/user/victor'], [RoutesRecognized, '/team/22/user/victor'],
|
[NavigationStart, '/team/22/user/victor'], [RoutesRecognized, '/team/22/user/victor'],
|
||||||
|
[GuardsCheckStart, '/team/22/user/victor'], [GuardsCheckEnd, '/team/22/user/victor'],
|
||||||
|
[ResolveStart, '/team/22/user/victor'], [ResolveEnd, '/team/22/user/victor'],
|
||||||
[NavigationEnd, '/team/22/user/victor'],
|
[NavigationEnd, '/team/22/user/victor'],
|
||||||
|
|
||||||
[NavigationStart, '/team/22/user/fedor'], [RoutesRecognized, '/team/22/user/fedor'],
|
[NavigationStart, '/team/22/user/fedor'], [RoutesRecognized, '/team/22/user/fedor'],
|
||||||
|
[GuardsCheckStart, '/team/22/user/fedor'], [GuardsCheckEnd, '/team/22/user/fedor'],
|
||||||
|
[ResolveStart, '/team/22/user/fedor'], [ResolveEnd, '/team/22/user/fedor'],
|
||||||
[NavigationEnd, '/team/22/user/fedor']
|
[NavigationEnd, '/team/22/user/fedor']
|
||||||
]);
|
]);
|
||||||
})));
|
})));
|
||||||
|
@ -681,11 +685,14 @@ describe('Integration', () => {
|
||||||
|
|
||||||
expectEvents(recordedEvents, [
|
expectEvents(recordedEvents, [
|
||||||
[NavigationStart, '/user/init'], [RoutesRecognized, '/user/init'],
|
[NavigationStart, '/user/init'], [RoutesRecognized, '/user/init'],
|
||||||
[NavigationEnd, '/user/init'],
|
[GuardsCheckStart, '/user/init'], [GuardsCheckEnd, '/user/init'],
|
||||||
|
[ResolveStart, '/user/init'], [ResolveEnd, '/user/init'], [NavigationEnd, '/user/init'],
|
||||||
|
|
||||||
[NavigationStart, '/user/victor'], [NavigationCancel, '/user/victor'],
|
[NavigationStart, '/user/victor'], [NavigationCancel, '/user/victor'],
|
||||||
|
|
||||||
[NavigationStart, '/user/fedor'], [RoutesRecognized, '/user/fedor'],
|
[NavigationStart, '/user/fedor'], [RoutesRecognized, '/user/fedor'],
|
||||||
|
[GuardsCheckStart, '/user/fedor'], [GuardsCheckEnd, '/user/fedor'],
|
||||||
|
[ResolveStart, '/user/fedor'], [ResolveEnd, '/user/fedor'],
|
||||||
[NavigationEnd, '/user/fedor']
|
[NavigationEnd, '/user/fedor']
|
||||||
]);
|
]);
|
||||||
})));
|
})));
|
||||||
|
@ -712,6 +719,8 @@ describe('Integration', () => {
|
||||||
[NavigationStart, '/invalid'], [NavigationError, '/invalid'],
|
[NavigationStart, '/invalid'], [NavigationError, '/invalid'],
|
||||||
|
|
||||||
[NavigationStart, '/user/fedor'], [RoutesRecognized, '/user/fedor'],
|
[NavigationStart, '/user/fedor'], [RoutesRecognized, '/user/fedor'],
|
||||||
|
[GuardsCheckStart, '/user/fedor'], [GuardsCheckEnd, '/user/fedor'],
|
||||||
|
[ResolveStart, '/user/fedor'], [ResolveEnd, '/user/fedor'],
|
||||||
[NavigationEnd, '/user/fedor']
|
[NavigationEnd, '/user/fedor']
|
||||||
]);
|
]);
|
||||||
})));
|
})));
|
||||||
|
@ -965,6 +974,7 @@ describe('Integration', () => {
|
||||||
|
|
||||||
expectEvents(recordedEvents, [
|
expectEvents(recordedEvents, [
|
||||||
[NavigationStart, '/simple'], [RoutesRecognized, '/simple'],
|
[NavigationStart, '/simple'], [RoutesRecognized, '/simple'],
|
||||||
|
[GuardsCheckStart, '/simple'], [GuardsCheckEnd, '/simple'], [ResolveStart, '/simple'],
|
||||||
[NavigationError, '/simple']
|
[NavigationError, '/simple']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -1382,8 +1392,10 @@ describe('Integration', () => {
|
||||||
expect(location.path()).toEqual('/');
|
expect(location.path()).toEqual('/');
|
||||||
expectEvents(recordedEvents, [
|
expectEvents(recordedEvents, [
|
||||||
[NavigationStart, '/team/22'], [RoutesRecognized, '/team/22'],
|
[NavigationStart, '/team/22'], [RoutesRecognized, '/team/22'],
|
||||||
|
[GuardsCheckStart, '/team/22'], [GuardsCheckEnd, '/team/22'],
|
||||||
[NavigationCancel, '/team/22']
|
[NavigationCancel, '/team/22']
|
||||||
]);
|
]);
|
||||||
|
expect((recordedEvents[3] as GuardsCheckEnd).shouldActivate).toBe(false);
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2234,6 +2246,7 @@ describe('Integration', () => {
|
||||||
|
|
||||||
expectEvents(recordedEvents, [
|
expectEvents(recordedEvents, [
|
||||||
[NavigationStart, '/lazyFalse/loaded'],
|
[NavigationStart, '/lazyFalse/loaded'],
|
||||||
|
// [GuardsCheckStart, '/lazyFalse/loaded'],
|
||||||
[NavigationCancel, '/lazyFalse/loaded'],
|
[NavigationCancel, '/lazyFalse/loaded'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -2250,6 +2263,10 @@ describe('Integration', () => {
|
||||||
[RouteConfigLoadStart],
|
[RouteConfigLoadStart],
|
||||||
[RouteConfigLoadEnd],
|
[RouteConfigLoadEnd],
|
||||||
[RoutesRecognized, '/lazyTrue/loaded'],
|
[RoutesRecognized, '/lazyTrue/loaded'],
|
||||||
|
[GuardsCheckStart, '/lazyTrue/loaded'],
|
||||||
|
[GuardsCheckEnd, '/lazyTrue/loaded'],
|
||||||
|
[ResolveStart, '/lazyTrue/loaded'],
|
||||||
|
[ResolveEnd, '/lazyTrue/loaded'],
|
||||||
[NavigationEnd, '/lazyTrue/loaded'],
|
[NavigationEnd, '/lazyTrue/loaded'],
|
||||||
]);
|
]);
|
||||||
})));
|
})));
|
||||||
|
@ -2274,8 +2291,14 @@ describe('Integration', () => {
|
||||||
expect(location.path()).toEqual('/blank');
|
expect(location.path()).toEqual('/blank');
|
||||||
|
|
||||||
expectEvents(recordedEvents, [
|
expectEvents(recordedEvents, [
|
||||||
[NavigationStart, '/lazyFalse/loaded'], [NavigationCancel, '/lazyFalse/loaded'],
|
[NavigationStart, '/lazyFalse/loaded'],
|
||||||
[NavigationStart, '/blank'], [RoutesRecognized, '/blank'], [NavigationEnd, '/blank']
|
// No GuardCheck events as `canLoad` is a special guard that's not actually part of the
|
||||||
|
// guard lifecycle.
|
||||||
|
[NavigationCancel, '/lazyFalse/loaded'],
|
||||||
|
|
||||||
|
[NavigationStart, '/blank'], [RoutesRecognized, '/blank'],
|
||||||
|
[GuardsCheckStart, '/blank'], [GuardsCheckEnd, '/blank'], [ResolveStart, '/blank'],
|
||||||
|
[ResolveEnd, '/blank'], [NavigationEnd, '/blank']
|
||||||
]);
|
]);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
@ -3174,6 +3197,8 @@ describe('Integration', () => {
|
||||||
expect(location.path()).toEqual('/include/user/kate');
|
expect(location.path()).toEqual('/include/user/kate');
|
||||||
expectEvents(events, [
|
expectEvents(events, [
|
||||||
[NavigationStart, '/include/user/kate'], [RoutesRecognized, '/include/user/kate'],
|
[NavigationStart, '/include/user/kate'], [RoutesRecognized, '/include/user/kate'],
|
||||||
|
[GuardsCheckStart, '/include/user/kate'], [GuardsCheckEnd, '/include/user/kate'],
|
||||||
|
[ResolveStart, '/include/user/kate'], [ResolveEnd, '/include/user/kate'],
|
||||||
[NavigationEnd, '/include/user/kate']
|
[NavigationEnd, '/include/user/kate']
|
||||||
]);
|
]);
|
||||||
expect(fixture.nativeElement).toHaveText('team [ user kate, right: ]');
|
expect(fixture.nativeElement).toHaveText('team [ user kate, right: ]');
|
||||||
|
@ -3186,8 +3211,10 @@ describe('Integration', () => {
|
||||||
expect(location.path()).toEqual('/exclude/one');
|
expect(location.path()).toEqual('/exclude/one');
|
||||||
expect(Object.keys(router.routerState.root.children).length).toEqual(0);
|
expect(Object.keys(router.routerState.root.children).length).toEqual(0);
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
expectEvents(
|
expectEvents(events, [
|
||||||
events, [[NavigationStart, '/exclude/one'], [NavigationEnd, '/exclude/one']]);
|
[NavigationStart, '/exclude/one'], [GuardsCheckStart, '/exclude/one'],
|
||||||
|
[GuardsCheckEnd, '/exclude/one'], [NavigationEnd, '/exclude/one']
|
||||||
|
]);
|
||||||
events.splice(0);
|
events.splice(0);
|
||||||
|
|
||||||
// another unsupported URL
|
// another unsupported URL
|
||||||
|
@ -3206,6 +3233,8 @@ describe('Integration', () => {
|
||||||
|
|
||||||
expectEvents(events, [
|
expectEvents(events, [
|
||||||
[NavigationStart, '/include/simple'], [RoutesRecognized, '/include/simple'],
|
[NavigationStart, '/include/simple'], [RoutesRecognized, '/include/simple'],
|
||||||
|
[GuardsCheckStart, '/include/simple'], [GuardsCheckEnd, '/include/simple'],
|
||||||
|
[ResolveStart, '/include/simple'], [ResolveEnd, '/include/simple'],
|
||||||
[NavigationEnd, '/include/simple']
|
[NavigationEnd, '/include/simple']
|
||||||
]);
|
]);
|
||||||
})));
|
})));
|
||||||
|
@ -3231,6 +3260,8 @@ describe('Integration', () => {
|
||||||
expect(location.path()).toEqual('/include/user/kate(aux:excluded)');
|
expect(location.path()).toEqual('/include/user/kate(aux:excluded)');
|
||||||
expectEvents(events, [
|
expectEvents(events, [
|
||||||
[NavigationStart, '/include/user/kate'], [RoutesRecognized, '/include/user/kate'],
|
[NavigationStart, '/include/user/kate'], [RoutesRecognized, '/include/user/kate'],
|
||||||
|
[GuardsCheckStart, '/include/user/kate'], [GuardsCheckEnd, '/include/user/kate'],
|
||||||
|
[ResolveStart, '/include/user/kate'], [ResolveEnd, '/include/user/kate'],
|
||||||
[NavigationEnd, '/include/user/kate']
|
[NavigationEnd, '/include/user/kate']
|
||||||
]);
|
]);
|
||||||
events.splice(0);
|
events.splice(0);
|
||||||
|
@ -3245,6 +3276,8 @@ describe('Integration', () => {
|
||||||
expect(location.path()).toEqual('/include/simple(aux:excluded2)');
|
expect(location.path()).toEqual('/include/simple(aux:excluded2)');
|
||||||
expectEvents(events, [
|
expectEvents(events, [
|
||||||
[NavigationStart, '/include/simple'], [RoutesRecognized, '/include/simple'],
|
[NavigationStart, '/include/simple'], [RoutesRecognized, '/include/simple'],
|
||||||
|
[GuardsCheckStart, '/include/simple'], [GuardsCheckEnd, '/include/simple'],
|
||||||
|
[ResolveStart, '/include/simple'], [ResolveEnd, '/include/simple'],
|
||||||
[NavigationEnd, '/include/simple']
|
[NavigationEnd, '/include/simple']
|
||||||
]);
|
]);
|
||||||
})));
|
})));
|
||||||
|
|
|
@ -87,7 +87,7 @@ export declare class DefaultUrlSerializer implements UrlSerializer {
|
||||||
export declare type DetachedRouteHandle = {};
|
export declare type DetachedRouteHandle = {};
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError | RoutesRecognized | RouteConfigLoadStart | RouteConfigLoadEnd;
|
export declare type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError | RoutesRecognized | RouteConfigLoadStart | RouteConfigLoadEnd | GuardsCheckStart | GuardsCheckEnd | ResolveStart | ResolveEnd;
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export interface ExtraOptions {
|
export interface ExtraOptions {
|
||||||
|
@ -98,6 +98,36 @@ export interface ExtraOptions {
|
||||||
useHash?: boolean;
|
useHash?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @experimental */
|
||||||
|
export declare class GuardsCheckEnd {
|
||||||
|
id: number;
|
||||||
|
shouldActivate: boolean;
|
||||||
|
state: RouterStateSnapshot;
|
||||||
|
url: string;
|
||||||
|
urlAfterRedirects: string;
|
||||||
|
constructor(
|
||||||
|
id: number,
|
||||||
|
url: string,
|
||||||
|
urlAfterRedirects: string,
|
||||||
|
state: RouterStateSnapshot,
|
||||||
|
shouldActivate: boolean);
|
||||||
|
toString(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @experimental */
|
||||||
|
export declare class GuardsCheckStart {
|
||||||
|
id: number;
|
||||||
|
state: RouterStateSnapshot;
|
||||||
|
url: string;
|
||||||
|
urlAfterRedirects: string;
|
||||||
|
constructor(
|
||||||
|
id: number,
|
||||||
|
url: string,
|
||||||
|
urlAfterRedirects: string,
|
||||||
|
state: RouterStateSnapshot);
|
||||||
|
toString(): string;
|
||||||
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare type LoadChildren = string | LoadChildrenCallback;
|
export declare type LoadChildren = string | LoadChildrenCallback;
|
||||||
|
|
||||||
|
@ -215,6 +245,34 @@ export declare type ResolveData = {
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @experimental */
|
||||||
|
export declare class ResolveEnd {
|
||||||
|
id: number;
|
||||||
|
state: RouterStateSnapshot;
|
||||||
|
url: string;
|
||||||
|
urlAfterRedirects: string;
|
||||||
|
constructor(
|
||||||
|
id: number,
|
||||||
|
url: string,
|
||||||
|
urlAfterRedirects: string,
|
||||||
|
state: RouterStateSnapshot);
|
||||||
|
toString(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @experimental */
|
||||||
|
export declare class ResolveStart {
|
||||||
|
id: number;
|
||||||
|
state: RouterStateSnapshot;
|
||||||
|
url: string;
|
||||||
|
urlAfterRedirects: string;
|
||||||
|
constructor(
|
||||||
|
id: number,
|
||||||
|
url: string,
|
||||||
|
urlAfterRedirects: string,
|
||||||
|
state: RouterStateSnapshot);
|
||||||
|
toString(): string;
|
||||||
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export interface Route {
|
export interface Route {
|
||||||
canActivate?: any[];
|
canActivate?: any[];
|
||||||
|
|
Loading…
Reference in New Issue