diff --git a/packages/router/src/apply_redirects.ts b/packages/router/src/apply_redirects.ts index 81e4ba36ea..c29a128cad 100644 --- a/packages/router/src/apply_redirects.ts +++ b/packages/router/src/apply_redirects.ts @@ -255,7 +255,7 @@ class ApplyRedirects { if (!matched) return noMatch(rawSegmentGroup); const rawSlicedSegments = segments.slice(lastChild); - const childConfig$ = this.getChildConfig(ngModule, route); + const childConfig$ = this.getChildConfig(ngModule, route, segments); return childConfig$.pipe(mergeMap((routerConfig: LoadedRouterConfig) => { const childModule = routerConfig.module; @@ -282,7 +282,8 @@ class ApplyRedirects { })); } - private getChildConfig(ngModule: NgModuleRef, route: Route): Observable { + private getChildConfig(ngModule: NgModuleRef, route: Route, segments: UrlSegment[]): + Observable { if (route.children) { // The children belong to the same module return of (new LoadedRouterConfig(route.children, ngModule)); @@ -294,16 +295,17 @@ class ApplyRedirects { return of (route._loadedConfig); } - return runCanLoadGuard(ngModule.injector, route).pipe(mergeMap((shouldLoad: boolean) => { - if (shouldLoad) { - return this.configLoader.load(ngModule.injector, route) - .pipe(map((cfg: LoadedRouterConfig) => { - route._loadedConfig = cfg; - return cfg; - })); - } - return canLoadFails(route); - })); + return runCanLoadGuard(ngModule.injector, route, segments) + .pipe(mergeMap((shouldLoad: boolean) => { + if (shouldLoad) { + return this.configLoader.load(ngModule.injector, route) + .pipe(map((cfg: LoadedRouterConfig) => { + route._loadedConfig = cfg; + return cfg; + })); + } + return canLoadFails(route); + })); } return of (new LoadedRouterConfig([], ngModule)); @@ -399,13 +401,15 @@ class ApplyRedirects { } } -function runCanLoadGuard(moduleInjector: Injector, route: Route): Observable { +function runCanLoadGuard( + moduleInjector: Injector, route: Route, segments: UrlSegment[]): Observable { const canLoad = route.canLoad; if (!canLoad || canLoad.length === 0) return of (true); const obs = from(canLoad).pipe(map((injectionToken: any) => { const guard = moduleInjector.get(injectionToken); - return wrapIntoObservable(guard.canLoad ? guard.canLoad(route) : guard(route)); + return wrapIntoObservable( + guard.canLoad ? guard.canLoad(route, segments) : guard(route, segments)); })); return andObservables(obs); diff --git a/packages/router/src/interfaces.ts b/packages/router/src/interfaces.ts index 1d10033c04..6de696dca4 100644 --- a/packages/router/src/interfaces.ts +++ b/packages/router/src/interfaces.ts @@ -10,6 +10,7 @@ import {Observable} from 'rxjs'; import {Route} from './config'; import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state'; +import {UrlSegment} from './url_tree'; /** @@ -316,7 +317,7 @@ export interface Resolve { * ``` * class UserToken {} * class Permissions { - * canLoadChildren(user: UserToken, id: string): boolean { + * canLoadChildren(user: UserToken, id: string, segments: UrlSegment[]): boolean { * return true; * } * } @@ -325,8 +326,8 @@ export interface Resolve { * class CanLoadTeamSection implements CanLoad { * constructor(private permissions: Permissions, private currentUser: UserToken) {} * - * canLoad(route: Route): Observable|Promise|boolean { - * return this.permissions.canLoadChildren(this.currentUser, route); + * canLoad(route: Route, segments: UrlSegment[]): Observable|Promise|boolean { + * return this.permissions.canLoadChildren(this.currentUser, route, segments); * } * } * @@ -363,7 +364,7 @@ export interface Resolve { * providers: [ * { * provide: 'canLoadTeamSection', - * useValue: (route: Route) => true + * useValue: (route: Route, segments: UrlSegment[]) => true * } * ] * }) @@ -372,4 +373,6 @@ export interface Resolve { * * */ -export interface CanLoad { canLoad(route: Route): Observable|Promise|boolean; } +export interface CanLoad { + canLoad(route: Route, segments: UrlSegment[]): Observable|Promise|boolean; +} diff --git a/packages/router/test/apply_redirects.spec.ts b/packages/router/test/apply_redirects.spec.ts index 70f9abc7f6..c22166c653 100644 --- a/packages/router/test/apply_redirects.spec.ts +++ b/packages/router/test/apply_redirects.spec.ts @@ -11,8 +11,8 @@ import {TestBed} from '@angular/core/testing'; import {Observable, of } from 'rxjs'; import {applyRedirects} from '../src/apply_redirects'; -import {LoadedRouterConfig, Routes} from '../src/config'; -import {DefaultUrlSerializer, UrlSegmentGroup, UrlTree, equalSegments} from '../src/url_tree'; +import {LoadedRouterConfig, Route, Routes} from '../src/config'; +import {DefaultUrlSerializer, UrlSegment, UrlSegmentGroup, UrlTree, equalSegments} from '../src/url_tree'; describe('applyRedirects', () => { const serializer = new DefaultUrlSerializer(); @@ -293,6 +293,62 @@ describe('applyRedirects', () => { }); + it('should pass UrlSegments to functions implementing the canLoad guard interface', () => { + const loadedConfig = new LoadedRouterConfig([{path: 'b', component: ComponentB}], testModule); + const loader = {load: (injector: any, p: any) => of (loadedConfig)}; + + let passedUrlSegments: UrlSegment[]; + + const guard = (route: Route, urlSegments: UrlSegment[]) => { + passedUrlSegments = urlSegments; + return true; + }; + const injector = {get: (token: any) => token === 'guard' ? guard : {injector}}; + + const config = + [{path: 'a', component: ComponentA, canLoad: ['guard'], loadChildren: 'children'}]; + + applyRedirects(injector, loader, serializer, tree('a/b'), config) + .subscribe( + (r) => { + expectTreeToBe(r, '/a/b'); + expect(passedUrlSegments.length).toBe(2); + expect(passedUrlSegments[0].path).toBe('a'); + expect(passedUrlSegments[1].path).toBe('b'); + }, + (e) => { throw 'Should not reach'; }); + + }); + + it('should pass UrlSegments to objects implementing the canLoad guard interface', () => { + const loadedConfig = new LoadedRouterConfig([{path: 'b', component: ComponentB}], testModule); + const loader = {load: (injector: any, p: any) => of (loadedConfig)}; + + let passedUrlSegments: UrlSegment[]; + + const guard = { + canLoad: (route: Route, urlSegments: UrlSegment[]) => { + passedUrlSegments = urlSegments; + return true; + } + }; + const injector = {get: (token: any) => token === 'guard' ? guard : {injector}}; + + const config = + [{path: 'a', component: ComponentA, canLoad: ['guard'], loadChildren: 'children'}]; + + applyRedirects(injector, loader, serializer, tree('a/b'), config) + .subscribe( + (r) => { + expectTreeToBe(r, '/a/b'); + expect(passedUrlSegments.length).toBe(2); + expect(passedUrlSegments[0].path).toBe('a'); + expect(passedUrlSegments[1].path).toBe('b'); + }, + (e) => { throw 'Should not reach'; }); + + }); + it('should work with absolute redirects', () => { const loadedConfig = new LoadedRouterConfig([{path: '', component: ComponentB}], testModule); diff --git a/tools/public_api_guard/router/router.d.ts b/tools/public_api_guard/router/router.d.ts index c97c290390..96ed908f89 100644 --- a/tools/public_api_guard/router/router.d.ts +++ b/tools/public_api_guard/router/router.d.ts @@ -66,7 +66,7 @@ export interface CanDeactivate { } export interface CanLoad { - canLoad(route: Route): Observable | Promise | boolean; + canLoad(route: Route, segments: UrlSegment[]): Observable | Promise | boolean; } /** @experimental */