feat(router): add pathParamsChange mode for runGuardsAndResolvers (#26861)
This option means guards and resolvers will ignore changes to optional parameters such as query and matrix params. When the path or any path params change, guards and resolvers will be run Related to discussion in #18253 FW-560 #resolve PR Close #26861
This commit is contained in:
parent
3da82338d1
commit
bf6ac6cef8
|
@ -37,8 +37,15 @@ import {UrlSegment, UrlSegmentGroup} from './url_tree';
|
||||||
* - `resolve` is a map of DI tokens used to look up data resolvers. See `Resolve` for more
|
* - `resolve` is a map of DI tokens used to look up data resolvers. See `Resolve` for more
|
||||||
* info.
|
* info.
|
||||||
* - `runGuardsAndResolvers` defines when guards and resolvers will be run. By default they run only
|
* - `runGuardsAndResolvers` defines when guards and resolvers will be run. By default they run only
|
||||||
* when the matrix parameters of the route change. When set to `paramsOrQueryParamsChange` they
|
* when the matrix parameters of the route change. Options include:
|
||||||
* will also run when query params change. And when set to `always`, they will run every time.
|
* - `paramsChange` (default) - Run guards and resolvers when path or matrix params change. This
|
||||||
|
* mode ignores query param changes.
|
||||||
|
* - `paramsOrQueryParamsChange` - Guards and resolvers will run when any parameters change. This
|
||||||
|
* includes path, matrix, and query params.
|
||||||
|
* - `pathParamsChange` Run guards and resolvers path or any path params change. This mode is
|
||||||
|
* useful if you want to ignore changes to all optional parameters such as query *and* matrix
|
||||||
|
* params.
|
||||||
|
* - `always` - Run guards and resolvers on every navigation.
|
||||||
* - `children` is an array of child route definitions.
|
* - `children` is an array of child route definitions.
|
||||||
* - `loadChildren` is a reference to lazy loaded child routes. See `LoadChildren` for more
|
* - `loadChildren` is a reference to lazy loaded child routes. See `LoadChildren` for more
|
||||||
* info.
|
* info.
|
||||||
|
@ -359,7 +366,8 @@ export type QueryParamsHandling = 'merge' | 'preserve' | '';
|
||||||
* See `Routes` for more details.
|
* See `Routes` for more details.
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export type RunGuardsAndResolvers = 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';
|
export type RunGuardsAndResolvers =
|
||||||
|
'pathParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See `Routes` for more details.
|
* See `Routes` for more details.
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {Injector} from '@angular/core';
|
||||||
import {LoadedRouterConfig, RunGuardsAndResolvers} from '../config';
|
import {LoadedRouterConfig, RunGuardsAndResolvers} from '../config';
|
||||||
import {ChildrenOutletContexts, OutletContext} from '../router_outlet_context';
|
import {ChildrenOutletContexts, OutletContext} from '../router_outlet_context';
|
||||||
import {ActivatedRouteSnapshot, RouterStateSnapshot, equalParamsAndUrlSegments} from '../router_state';
|
import {ActivatedRouteSnapshot, RouterStateSnapshot, equalParamsAndUrlSegments} from '../router_state';
|
||||||
|
import {equalPath} from '../url_tree';
|
||||||
import {forEach, shallowEqual} from '../utils/collection';
|
import {forEach, shallowEqual} from '../utils/collection';
|
||||||
import {TreeNode, nodeChildrenAsMap} from '../utils/tree';
|
import {TreeNode, nodeChildrenAsMap} from '../utils/tree';
|
||||||
|
|
||||||
|
@ -147,6 +148,9 @@ function shouldRunGuardsAndResolvers(
|
||||||
curr: ActivatedRouteSnapshot, future: ActivatedRouteSnapshot,
|
curr: ActivatedRouteSnapshot, future: ActivatedRouteSnapshot,
|
||||||
mode: RunGuardsAndResolvers | undefined): boolean {
|
mode: RunGuardsAndResolvers | undefined): boolean {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
case 'pathParamsChange':
|
||||||
|
return !equalPath(curr.url, future.url);
|
||||||
|
|
||||||
case 'always':
|
case 'always':
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
|
@ -2138,7 +2138,13 @@ describe('Integration', () => {
|
||||||
canActivate: ['guard'],
|
canActivate: ['guard'],
|
||||||
resolve: {data: 'resolver'}
|
resolve: {data: 'resolver'}
|
||||||
},
|
},
|
||||||
{path: 'b', component: SimpleCmp, outlet: 'right'}
|
{path: 'b', component: SimpleCmp, outlet: 'right'}, {
|
||||||
|
path: 'c/:param',
|
||||||
|
runGuardsAndResolvers,
|
||||||
|
component: RouteCmp,
|
||||||
|
canActivate: ['guard'],
|
||||||
|
resolve: {data: 'resolver'}
|
||||||
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
router.navigateByUrl('/a');
|
router.navigateByUrl('/a');
|
||||||
|
@ -2236,6 +2242,55 @@ describe('Integration', () => {
|
||||||
expect(guardRunCount).toEqual(5);
|
expect(guardRunCount).toEqual(5);
|
||||||
expect(recordedData).toEqual([{data: 0}, {data: 1}, {data: 2}, {data: 3}, {data: 4}]);
|
expect(recordedData).toEqual([{data: 0}, {data: 1}, {data: 2}, {data: 3}, {data: 4}]);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
it('should not rerun guards and resolvers', fakeAsync(inject([Router], (router: Router) => {
|
||||||
|
const fixture = configureRouter(router, 'pathParamsChange');
|
||||||
|
|
||||||
|
const cmp: RouteCmp = fixture.debugElement.children[1].componentInstance;
|
||||||
|
const recordedData: any[] = [];
|
||||||
|
cmp.route.data.subscribe((data: any) => recordedData.push(data));
|
||||||
|
|
||||||
|
// First navigation has already run
|
||||||
|
expect(guardRunCount).toEqual(1);
|
||||||
|
expect(recordedData).toEqual([{data: 0}]);
|
||||||
|
|
||||||
|
// Changing any optional params will not result in running guards or resolvers
|
||||||
|
router.navigateByUrl('/a;p=1');
|
||||||
|
advance(fixture);
|
||||||
|
expect(guardRunCount).toEqual(1);
|
||||||
|
expect(recordedData).toEqual([{data: 0}]);
|
||||||
|
|
||||||
|
router.navigateByUrl('/a;p=2');
|
||||||
|
advance(fixture);
|
||||||
|
expect(guardRunCount).toEqual(1);
|
||||||
|
expect(recordedData).toEqual([{data: 0}]);
|
||||||
|
|
||||||
|
router.navigateByUrl('/a;p=2?q=1');
|
||||||
|
advance(fixture);
|
||||||
|
expect(guardRunCount).toEqual(1);
|
||||||
|
expect(recordedData).toEqual([{data: 0}]);
|
||||||
|
|
||||||
|
router.navigateByUrl('/a;p=2(right:b)?q=1');
|
||||||
|
advance(fixture);
|
||||||
|
expect(guardRunCount).toEqual(1);
|
||||||
|
expect(recordedData).toEqual([{data: 0}]);
|
||||||
|
|
||||||
|
// Change to new route with path param should run guards and resolvers
|
||||||
|
router.navigateByUrl('/c/paramValue');
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
expect(guardRunCount).toEqual(2);
|
||||||
|
|
||||||
|
// Modifying a path param should run guards and resolvers
|
||||||
|
router.navigateByUrl('/c/paramValueChanged');
|
||||||
|
advance(fixture);
|
||||||
|
expect(guardRunCount).toEqual(3);
|
||||||
|
|
||||||
|
// Adding optional params should not cause guards/resolvers to run
|
||||||
|
router.navigateByUrl('/c/paramValueChanged;p=1?q=2');
|
||||||
|
advance(fixture);
|
||||||
|
expect(guardRunCount).toEqual(3);
|
||||||
|
})));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should wait for parent to complete', () => {
|
describe('should wait for parent to complete', () => {
|
||||||
|
|
|
@ -451,7 +451,7 @@ export declare class RoutesRecognized extends RouterEvent {
|
||||||
toString(): string;
|
toString(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare type RunGuardsAndResolvers = 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';
|
export declare type RunGuardsAndResolvers = 'pathParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';
|
||||||
|
|
||||||
export declare class Scroll {
|
export declare class Scroll {
|
||||||
readonly anchor: string | null;
|
readonly anchor: string | null;
|
||||||
|
|
Loading…
Reference in New Issue