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 predicate function mode for runGuardsAndResolvers #27682

Closed
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 9 additions & 1 deletion packages/router/src/config.ts
Expand Up @@ -8,10 +8,13 @@

import {NgModuleFactory, NgModuleRef, Type} from '@angular/core';
import {Observable} from 'rxjs';

import {EmptyOutletComponent} from './components/empty_outlet';
import {ActivatedRouteSnapshot} from './router_state';
import {PRIMARY_OUTLET} from './shared';
import {UrlSegment, UrlSegmentGroup} from './url_tree';


/**
* @description
*
Expand Down Expand Up @@ -48,6 +51,10 @@ import {UrlSegment, UrlSegmentGroup} from './url_tree';
* - `pathParamsOrQueryParamsChange` - Same as `pathParamsChange`, but also rerun when any query
* param changes
* - `always` - Run guards and resolvers on every navigation.
* - (from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean - Use a predicate
* function when none of the pre-configured modes fit the needs of the application. An example
* might be when you need to ignore updates to a param such as `sortDirection`, but need to
* reload guards and resolvers when changing the `searchRoot` param.
* - `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 @@ -369,7 +376,8 @@ export type QueryParamsHandling = 'merge' | 'preserve' | '';
* @publicApi
*/
export type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' |
'paramsChange' | 'paramsOrQueryParamsChange' | 'always';
'paramsChange' | 'paramsOrQueryParamsChange' | 'always' |
((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);

/**
* See `Routes` for more details.
Expand Down
3 changes: 3 additions & 0 deletions packages/router/src/utils/preactivation.ts
Expand Up @@ -147,6 +147,9 @@ function getRouteGuards(
function shouldRunGuardsAndResolvers(
curr: ActivatedRouteSnapshot, future: ActivatedRouteSnapshot,
mode: RunGuardsAndResolvers | undefined): boolean {
if (typeof mode === 'function') {
return mode(curr, future);
}
switch (mode) {
case 'pathParamsChange':
return !equalPath(curr.url, future.url);
Expand Down
37 changes: 37 additions & 0 deletions packages/router/test/integration.spec.ts
Expand Up @@ -2533,6 +2533,43 @@ describe('Integration', () => {
expect(guardRunCount).toEqual(3);
expect(recordedData).toEqual([{data: 0}, {data: 1}, {data: 2}]);
})));

it('should allow a predicate function to determine when to run guards and resolvers',
fakeAsync(inject([Router], (router: Router) => {
const fixture = configureRouter(router, (from, to) => to.paramMap.get('p') === '2');

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}]);

// Adding `p` param shouldn't cause re-run
router.navigateByUrl('/a;p=1');
advance(fixture);
expect(guardRunCount).toEqual(1);
expect(recordedData).toEqual([{data: 0}]);

// Re-run should trigger on p=2
router.navigateByUrl('/a;p=2');
advance(fixture);
expect(guardRunCount).toEqual(2);
expect(recordedData).toEqual([{data: 0}, {data: 1}]);

// Any other changes don't pass the predicate
router.navigateByUrl('/a;p=3?q=1');
advance(fixture);
expect(guardRunCount).toEqual(2);
expect(recordedData).toEqual([{data: 0}, {data: 1}]);

// Changing query params will re-run guards/resolvers
router.navigateByUrl('/a;p=3?q=2');
advance(fixture);
expect(guardRunCount).toEqual(2);
expect(recordedData).toEqual([{data: 0}, {data: 1}]);
})));
});

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 @@ -473,7 +473,7 @@ export declare class RoutesRecognized extends RouterEvent {
toString(): string;
}

export declare type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always';
export declare type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always' | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);

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