Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): add pathParamsChange mode for runGuardsAndResolvers #26861

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/router/src/config.ts
Expand Up @@ -39,6 +39,9 @@ import {UrlSegment, UrlSegmentGroup} from './url_tree';
* - `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 set to `pathParamsChange`, guards and resolvers will run when the path or any path params
jasonaden marked this conversation as resolved.
Show resolved Hide resolved
* change. This mode is useful if you want to ignore changes to all optional parameters such as
* query *and* matrix params. This mode is the opposite of `always`.
* - `children` is an array of child route definitions.
* - `loadChildren` is a reference to lazy loaded child routes. See `LoadChildren` for more
* info.
Expand Down Expand Up @@ -359,7 +362,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.
Expand Down
4 changes: 4 additions & 0 deletions packages/router/src/utils/preactivation.ts
Expand Up @@ -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';

Expand Down Expand Up @@ -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;

Expand Down
57 changes: 56 additions & 1 deletion packages/router/test/integration.spec.ts
Expand Up @@ -2103,7 +2103,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');
Expand Down Expand Up @@ -2201,6 +2207,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', () => {
Expand Down
2 changes: 1 addition & 1 deletion tools/public_api_guard/router/router.d.ts
Expand Up @@ -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;
Expand Down