Router Fixes (#10579)

* fix(router): copy over data during data resolution
* fix(router): components instantiated in lazy-loaded modules should use location's injector
This commit is contained in:
Victor Savkin 2016-08-10 15:53:57 -07:00 committed by vikerman
parent 9a11ec2624
commit 6db27153ef
6 changed files with 1901 additions and 1812 deletions

View File

@ -104,7 +104,6 @@ export class RouterOutlet implements OnDestroy {
} }
const injector = loadedInjector ? loadedInjector : this.location.parentInjector; const injector = loadedInjector ? loadedInjector : this.location.parentInjector;
const inj = ReflectiveInjector.fromResolvedProviders(providers, injector); const inj = ReflectiveInjector.fromResolvedProviders(providers, injector);
this.activated = this.location.createComponent(factory, this.location.length, inj, []); this.activated = this.location.createComponent(factory, this.location.length, inj, []);
this.activated.changeDetectorRef.detectChanges(); this.activated.changeDetectorRef.detectChanges();

View File

@ -447,7 +447,6 @@ export class Router {
class CanActivate { class CanActivate {
constructor(public path: ActivatedRouteSnapshot[]) {} constructor(public path: ActivatedRouteSnapshot[]) {}
get route(): ActivatedRouteSnapshot { return this.path[this.path.length - 1]; } get route(): ActivatedRouteSnapshot { return this.path[this.path.length - 1]; }
} }
@ -456,7 +455,7 @@ class CanDeactivate {
} }
class PreActivation { export class PreActivation {
private checks: Array<CanActivate|CanDeactivate> = []; private checks: Array<CanActivate|CanDeactivate> = [];
constructor( constructor(
private future: RouterStateSnapshot, private curr: RouterStateSnapshot, private future: RouterStateSnapshot, private curr: RouterStateSnapshot,
@ -504,6 +503,7 @@ class PreActivation {
futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>, futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>,
outletMap: RouterOutletMap, futurePath: ActivatedRouteSnapshot[]): void { outletMap: RouterOutletMap, futurePath: ActivatedRouteSnapshot[]): void {
const prevChildren: {[key: string]: any} = nodeChildrenAsMap(currNode); const prevChildren: {[key: string]: any} = nodeChildrenAsMap(currNode);
futureNode.children.forEach(c => { futureNode.children.forEach(c => {
this.traverseRoutes(c, prevChildren[c.value.outlet], outletMap, futurePath.concat([c.value])); this.traverseRoutes(c, prevChildren[c.value.outlet], outletMap, futurePath.concat([c.value]));
delete prevChildren[c.value.outlet]; delete prevChildren[c.value.outlet];
@ -524,6 +524,9 @@ class PreActivation {
if (curr && future._routeConfig === curr._routeConfig) { if (curr && future._routeConfig === curr._routeConfig) {
if (!shallowEqual(future.params, curr.params)) { if (!shallowEqual(future.params, curr.params)) {
this.checks.push(new CanDeactivate(outlet.component, curr), new CanActivate(futurePath)); this.checks.push(new CanDeactivate(outlet.component, curr), new CanActivate(futurePath));
} else {
// we need to set the data
future.data = curr.data;
} }
// If we have a component, we need to go through an outlet. // If we have a component, we need to go through an outlet.
@ -578,7 +581,7 @@ class PreActivation {
const canActivate = future._routeConfig ? future._routeConfig.canActivate : null; const canActivate = future._routeConfig ? future._routeConfig.canActivate : null;
if (!canActivate || canActivate.length === 0) return of (true); if (!canActivate || canActivate.length === 0) return of (true);
const obs = from(canActivate).map(c => { const obs = from(canActivate).map(c => {
const guard = this.getToken(c, future, this.future); const guard = this.getToken(c, future);
if (guard.canActivate) { if (guard.canActivate) {
return wrapIntoObservable(guard.canActivate(future, this.future)); return wrapIntoObservable(guard.canActivate(future, this.future));
} else { } else {
@ -598,7 +601,7 @@ class PreActivation {
return andObservables(from(canActivateChildGuards).map(d => { return andObservables(from(canActivateChildGuards).map(d => {
const obs = from(d.guards).map(c => { const obs = from(d.guards).map(c => {
const guard = this.getToken(c, c.node, this.future); const guard = this.getToken(c, c.node);
if (guard.canActivateChild) { if (guard.canActivateChild) {
return wrapIntoObservable(guard.canActivateChild(future, this.future)); return wrapIntoObservable(guard.canActivateChild(future, this.future));
} else { } else {
@ -621,7 +624,7 @@ class PreActivation {
if (!canDeactivate || canDeactivate.length === 0) return of (true); if (!canDeactivate || canDeactivate.length === 0) return of (true);
return from(canDeactivate) return from(canDeactivate)
.map(c => { .map(c => {
const guard = this.getToken(c, curr, this.curr); const guard = this.getToken(c, curr);
if (guard.canDeactivate) { if (guard.canDeactivate) {
return wrapIntoObservable(guard.canDeactivate(component, curr, this.curr)); return wrapIntoObservable(guard.canDeactivate(component, curr, this.curr));
} else { } else {
@ -643,14 +646,14 @@ class PreActivation {
private resolveNode(resolve: ResolveData, future: ActivatedRouteSnapshot): Observable<any> { private resolveNode(resolve: ResolveData, future: ActivatedRouteSnapshot): Observable<any> {
return waitForMap(resolve, (k, v) => { return waitForMap(resolve, (k, v) => {
const resolver = this.getToken(v, future, this.future); const resolver = this.getToken(v, future);
return resolver.resolve ? wrapIntoObservable(resolver.resolve(future, this.future)) : return resolver.resolve ? wrapIntoObservable(resolver.resolve(future, this.future)) :
wrapIntoObservable(resolver(future, this.future)); wrapIntoObservable(resolver(future, this.future));
}); });
} }
private getToken(token: any, snapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot): any { private getToken(token: any, snapshot: ActivatedRouteSnapshot): any {
const config = closestLoadedConfig(state, snapshot); const config = closestLoadedConfig(snapshot);
const injector = config ? config.injector : this.injector; const injector = config ? config.injector : this.injector;
return injector.get(token); return injector.get(token);
} }
@ -736,7 +739,8 @@ class ActivateRoutes {
useValue: outletMap useValue: outletMap
}]; }];
const config = closestLoadedConfig(this.futureState.snapshot, future.snapshot); const config = parentLoadedConfig(future.snapshot);
let loadedFactoryResolver: ComponentFactoryResolver = null; let loadedFactoryResolver: ComponentFactoryResolver = null;
let loadedInjector: Injector = null; let loadedInjector: Injector = null;
@ -744,8 +748,7 @@ class ActivateRoutes {
loadedFactoryResolver = config.factoryResolver; loadedFactoryResolver = config.factoryResolver;
loadedInjector = config.injector; loadedInjector = config.injector;
resolved.push({provide: ComponentFactoryResolver, useValue: loadedFactoryResolver}); resolved.push({provide: ComponentFactoryResolver, useValue: loadedFactoryResolver});
}; }
outlet.activate( outlet.activate(
future, loadedFactoryResolver, loadedInjector, ReflectiveInjector.resolve(resolved), future, loadedFactoryResolver, loadedInjector, ReflectiveInjector.resolve(resolved),
outletMap); outletMap);
@ -763,13 +766,27 @@ class ActivateRoutes {
} }
} }
function closestLoadedConfig( function parentLoadedConfig(snapshot: ActivatedRouteSnapshot): LoadedRouterConfig {
state: RouterStateSnapshot, snapshot: ActivatedRouteSnapshot): LoadedRouterConfig { let s = snapshot.parent;
const b = state.pathFromRoot(snapshot).filter(s => { while (s) {
const config = (<any>s)._routeConfig; const c: any = s._routeConfig;
return config && config._loadedConfig && s !== snapshot; if (c && c._loadedConfig) return c._loadedConfig;
}); if (c && c.component) return null;
return b.length > 0 ? (<any>b[b.length - 1])._routeConfig._loadedConfig : null; s = s.parent;
}
return null;
}
function closestLoadedConfig(snapshot: ActivatedRouteSnapshot): LoadedRouterConfig {
if (!snapshot) return null;
let s = snapshot.parent;
while (s) {
const c: any = s._routeConfig;
if (c && c._loadedConfig) return c._loadedConfig;
s = s.parent;
}
return null;
} }
function nodeChildrenAsMap(node: TreeNode<any>) { function nodeChildrenAsMap(node: TreeNode<any>) {

View File

@ -70,7 +70,8 @@ export function createEmptyState(urlTree: UrlTree, rootComponent: Type): RouterS
return new RouterState(new TreeNode<ActivatedRoute>(activated, []), snapshot); return new RouterState(new TreeNode<ActivatedRoute>(activated, []), snapshot);
} }
function createEmptyStateSnapshot(urlTree: UrlTree, rootComponent: Type): RouterStateSnapshot { export function createEmptyStateSnapshot(
urlTree: UrlTree, rootComponent: Type): RouterStateSnapshot {
const emptyParams = {}; const emptyParams = {};
const emptyData = {}; const emptyData = {};
const emptyQueryParams = {}; const emptyQueryParams = {};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@
"test/config.spec.ts", "test/config.spec.ts",
"test/router_state.spec.ts", "test/router_state.spec.ts",
"test/router.spec.ts", "test/router.spec.ts",
"test/integration.spec.ts",
"../../../node_modules/@types/jasmine/index.d.ts" "../../../node_modules/@types/jasmine/index.d.ts"
] ]
} }