Skip to content

Commit

Permalink
feat(router): add predicate function mode for runGuardsAndResolvers
Browse files Browse the repository at this point in the history
This option means guards and resolvers will ignore changes when a provided predicate function returns `false`. This supports use cases where an application needs to ignore some param updates but not others. For example, changing a sort param in the URL might need to be ignored, whereas changing the a `project` param might require re-run of guards and resolvers.

Related to angular#26861 angular#18253 angular#27464
  • Loading branch information
jasonaden committed Dec 14, 2018
1 parent ad26cd6 commit 7085b30
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 1 deletion.
10 changes: 9 additions & 1 deletion packages/router/src/config.ts
Original file line number Diff line number Diff line change
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 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` params.
* - `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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit 7085b30

Please sign in to comment.