feat(router): add UrlSegment[] to CanLoad interface (#13127)
CanLoad now defines UrlSegment[] as a second parameter of the function. Users can store the initial url segments and refer to them later, e.g. to go back to the original url after authentication via router.navigate(urlSegments). Existing code still works as before because the second function parameter does not have to be defined. Closes #12411 PR Close #13127
This commit is contained in:
parent
116946fb11
commit
07d8d3994c
|
@ -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<any>, route: Route): Observable<LoadedRouterConfig> {
|
||||
private getChildConfig(ngModule: NgModuleRef<any>, route: Route, segments: UrlSegment[]):
|
||||
Observable<LoadedRouterConfig> {
|
||||
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<boolean> {
|
||||
function runCanLoadGuard(
|
||||
moduleInjector: Injector, route: Route, segments: UrlSegment[]): Observable<boolean> {
|
||||
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);
|
||||
|
|
|
@ -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<T> {
|
|||
* ```
|
||||
* 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<T> {
|
|||
* class CanLoadTeamSection implements CanLoad {
|
||||
* constructor(private permissions: Permissions, private currentUser: UserToken) {}
|
||||
*
|
||||
* canLoad(route: Route): Observable<boolean>|Promise<boolean>|boolean {
|
||||
* return this.permissions.canLoadChildren(this.currentUser, route);
|
||||
* canLoad(route: Route, segments: UrlSegment[]): Observable<boolean>|Promise<boolean>|boolean {
|
||||
* return this.permissions.canLoadChildren(this.currentUser, route, segments);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
|
@ -363,7 +364,7 @@ export interface Resolve<T> {
|
|||
* providers: [
|
||||
* {
|
||||
* provide: 'canLoadTeamSection',
|
||||
* useValue: (route: Route) => true
|
||||
* useValue: (route: Route, segments: UrlSegment[]) => true
|
||||
* }
|
||||
* ]
|
||||
* })
|
||||
|
@ -372,4 +373,6 @@ export interface Resolve<T> {
|
|||
*
|
||||
*
|
||||
*/
|
||||
export interface CanLoad { canLoad(route: Route): Observable<boolean>|Promise<boolean>|boolean; }
|
||||
export interface CanLoad {
|
||||
canLoad(route: Route, segments: UrlSegment[]): Observable<boolean>|Promise<boolean>|boolean;
|
||||
}
|
||||
|
|
|
@ -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(<any>injector, <any>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(<any>injector, <any>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);
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ export interface CanDeactivate<T> {
|
|||
}
|
||||
|
||||
export interface CanLoad {
|
||||
canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean;
|
||||
canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
|
|
Loading…
Reference in New Issue