Skip to content
Permalink
Browse files

feat(router): add pathParamsChange mode for runGuardsAndResolvers (#2…

…6861)

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
  • Loading branch information...
jasonaden authored and AndrewKushnir committed Oct 30, 2018
1 parent 3da8233 commit bf6ac6cef89ed3934b211cdac9d29602e8d2726d
@@ -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
* info.
* - `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
* will also run when query params change. And when set to `always`, they will run every time.
* when the matrix parameters of the route change. Options include:
* - `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.
* - `loadChildren` is a reference to lazy loaded child routes. See `LoadChildren` for more
* info.
@@ -359,7 +366,8 @@ export type QueryParamsHandling = 'merge' | 'preserve' | '';
* See `Routes` for more details.
* @publicApi
*/
export type RunGuardsAndResolvers = 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';
export type RunGuardsAndResolvers =
'pathParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';

/**
* See `Routes` for more details.
@@ -11,6 +11,7 @@ import {Injector} from '@angular/core';
import {LoadedRouterConfig, RunGuardsAndResolvers} from '../config';
import {ChildrenOutletContexts, OutletContext} from '../router_outlet_context';
import {ActivatedRouteSnapshot, RouterStateSnapshot, equalParamsAndUrlSegments} from '../router_state';
import {equalPath} from '../url_tree';
import {forEach, shallowEqual} from '../utils/collection';
import {TreeNode, nodeChildrenAsMap} from '../utils/tree';

@@ -147,6 +148,9 @@ function shouldRunGuardsAndResolvers(
curr: ActivatedRouteSnapshot, future: ActivatedRouteSnapshot,
mode: RunGuardsAndResolvers | undefined): boolean {
switch (mode) {
case 'pathParamsChange':
return !equalPath(curr.url, future.url);

case 'always':
return true;

@@ -2138,7 +2138,13 @@ describe('Integration', () => {
canActivate: ['guard'],
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');
@@ -2236,6 +2242,55 @@ describe('Integration', () => {
expect(guardRunCount).toEqual(5);
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', () => {
@@ -451,7 +451,7 @@ export declare class RoutesRecognized extends RouterEvent {
toString(): string;
}

export declare type RunGuardsAndResolvers = 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';
export declare type RunGuardsAndResolvers = 'pathParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';

export declare class Scroll {
readonly anchor: string | null;

0 comments on commit bf6ac6c

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.