feat(router): add "paramsInheritanceStrategy" router configuration option
Previously, the router would merge path and matrix params, as well as data/resolve, with special rules (only merging down when the route has an empty path, or is component-less). This change adds an extra option "paramsInheritanceStrategy" which, when set to 'always', makes child routes unconditionally inherit params from parent routes. Closes #20572.
This commit is contained in:
parent
5f23a1223f
commit
5efea2f6a0
@ -21,7 +21,7 @@ import {reduce} from 'rxjs/operator/reduce';
|
|||||||
import {LoadedRouterConfig, ResolveData, RunGuardsAndResolvers} from './config';
|
import {LoadedRouterConfig, ResolveData, RunGuardsAndResolvers} from './config';
|
||||||
import {ActivationStart, ChildActivationStart, Event} from './events';
|
import {ActivationStart, ChildActivationStart, Event} from './events';
|
||||||
import {ChildrenOutletContexts, OutletContext} from './router_outlet_context';
|
import {ChildrenOutletContexts, OutletContext} from './router_outlet_context';
|
||||||
import {ActivatedRouteSnapshot, RouterStateSnapshot, equalParamsAndUrlSegments, inheritedParamsDataResolve} from './router_state';
|
import {ActivatedRouteSnapshot, ParamsInheritanceStrategy, RouterStateSnapshot, equalParamsAndUrlSegments, inheritedParamsDataResolve} from './router_state';
|
||||||
import {andObservables, forEach, shallowEqual, wrapIntoObservable} from './utils/collection';
|
import {andObservables, forEach, shallowEqual, wrapIntoObservable} from './utils/collection';
|
||||||
import {TreeNode, nodeChildrenAsMap} from './utils/tree';
|
import {TreeNode, nodeChildrenAsMap} from './utils/tree';
|
||||||
|
|
||||||
@ -63,11 +63,11 @@ export class PreActivation {
|
|||||||
(canDeactivate: boolean) => canDeactivate ? this.runCanActivateChecks() : of (false));
|
(canDeactivate: boolean) => canDeactivate ? this.runCanActivateChecks() : of (false));
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveData(): Observable<any> {
|
resolveData(paramsInheritanceStrategy: ParamsInheritanceStrategy): Observable<any> {
|
||||||
if (!this.isActivating()) 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(
|
||||||
concatMap.call(checks$, (check: CanActivate) => this.runResolve(check.route));
|
checks$, (check: CanActivate) => this.runResolve(check.route, paramsInheritanceStrategy));
|
||||||
return reduce.call(runningChecks$, (_: any, __: any) => _);
|
return reduce.call(runningChecks$, (_: any, __: any) => _);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,11 +306,14 @@ export class PreActivation {
|
|||||||
return every.call(canDeactivate$, (result: any) => result === true);
|
return every.call(canDeactivate$, (result: any) => result === true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private runResolve(future: ActivatedRouteSnapshot): Observable<any> {
|
private runResolve(
|
||||||
|
future: ActivatedRouteSnapshot,
|
||||||
|
paramsInheritanceStrategy: ParamsInheritanceStrategy): Observable<any> {
|
||||||
const resolve = future._resolve;
|
const resolve = future._resolve;
|
||||||
return map.call(this.resolveNode(resolve, future), (resolvedData: any): any => {
|
return map.call(this.resolveNode(resolve, future), (resolvedData: any): any => {
|
||||||
future._resolvedData = resolvedData;
|
future._resolvedData = resolvedData;
|
||||||
future.data = {...future.data, ...inheritedParamsDataResolve(future).resolve};
|
future.data = {...future.data,
|
||||||
|
...inheritedParamsDataResolve(future, paramsInheritanceStrategy).resolve};
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import {Observer} from 'rxjs/Observer';
|
|||||||
import {of } from 'rxjs/observable/of';
|
import {of } from 'rxjs/observable/of';
|
||||||
|
|
||||||
import {Data, ResolveData, Route, Routes} from './config';
|
import {Data, ResolveData, Route, Routes} from './config';
|
||||||
import {ActivatedRouteSnapshot, RouterStateSnapshot, inheritedParamsDataResolve} from './router_state';
|
import {ActivatedRouteSnapshot, ParamsInheritanceStrategy, RouterStateSnapshot, inheritedParamsDataResolve} from './router_state';
|
||||||
import {PRIMARY_OUTLET, defaultUrlMatcher} from './shared';
|
import {PRIMARY_OUTLET, defaultUrlMatcher} from './shared';
|
||||||
import {UrlSegment, UrlSegmentGroup, UrlTree, mapChildrenIntoArray} from './url_tree';
|
import {UrlSegment, UrlSegmentGroup, UrlTree, mapChildrenIntoArray} from './url_tree';
|
||||||
import {forEach, last} from './utils/collection';
|
import {forEach, last} from './utils/collection';
|
||||||
@ -21,15 +21,17 @@ import {TreeNode} from './utils/tree';
|
|||||||
class NoMatch {}
|
class NoMatch {}
|
||||||
|
|
||||||
export function recognize(
|
export function recognize(
|
||||||
rootComponentType: Type<any>| null, config: Routes, urlTree: UrlTree,
|
rootComponentType: Type<any>| null, config: Routes, urlTree: UrlTree, url: string,
|
||||||
url: string): Observable<RouterStateSnapshot> {
|
paramsInheritanceStrategy: ParamsInheritanceStrategy =
|
||||||
return new Recognizer(rootComponentType, config, urlTree, url).recognize();
|
'emptyOnly'): Observable<RouterStateSnapshot> {
|
||||||
|
return new Recognizer(rootComponentType, config, urlTree, url, paramsInheritanceStrategy)
|
||||||
|
.recognize();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Recognizer {
|
class Recognizer {
|
||||||
constructor(
|
constructor(
|
||||||
private rootComponentType: Type<any>|null, private config: Routes, private urlTree: UrlTree,
|
private rootComponentType: Type<any>|null, private config: Routes, private urlTree: UrlTree,
|
||||||
private url: string) {}
|
private url: string, private paramsInheritanceStrategy: ParamsInheritanceStrategy) {}
|
||||||
|
|
||||||
recognize(): Observable<RouterStateSnapshot> {
|
recognize(): Observable<RouterStateSnapshot> {
|
||||||
try {
|
try {
|
||||||
@ -55,7 +57,7 @@ class Recognizer {
|
|||||||
inheritParamsAndData(routeNode: TreeNode<ActivatedRouteSnapshot>): void {
|
inheritParamsAndData(routeNode: TreeNode<ActivatedRouteSnapshot>): void {
|
||||||
const route = routeNode.value;
|
const route = routeNode.value;
|
||||||
|
|
||||||
const i = inheritedParamsDataResolve(route);
|
const i = inheritedParamsDataResolve(route, this.paramsInheritanceStrategy);
|
||||||
route.params = Object.freeze(i.params);
|
route.params = Object.freeze(i.params);
|
||||||
route.data = Object.freeze(i.data);
|
route.data = Object.freeze(i.data);
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ 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';
|
||||||
import {ChildrenOutletContexts} from './router_outlet_context';
|
import {ChildrenOutletContexts} from './router_outlet_context';
|
||||||
import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot, advanceActivatedRoute, createEmptyState} from './router_state';
|
import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot, advanceActivatedRoute, createEmptyState, inheritedParamsDataResolve} from './router_state';
|
||||||
import {Params, isNavigationCancelingError} from './shared';
|
import {Params, isNavigationCancelingError} from './shared';
|
||||||
import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy';
|
import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy';
|
||||||
import {UrlSerializer, UrlTree, containsTree, createEmptyUrlTree} from './url_tree';
|
import {UrlSerializer, UrlTree, containsTree, createEmptyUrlTree} from './url_tree';
|
||||||
@ -249,6 +249,16 @@ export class Router {
|
|||||||
*/
|
*/
|
||||||
onSameUrlNavigation: 'reload'|'ignore' = 'ignore';
|
onSameUrlNavigation: 'reload'|'ignore' = 'ignore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines how the router merges params, data and resolved data from parent to child
|
||||||
|
* routes. Available options are:
|
||||||
|
*
|
||||||
|
* - `'emptyOnly'`, the default, only inherits parent params for path-less or component-less
|
||||||
|
* routes.
|
||||||
|
* - `'always'`, enables unconditional inheritance of parent params.
|
||||||
|
*/
|
||||||
|
paramsInheritanceStrategy: 'emptyOnly'|'always' = 'emptyOnly';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the router service.
|
* Creates the router service.
|
||||||
*/
|
*/
|
||||||
@ -611,7 +621,8 @@ export class Router {
|
|||||||
urlAndSnapshot$ = mergeMap.call(redirectsApplied$, (appliedUrl: UrlTree) => {
|
urlAndSnapshot$ = mergeMap.call(redirectsApplied$, (appliedUrl: UrlTree) => {
|
||||||
return map.call(
|
return map.call(
|
||||||
recognize(
|
recognize(
|
||||||
this.rootComponentType, this.config, appliedUrl, this.serializeUrl(appliedUrl)),
|
this.rootComponentType, this.config, appliedUrl, this.serializeUrl(appliedUrl),
|
||||||
|
this.paramsInheritanceStrategy),
|
||||||
(snapshot: any) => {
|
(snapshot: any) => {
|
||||||
|
|
||||||
(this.events as Subject<Event>)
|
(this.events as Subject<Event>)
|
||||||
@ -667,7 +678,7 @@ export class Router {
|
|||||||
if (p.shouldActivate && preActivation.isActivating()) {
|
if (p.shouldActivate && preActivation.isActivating()) {
|
||||||
this.triggerEvent(
|
this.triggerEvent(
|
||||||
new ResolveStart(id, this.serializeUrl(url), p.appliedUrl, p.snapshot));
|
new ResolveStart(id, this.serializeUrl(url), p.appliedUrl, p.snapshot));
|
||||||
return map.call(preActivation.resolveData(), () => {
|
return map.call(preActivation.resolveData(this.paramsInheritanceStrategy), () => {
|
||||||
this.triggerEvent(
|
this.triggerEvent(
|
||||||
new ResolveEnd(id, this.serializeUrl(url), p.appliedUrl, p.snapshot));
|
new ResolveEnd(id, this.serializeUrl(url), p.appliedUrl, p.snapshot));
|
||||||
return p;
|
return p;
|
||||||
|
@ -278,6 +278,16 @@ export interface ExtraOptions {
|
|||||||
* current URL. Default is 'ignore'.
|
* current URL. Default is 'ignore'.
|
||||||
*/
|
*/
|
||||||
onSameUrlNavigation?: 'reload'|'ignore';
|
onSameUrlNavigation?: 'reload'|'ignore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines how the router merges params, data and resolved data from parent to child
|
||||||
|
* routes. Available options are:
|
||||||
|
*
|
||||||
|
* - `'emptyOnly'`, the default, only inherits parent params for path-less or component-less
|
||||||
|
* routes.
|
||||||
|
* - `'always'`, enables unconditional inheritance of parent params.
|
||||||
|
*/
|
||||||
|
paramsInheritanceStrategy?: 'emptyOnly'|'always';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setupRouter(
|
export function setupRouter(
|
||||||
@ -314,6 +324,10 @@ export function setupRouter(
|
|||||||
router.onSameUrlNavigation = opts.onSameUrlNavigation;
|
router.onSameUrlNavigation = opts.onSameUrlNavigation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.paramsInheritanceStrategy) {
|
||||||
|
router.paramsInheritanceStrategy = opts.paramsInheritanceStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import {shallowEqual, shallowEqualArrays} from './utils/collection';
|
|||||||
import {Tree, TreeNode} from './utils/tree';
|
import {Tree, TreeNode} from './utils/tree';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @whatItDoes Represents the state of the router.
|
* @whatItDoes Represents the state of the router.
|
||||||
*
|
*
|
||||||
@ -174,6 +175,9 @@ export class ActivatedRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export type ParamsInheritanceStrategy = 'emptyOnly' | 'always';
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export type Inherited = {
|
export type Inherited = {
|
||||||
params: Params,
|
params: Params,
|
||||||
@ -181,29 +185,43 @@ export type Inherited = {
|
|||||||
resolve: Data,
|
resolve: Data,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @internal */
|
/**
|
||||||
export function inheritedParamsDataResolve(route: ActivatedRouteSnapshot): Inherited {
|
* Returns the inherited params, data, and resolve for a given route.
|
||||||
const pathToRoot = route.pathFromRoot;
|
* By default, this only inherits values up to the nearest path-less or component-less route.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function inheritedParamsDataResolve(
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
paramsInheritanceStrategy: ParamsInheritanceStrategy = 'emptyOnly'): Inherited {
|
||||||
|
const pathFromRoot = route.pathFromRoot;
|
||||||
|
|
||||||
let inhertingStartingFrom = pathToRoot.length - 1;
|
let inheritingStartingFrom = 0;
|
||||||
|
if (paramsInheritanceStrategy !== 'always') {
|
||||||
|
inheritingStartingFrom = pathFromRoot.length - 1;
|
||||||
|
|
||||||
while (inhertingStartingFrom >= 1) {
|
while (inheritingStartingFrom >= 1) {
|
||||||
const current = pathToRoot[inhertingStartingFrom];
|
const current = pathFromRoot[inheritingStartingFrom];
|
||||||
const parent = pathToRoot[inhertingStartingFrom - 1];
|
const parent = pathFromRoot[inheritingStartingFrom - 1];
|
||||||
// current route is an empty path => inherits its parent's params and data
|
// current route is an empty path => inherits its parent's params and data
|
||||||
if (current.routeConfig && current.routeConfig.path === '') {
|
if (current.routeConfig && current.routeConfig.path === '') {
|
||||||
inhertingStartingFrom--;
|
inheritingStartingFrom--;
|
||||||
|
|
||||||
// parent is componentless => current route should inherit its params and data
|
// parent is componentless => current route should inherit its params and data
|
||||||
} else if (!parent.component) {
|
} else if (!parent.component) {
|
||||||
inhertingStartingFrom--;
|
inheritingStartingFrom--;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathToRoot.slice(inhertingStartingFrom).reduce((res, curr) => {
|
return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
function flattenInherited(pathFromRoot: ActivatedRouteSnapshot[]): Inherited {
|
||||||
|
return pathFromRoot.reduce((res, curr) => {
|
||||||
const params = {...res.params, ...curr.params};
|
const params = {...res.params, ...curr.params};
|
||||||
const data = {...res.data, ...curr.data};
|
const data = {...res.data, ...curr.data};
|
||||||
const resolve = {...res.resolve, ...curr._resolvedData};
|
const resolve = {...res.resolve, ...curr._resolvedData};
|
||||||
@ -352,7 +370,7 @@ function setRouterState<U, T extends{_routerState: U}>(state: U, node: TreeNode<
|
|||||||
}
|
}
|
||||||
|
|
||||||
function serializeNode(node: TreeNode<ActivatedRouteSnapshot>): string {
|
function serializeNode(node: TreeNode<ActivatedRouteSnapshot>): string {
|
||||||
const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(", ")} } ` : '';
|
const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';
|
||||||
return `${node.value}${c}`;
|
return `${node.value}${c}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3794,6 +3794,19 @@ describe('Integration', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Testing router options', () => {
|
||||||
|
describe('paramsInheritanceStrategy', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{imports: [RouterTestingModule.withRoutes([], {paramsInheritanceStrategy: 'always'})]});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should configure the router', fakeAsync(inject([Router], (router: Router) => {
|
||||||
|
expect(router.paramsInheritanceStrategy).toEqual('always');
|
||||||
|
})));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function expectEvents(events: Event[], pairs: any[]) {
|
function expectEvents(events: Event[], pairs: any[]) {
|
||||||
expect(events.length).toEqual(pairs.length);
|
expect(events.length).toEqual(pairs.length);
|
||||||
for (let i = 0; i < events.length; ++i) {
|
for (let i = 0; i < events.length; ++i) {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Routes} from '../src/config';
|
import {Routes} from '../src/config';
|
||||||
import {recognize} from '../src/recognize';
|
import {recognize} from '../src/recognize';
|
||||||
import {ActivatedRouteSnapshot, RouterStateSnapshot} from '../src/router_state';
|
import {ActivatedRouteSnapshot, ParamsInheritanceStrategy, RouterStateSnapshot, inheritedParamsDataResolve} from '../src/router_state';
|
||||||
import {PRIMARY_OUTLET, Params} from '../src/shared';
|
import {PRIMARY_OUTLET, Params} from '../src/shared';
|
||||||
import {DefaultUrlSerializer, UrlTree} from '../src/url_tree';
|
import {DefaultUrlSerializer, UrlTree} from '../src/url_tree';
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ describe('recognize', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should merge componentless route\'s data', () => {
|
it('should inherit componentless route\'s data', () => {
|
||||||
checkRecognize(
|
checkRecognize(
|
||||||
[{
|
[{
|
||||||
path: 'a',
|
path: 'a',
|
||||||
@ -214,6 +214,34 @@ describe('recognize', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not inherit route\'s data if it has component', () => {
|
||||||
|
checkRecognize(
|
||||||
|
[{
|
||||||
|
path: 'a',
|
||||||
|
component: ComponentA,
|
||||||
|
data: {one: 1},
|
||||||
|
children: [{path: 'b', data: {two: 2}, component: ComponentB}]
|
||||||
|
}],
|
||||||
|
'a/b', (s: RouterStateSnapshot) => {
|
||||||
|
const r: ActivatedRouteSnapshot = s.firstChild(<any>s.firstChild(s.root)) !;
|
||||||
|
expect(r.data).toEqual({two: 2});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inherit route\'s data if paramsInheritanceStrategy is \'always\'', () => {
|
||||||
|
checkRecognize(
|
||||||
|
[{
|
||||||
|
path: 'a',
|
||||||
|
component: ComponentA,
|
||||||
|
data: {one: 1},
|
||||||
|
children: [{path: 'b', data: {two: 2}, component: ComponentB}]
|
||||||
|
}],
|
||||||
|
'a/b', (s: RouterStateSnapshot) => {
|
||||||
|
const r: ActivatedRouteSnapshot = s.firstChild(<any>s.firstChild(s.root)) !;
|
||||||
|
expect(r.data).toEqual({one: 1, two: 2});
|
||||||
|
}, 'always');
|
||||||
|
});
|
||||||
|
|
||||||
it('should set resolved data', () => {
|
it('should set resolved data', () => {
|
||||||
checkRecognize(
|
checkRecognize(
|
||||||
[{path: 'a', resolve: {one: 'some-token'}, component: ComponentA}], 'a',
|
[{path: 'a', resolve: {one: 'some-token'}, component: ComponentA}], 'a',
|
||||||
@ -307,7 +335,7 @@ describe('recognize', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match (non-termianl) when both primary and secondary and primary has a child',
|
it('should match (non-terminal) when both primary and secondary and primary has a child',
|
||||||
() => {
|
() => {
|
||||||
const config = [{
|
const config = [{
|
||||||
path: 'parent',
|
path: 'parent',
|
||||||
@ -579,7 +607,7 @@ describe('recognize', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should merge params until encounters a normal route', () => {
|
it('should inherit params until encounters a normal route', () => {
|
||||||
checkRecognize(
|
checkRecognize(
|
||||||
[{
|
[{
|
||||||
path: 'p/:id',
|
path: 'p/:id',
|
||||||
@ -606,6 +634,25 @@ describe('recognize', () => {
|
|||||||
checkActivatedRoute(c, 'c', {}, ComponentC);
|
checkActivatedRoute(c, 'c', {}, ComponentC);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should inherit all params if paramsInheritanceStrategy is \'always\'', () => {
|
||||||
|
checkRecognize(
|
||||||
|
[{
|
||||||
|
path: 'p/:id',
|
||||||
|
children: [{
|
||||||
|
path: 'a/:name',
|
||||||
|
children: [{
|
||||||
|
path: 'b',
|
||||||
|
component: ComponentB,
|
||||||
|
children: [{path: 'c', component: ComponentC}]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
'p/11/a/victor/b/c', (s: RouterStateSnapshot) => {
|
||||||
|
const c = s.firstChild(s.firstChild(s.firstChild(s.firstChild(s.root) !) !) !) !;
|
||||||
|
checkActivatedRoute(c, 'c', {id: '11', name: 'victor'}, ComponentC);
|
||||||
|
}, 'always');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('empty URL leftovers', () => {
|
describe('empty URL leftovers', () => {
|
||||||
@ -722,8 +769,11 @@ describe('recognize', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkRecognize(config: Routes, url: string, callback: any): void {
|
function checkRecognize(
|
||||||
recognize(RootComponent, config, tree(url), url).subscribe(callback, e => { throw e; });
|
config: Routes, url: string, callback: any,
|
||||||
|
paramsInheritanceStrategy?: ParamsInheritanceStrategy): void {
|
||||||
|
recognize(RootComponent, config, tree(url), url, paramsInheritanceStrategy)
|
||||||
|
.subscribe(callback, e => { throw e; });
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkActivatedRoute(
|
function checkActivatedRoute(
|
||||||
|
@ -498,7 +498,7 @@ function checkResolveData(
|
|||||||
future: RouterStateSnapshot, curr: RouterStateSnapshot, injector: any, check: any): void {
|
future: RouterStateSnapshot, curr: RouterStateSnapshot, injector: any, check: any): void {
|
||||||
const p = new PreActivation(future, curr, injector);
|
const p = new PreActivation(future, curr, injector);
|
||||||
p.initialize(new ChildrenOutletContexts());
|
p.initialize(new ChildrenOutletContexts());
|
||||||
p.resolveData().subscribe(check, (e) => { throw e; });
|
p.resolveData('emptyOnly').subscribe(check, (e) => { throw e; });
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkGuards(
|
function checkGuards(
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {Location, LocationStrategy} from '@angular/common';
|
import {Location, LocationStrategy} from '@angular/common';
|
||||||
import {MockLocationStrategy, SpyLocation} from '@angular/common/testing';
|
import {MockLocationStrategy, SpyLocation} from '@angular/common/testing';
|
||||||
import {Compiler, Injectable, Injector, ModuleWithProviders, NgModule, NgModuleFactory, NgModuleFactoryLoader, Optional} from '@angular/core';
|
import {Compiler, Injectable, Injector, ModuleWithProviders, NgModule, NgModuleFactory, NgModuleFactoryLoader, Optional} from '@angular/core';
|
||||||
import {ChildrenOutletContexts, NoPreloading, PreloadingStrategy, ROUTES, Route, Router, RouterModule, Routes, UrlHandlingStrategy, UrlSerializer, provideRoutes, ɵROUTER_PROVIDERS as ROUTER_PROVIDERS, ɵflatten as flatten} from '@angular/router';
|
import {ChildrenOutletContexts, ExtraOptions, NoPreloading, PreloadingStrategy, ROUTER_CONFIGURATION, ROUTES, Route, Router, RouterModule, Routes, UrlHandlingStrategy, UrlSerializer, provideRoutes, ɵROUTER_PROVIDERS as ROUTER_PROVIDERS, ɵflatten as flatten} from '@angular/router';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -76,6 +76,13 @@ export class SpyNgModuleFactoryLoader implements NgModuleFactoryLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUrlHandlingStrategy(opts: ExtraOptions | UrlHandlingStrategy):
|
||||||
|
opts is UrlHandlingStrategy {
|
||||||
|
// This property check is needed because UrlHandlingStrategy is an interface and doesn't exist at
|
||||||
|
// runtime.
|
||||||
|
return 'shouldProcessUrl' in opts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router setup factory function used for testing.
|
* Router setup factory function used for testing.
|
||||||
*
|
*
|
||||||
@ -84,9 +91,39 @@ export class SpyNgModuleFactoryLoader implements NgModuleFactoryLoader {
|
|||||||
export function setupTestingRouter(
|
export function setupTestingRouter(
|
||||||
urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location,
|
urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location,
|
||||||
loader: NgModuleFactoryLoader, compiler: Compiler, injector: Injector, routes: Route[][],
|
loader: NgModuleFactoryLoader, compiler: Compiler, injector: Injector, routes: Route[][],
|
||||||
urlHandlingStrategy?: UrlHandlingStrategy) {
|
opts?: ExtraOptions, urlHandlingStrategy?: UrlHandlingStrategy): Router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router setup factory function used for testing.
|
||||||
|
*
|
||||||
|
* @deprecated As of v5.2. The 2nd-to-last argument should be `ExtraOptions`, not
|
||||||
|
* `UrlHandlingStrategy`
|
||||||
|
*/
|
||||||
|
export function setupTestingRouter(
|
||||||
|
urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location,
|
||||||
|
loader: NgModuleFactoryLoader, compiler: Compiler, injector: Injector, routes: Route[][],
|
||||||
|
urlHandlingStrategy?: UrlHandlingStrategy): Router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router setup factory function used for testing.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export function setupTestingRouter(
|
||||||
|
urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location,
|
||||||
|
loader: NgModuleFactoryLoader, compiler: Compiler, injector: Injector, routes: Route[][],
|
||||||
|
opts?: ExtraOptions | UrlHandlingStrategy, urlHandlingStrategy?: UrlHandlingStrategy) {
|
||||||
const router = new Router(
|
const router = new Router(
|
||||||
null !, urlSerializer, contexts, location, injector, loader, compiler, flatten(routes));
|
null !, urlSerializer, contexts, location, injector, loader, compiler, flatten(routes));
|
||||||
|
// Handle deprecated argument ordering.
|
||||||
|
if (opts) {
|
||||||
|
if (isUrlHandlingStrategy(opts)) {
|
||||||
|
router.urlHandlingStrategy = opts;
|
||||||
|
} else if (opts.paramsInheritanceStrategy) {
|
||||||
|
router.paramsInheritanceStrategy = opts.paramsInheritanceStrategy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (urlHandlingStrategy) {
|
if (urlHandlingStrategy) {
|
||||||
router.urlHandlingStrategy = urlHandlingStrategy;
|
router.urlHandlingStrategy = urlHandlingStrategy;
|
||||||
}
|
}
|
||||||
@ -128,14 +165,20 @@ export function setupTestingRouter(
|
|||||||
useFactory: setupTestingRouter,
|
useFactory: setupTestingRouter,
|
||||||
deps: [
|
deps: [
|
||||||
UrlSerializer, ChildrenOutletContexts, Location, NgModuleFactoryLoader, Compiler, Injector,
|
UrlSerializer, ChildrenOutletContexts, Location, NgModuleFactoryLoader, Compiler, Injector,
|
||||||
ROUTES, [UrlHandlingStrategy, new Optional()]
|
ROUTES, ROUTER_CONFIGURATION, [UrlHandlingStrategy, new Optional()]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{provide: PreloadingStrategy, useExisting: NoPreloading}, provideRoutes([])
|
{provide: PreloadingStrategy, useExisting: NoPreloading}, provideRoutes([])
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class RouterTestingModule {
|
export class RouterTestingModule {
|
||||||
static withRoutes(routes: Routes): ModuleWithProviders {
|
static withRoutes(routes: Routes, config?: ExtraOptions): ModuleWithProviders {
|
||||||
return {ngModule: RouterTestingModule, providers: [provideRoutes(routes)]};
|
return {
|
||||||
|
ngModule: RouterTestingModule,
|
||||||
|
providers: [
|
||||||
|
provideRoutes(routes),
|
||||||
|
{provide: ROUTER_CONFIGURATION, useValue: config ? config : {}},
|
||||||
|
]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
tools/public_api_guard/router/router.d.ts
vendored
2
tools/public_api_guard/router/router.d.ts
vendored
@ -127,6 +127,7 @@ export interface ExtraOptions {
|
|||||||
errorHandler?: ErrorHandler;
|
errorHandler?: ErrorHandler;
|
||||||
initialNavigation?: InitialNavigation;
|
initialNavigation?: InitialNavigation;
|
||||||
onSameUrlNavigation?: 'reload' | 'ignore';
|
onSameUrlNavigation?: 'reload' | 'ignore';
|
||||||
|
paramsInheritanceStrategy?: 'emptyOnly' | 'always';
|
||||||
preloadingStrategy?: any;
|
preloadingStrategy?: any;
|
||||||
useHash?: boolean;
|
useHash?: boolean;
|
||||||
}
|
}
|
||||||
@ -329,6 +330,7 @@ export declare class Router {
|
|||||||
readonly events: Observable<Event>;
|
readonly events: Observable<Event>;
|
||||||
navigated: boolean;
|
navigated: boolean;
|
||||||
onSameUrlNavigation: 'reload' | 'ignore';
|
onSameUrlNavigation: 'reload' | 'ignore';
|
||||||
|
paramsInheritanceStrategy: 'emptyOnly' | 'always';
|
||||||
routeReuseStrategy: RouteReuseStrategy;
|
routeReuseStrategy: RouteReuseStrategy;
|
||||||
readonly routerState: RouterState;
|
readonly routerState: RouterState;
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user