fix: element injector vs module injector (#15044)
fixes #12869 fixes #12889 fixes #13885 fixes #13870 Before this change there was a single injector tree. Now we have 2 injector trees, one for the modules and one for the components. This fixes lazy loading modules. See the design docs for details: https://docs.google.com/document/d/1OEUIwc-s69l1o97K0wBd_-Lth5BBxir1KuCRWklTlI4 BREAKING CHANGES `ComponentFactory.create()` takes an extra optional `NgModuleRef` parameter. No change should be required in user code as the correct module will be used when none is provided DEPRECATIONS The following methods were used internally and are no more required: - `RouterOutlet.locationFactoryResolver` - `RouterOutlet.locationInjector`
This commit is contained in:

committed by
Chuck Jazdzewski

parent
f093501501
commit
13686bb518
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector} from '@angular/core';
|
||||
import {Injector, NgModuleRef} from '@angular/core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Observer} from 'rxjs/Observer';
|
||||
import {from} from 'rxjs/observable/from';
|
||||
@ -55,21 +55,24 @@ function canLoadFails(route: Route): Observable<LoadedRouterConfig> {
|
||||
}
|
||||
|
||||
export function applyRedirects(
|
||||
injector: Injector, configLoader: RouterConfigLoader, urlSerializer: UrlSerializer,
|
||||
moduleInjector: Injector, configLoader: RouterConfigLoader, urlSerializer: UrlSerializer,
|
||||
urlTree: UrlTree, config: Routes): Observable<UrlTree> {
|
||||
return new ApplyRedirects(injector, configLoader, urlSerializer, urlTree, config).apply();
|
||||
return new ApplyRedirects(moduleInjector, configLoader, urlSerializer, urlTree, config).apply();
|
||||
}
|
||||
|
||||
class ApplyRedirects {
|
||||
private allowRedirects: boolean = true;
|
||||
private ngModule: NgModuleRef<any>;
|
||||
|
||||
constructor(
|
||||
private injector: Injector, private configLoader: RouterConfigLoader,
|
||||
private urlSerializer: UrlSerializer, private urlTree: UrlTree, private config: Routes) {}
|
||||
moduleInjector: Injector, private configLoader: RouterConfigLoader,
|
||||
private urlSerializer: UrlSerializer, private urlTree: UrlTree, private config: Routes) {
|
||||
this.ngModule = moduleInjector.get(NgModuleRef);
|
||||
}
|
||||
|
||||
apply(): Observable<UrlTree> {
|
||||
const expanded$ =
|
||||
this.expandSegmentGroup(this.injector, this.config, this.urlTree.root, PRIMARY_OUTLET);
|
||||
this.expandSegmentGroup(this.ngModule, this.config, this.urlTree.root, PRIMARY_OUTLET);
|
||||
const urlTrees$ = map.call(
|
||||
expanded$, (rootSegmentGroup: UrlSegmentGroup) => this.createUrlTree(
|
||||
rootSegmentGroup, this.urlTree.queryParams, this.urlTree.fragment));
|
||||
@ -91,7 +94,7 @@ class ApplyRedirects {
|
||||
|
||||
private match(tree: UrlTree): Observable<UrlTree> {
|
||||
const expanded$ =
|
||||
this.expandSegmentGroup(this.injector, this.config, tree.root, PRIMARY_OUTLET);
|
||||
this.expandSegmentGroup(this.ngModule, this.config, tree.root, PRIMARY_OUTLET);
|
||||
const mapped$ = map.call(
|
||||
expanded$, (rootSegmentGroup: UrlSegmentGroup) =>
|
||||
this.createUrlTree(rootSegmentGroup, tree.queryParams, tree.fragment));
|
||||
@ -117,31 +120,33 @@ class ApplyRedirects {
|
||||
}
|
||||
|
||||
private expandSegmentGroup(
|
||||
injector: Injector, routes: Route[], segmentGroup: UrlSegmentGroup,
|
||||
ngModule: NgModuleRef<any>, routes: Route[], segmentGroup: UrlSegmentGroup,
|
||||
outlet: string): Observable<UrlSegmentGroup> {
|
||||
if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
|
||||
return map.call(
|
||||
this.expandChildren(injector, routes, segmentGroup),
|
||||
this.expandChildren(ngModule, routes, segmentGroup),
|
||||
(children: any) => new UrlSegmentGroup([], children));
|
||||
}
|
||||
|
||||
return this.expandSegment(injector, segmentGroup, routes, segmentGroup.segments, outlet, true);
|
||||
return this.expandSegment(ngModule, segmentGroup, routes, segmentGroup.segments, outlet, true);
|
||||
}
|
||||
|
||||
private expandChildren(injector: Injector, routes: Route[], segmentGroup: UrlSegmentGroup):
|
||||
Observable<{[name: string]: UrlSegmentGroup}> {
|
||||
private expandChildren(
|
||||
ngModule: NgModuleRef<any>, routes: Route[],
|
||||
segmentGroup: UrlSegmentGroup): Observable<{[name: string]: UrlSegmentGroup}> {
|
||||
return waitForMap(
|
||||
segmentGroup.children,
|
||||
(childOutlet, child) => this.expandSegmentGroup(injector, routes, child, childOutlet));
|
||||
(childOutlet, child) => this.expandSegmentGroup(ngModule, routes, child, childOutlet));
|
||||
}
|
||||
|
||||
private expandSegment(
|
||||
injector: Injector, segmentGroup: UrlSegmentGroup, routes: Route[], segments: UrlSegment[],
|
||||
outlet: string, allowRedirects: boolean): Observable<UrlSegmentGroup> {
|
||||
ngModule: NgModuleRef<any>, segmentGroup: UrlSegmentGroup, routes: Route[],
|
||||
segments: UrlSegment[], outlet: string,
|
||||
allowRedirects: boolean): Observable<UrlSegmentGroup> {
|
||||
const routes$ = of (...routes);
|
||||
const processedRoutes$ = map.call(routes$, (r: any) => {
|
||||
const expanded$ = this.expandSegmentAgainstRoute(
|
||||
injector, segmentGroup, routes, r, segments, outlet, allowRedirects);
|
||||
ngModule, segmentGroup, routes, r, segments, outlet, allowRedirects);
|
||||
return _catch.call(expanded$, (e: any) => {
|
||||
if (e instanceof NoMatch) {
|
||||
return of (null);
|
||||
@ -171,7 +176,7 @@ class ApplyRedirects {
|
||||
}
|
||||
|
||||
private expandSegmentAgainstRoute(
|
||||
injector: Injector, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route,
|
||||
ngModule: NgModuleRef<any>, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route,
|
||||
paths: UrlSegment[], outlet: string, allowRedirects: boolean): Observable<UrlSegmentGroup> {
|
||||
if (getOutlet(route) !== outlet) {
|
||||
return noMatch(segmentGroup);
|
||||
@ -182,27 +187,27 @@ class ApplyRedirects {
|
||||
}
|
||||
|
||||
if (route.redirectTo === undefined) {
|
||||
return this.matchSegmentAgainstRoute(injector, segmentGroup, route, paths);
|
||||
return this.matchSegmentAgainstRoute(ngModule, segmentGroup, route, paths);
|
||||
}
|
||||
|
||||
return this.expandSegmentAgainstRouteUsingRedirect(
|
||||
injector, segmentGroup, routes, route, paths, outlet);
|
||||
ngModule, segmentGroup, routes, route, paths, outlet);
|
||||
}
|
||||
|
||||
private expandSegmentAgainstRouteUsingRedirect(
|
||||
injector: Injector, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route,
|
||||
ngModule: NgModuleRef<any>, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route,
|
||||
segments: UrlSegment[], outlet: string): Observable<UrlSegmentGroup> {
|
||||
if (route.path === '**') {
|
||||
return this.expandWildCardWithParamsAgainstRouteUsingRedirect(
|
||||
injector, routes, route, outlet);
|
||||
ngModule, routes, route, outlet);
|
||||
}
|
||||
|
||||
return this.expandRegularSegmentAgainstRouteUsingRedirect(
|
||||
injector, segmentGroup, routes, route, segments, outlet);
|
||||
ngModule, segmentGroup, routes, route, segments, outlet);
|
||||
}
|
||||
|
||||
private expandWildCardWithParamsAgainstRouteUsingRedirect(
|
||||
injector: Injector, routes: Route[], route: Route,
|
||||
ngModule: NgModuleRef<any>, routes: Route[], route: Route,
|
||||
outlet: string): Observable<UrlSegmentGroup> {
|
||||
const newTree = this.applyRedirectCommands([], route.redirectTo, {});
|
||||
if (route.redirectTo.startsWith('/')) {
|
||||
@ -211,12 +216,12 @@ class ApplyRedirects {
|
||||
|
||||
return mergeMap.call(this.lineralizeSegments(route, newTree), (newSegments: UrlSegment[]) => {
|
||||
const group = new UrlSegmentGroup(newSegments, {});
|
||||
return this.expandSegment(injector, group, routes, newSegments, outlet, false);
|
||||
return this.expandSegment(ngModule, group, routes, newSegments, outlet, false);
|
||||
});
|
||||
}
|
||||
|
||||
private expandRegularSegmentAgainstRouteUsingRedirect(
|
||||
injector: Injector, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route,
|
||||
ngModule: NgModuleRef<any>, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route,
|
||||
segments: UrlSegment[], outlet: string): Observable<UrlSegmentGroup> {
|
||||
const {matched, consumedSegments, lastChild, positionalParamSegments} =
|
||||
match(segmentGroup, route, segments);
|
||||
@ -230,20 +235,21 @@ class ApplyRedirects {
|
||||
|
||||
return mergeMap.call(this.lineralizeSegments(route, newTree), (newSegments: UrlSegment[]) => {
|
||||
return this.expandSegment(
|
||||
injector, segmentGroup, routes, newSegments.concat(segments.slice(lastChild)), outlet,
|
||||
ngModule, segmentGroup, routes, newSegments.concat(segments.slice(lastChild)), outlet,
|
||||
false);
|
||||
});
|
||||
}
|
||||
|
||||
private matchSegmentAgainstRoute(
|
||||
injector: Injector, rawSegmentGroup: UrlSegmentGroup, route: Route,
|
||||
ngModule: NgModuleRef<any>, rawSegmentGroup: UrlSegmentGroup, route: Route,
|
||||
segments: UrlSegment[]): Observable<UrlSegmentGroup> {
|
||||
if (route.path === '**') {
|
||||
if (route.loadChildren) {
|
||||
return map.call(this.configLoader.load(injector, route), (cfg: LoadedRouterConfig) => {
|
||||
(<any>route)._loadedConfig = cfg;
|
||||
return new UrlSegmentGroup(segments, {});
|
||||
});
|
||||
return map.call(
|
||||
this.configLoader.load(ngModule.injector, route), (cfg: LoadedRouterConfig) => {
|
||||
(<any>route)._loadedConfig = cfg;
|
||||
return new UrlSegmentGroup(segments, {});
|
||||
});
|
||||
}
|
||||
|
||||
return of (new UrlSegmentGroup(segments, {}));
|
||||
@ -253,15 +259,17 @@ class ApplyRedirects {
|
||||
if (!matched) return noMatch(rawSegmentGroup);
|
||||
|
||||
const rawSlicedSegments = segments.slice(lastChild);
|
||||
const childConfig$ = this.getChildConfig(injector, route);
|
||||
const childConfig$ = this.getChildConfig(ngModule, route);
|
||||
|
||||
return mergeMap.call(childConfig$, (routerConfig: LoadedRouterConfig) => {
|
||||
const childInjector = routerConfig.injector;
|
||||
const childModule = routerConfig.module;
|
||||
const childConfig = routerConfig.routes;
|
||||
|
||||
const {segmentGroup, slicedSegments} =
|
||||
split(rawSegmentGroup, consumedSegments, rawSlicedSegments, childConfig);
|
||||
|
||||
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
|
||||
const expanded$ = this.expandChildren(childInjector, childConfig, segmentGroup);
|
||||
const expanded$ = this.expandChildren(childModule, childConfig, segmentGroup);
|
||||
return map.call(
|
||||
expanded$, (children: any) => new UrlSegmentGroup(consumedSegments, children));
|
||||
}
|
||||
@ -271,35 +279,37 @@ class ApplyRedirects {
|
||||
}
|
||||
|
||||
const expanded$ = this.expandSegment(
|
||||
childInjector, segmentGroup, childConfig, slicedSegments, PRIMARY_OUTLET, true);
|
||||
childModule, segmentGroup, childConfig, slicedSegments, PRIMARY_OUTLET, true);
|
||||
return map.call(
|
||||
expanded$, (cs: UrlSegmentGroup) =>
|
||||
new UrlSegmentGroup(consumedSegments.concat(cs.segments), cs.children));
|
||||
});
|
||||
}
|
||||
|
||||
private getChildConfig(injector: Injector, route: Route): Observable<LoadedRouterConfig> {
|
||||
private getChildConfig(ngModule: NgModuleRef<any>, route: Route): Observable<LoadedRouterConfig> {
|
||||
if (route.children) {
|
||||
return of (new LoadedRouterConfig(route.children, injector, null, null));
|
||||
// The children belong to the same module
|
||||
return of (new LoadedRouterConfig(route.children, ngModule));
|
||||
}
|
||||
|
||||
if (route.loadChildren) {
|
||||
return mergeMap.call(runGuards(injector, route), (shouldLoad: any) => {
|
||||
return mergeMap.call(runGuards(ngModule.injector, route), (shouldLoad: any) => {
|
||||
|
||||
if (shouldLoad) {
|
||||
return (<any>route)._loadedConfig ?
|
||||
of ((<any>route)._loadedConfig) :
|
||||
map.call(this.configLoader.load(injector, route), (cfg: LoadedRouterConfig) => {
|
||||
(<any>route)._loadedConfig = cfg;
|
||||
return cfg;
|
||||
});
|
||||
map.call(
|
||||
this.configLoader.load(ngModule.injector, route), (cfg: LoadedRouterConfig) => {
|
||||
(<any>route)._loadedConfig = cfg;
|
||||
return cfg;
|
||||
});
|
||||
}
|
||||
|
||||
return canLoadFails(route);
|
||||
});
|
||||
}
|
||||
|
||||
return of (new LoadedRouterConfig([], injector, null, null));
|
||||
return of (new LoadedRouterConfig([], ngModule));
|
||||
}
|
||||
|
||||
private lineralizeSegments(route: Route, urlTree: UrlTree): Observable<UrlSegment[]> {
|
||||
@ -386,12 +396,12 @@ class ApplyRedirects {
|
||||
}
|
||||
}
|
||||
|
||||
function runGuards(injector: Injector, route: Route): Observable<boolean> {
|
||||
function runGuards(moduleInjector: Injector, route: Route): Observable<boolean> {
|
||||
const canLoad = route.canLoad;
|
||||
if (!canLoad || canLoad.length === 0) return of (true);
|
||||
|
||||
const obs = map.call(from(canLoad), (c: any) => {
|
||||
const guard = injector.get(c);
|
||||
const guard = moduleInjector.get(c);
|
||||
return wrapIntoObservable(guard.canLoad ? guard.canLoad(route) : guard(route));
|
||||
});
|
||||
|
||||
|
@ -52,7 +52,9 @@ export class RouterOutlet implements OnDestroy {
|
||||
|
||||
ngOnDestroy(): void { this.parentOutletMap.removeOutlet(this.name ? this.name : PRIMARY_OUTLET); }
|
||||
|
||||
/** @deprecated since v4 **/
|
||||
get locationInjector(): Injector { return this.location.injector; }
|
||||
/** @deprecated since v4 **/
|
||||
get locationFactoryResolver(): ComponentFactoryResolver { return this.resolver; }
|
||||
|
||||
get isActivated(): boolean { return !!this.activated; }
|
||||
@ -90,6 +92,7 @@ export class RouterOutlet implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated since v4, use {@link activateWith} */
|
||||
activate(
|
||||
activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver, injector: Injector,
|
||||
providers: ResolvedReflectiveProvider[], outletMap: RouterOutletMap): void {
|
||||
@ -105,9 +108,51 @@ export class RouterOutlet implements OnDestroy {
|
||||
const factory = resolver.resolveComponentFactory(component);
|
||||
|
||||
const inj = ReflectiveInjector.fromResolvedProviders(providers, injector);
|
||||
|
||||
this.activated = this.location.createComponent(factory, this.location.length, inj, []);
|
||||
this.activated.changeDetectorRef.detectChanges();
|
||||
|
||||
this.activateEvents.emit(this.activated.instance);
|
||||
}
|
||||
|
||||
activateWith(
|
||||
activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null,
|
||||
outletMap: RouterOutletMap) {
|
||||
if (this.isActivated) {
|
||||
throw new Error('Cannot activate an already activated outlet');
|
||||
}
|
||||
|
||||
this.outletMap = outletMap;
|
||||
this._activatedRoute = activatedRoute;
|
||||
|
||||
const snapshot = activatedRoute._futureSnapshot;
|
||||
const component = <any>snapshot._routeConfig.component;
|
||||
|
||||
resolver = resolver || this.resolver;
|
||||
const factory = resolver.resolveComponentFactory(component);
|
||||
|
||||
const injector = new OutletInjector(activatedRoute, outletMap, this.location.injector);
|
||||
|
||||
this.activated = this.location.createComponent(factory, this.location.length, injector, []);
|
||||
this.activated.changeDetectorRef.detectChanges();
|
||||
|
||||
this.activateEvents.emit(this.activated.instance);
|
||||
}
|
||||
}
|
||||
|
||||
class OutletInjector implements Injector {
|
||||
constructor(
|
||||
private route: ActivatedRoute, private map: RouterOutletMap, private parent: Injector) {}
|
||||
|
||||
get(token: any, notFoundValue?: any): any {
|
||||
if (token === ActivatedRoute) {
|
||||
return this.route;
|
||||
}
|
||||
|
||||
if (token === RouterOutletMap) {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
return this.parent.get(token, notFoundValue);
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Location} from '@angular/common';
|
||||
import {Compiler, ComponentFactoryResolver, Injector, NgModuleFactoryLoader, ReflectiveInjector, Type, isDevMode} from '@angular/core';
|
||||
import {Compiler, Injector, NgModuleFactoryLoader, NgModuleRef, Type, isDevMode} from '@angular/core';
|
||||
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
@ -225,6 +225,7 @@ export class Router {
|
||||
private locationSubscription: Subscription;
|
||||
private navigationId: number = 0;
|
||||
private configLoader: RouterConfigLoader;
|
||||
private ngModule: NgModuleRef<any>;
|
||||
|
||||
/**
|
||||
* Error handler that is invoked when a navigation errors.
|
||||
@ -263,11 +264,13 @@ export class Router {
|
||||
// TODO: vsavkin make internal after the final is out.
|
||||
constructor(
|
||||
private rootComponentType: Type<any>, private urlSerializer: UrlSerializer,
|
||||
private outletMap: RouterOutletMap, private location: Location, private injector: Injector,
|
||||
private outletMap: RouterOutletMap, private location: Location, injector: Injector,
|
||||
loader: NgModuleFactoryLoader, compiler: Compiler, public config: Routes) {
|
||||
const onLoadStart = (r: Route) => this.triggerEvent(new RouteConfigLoadStart(r));
|
||||
const onLoadEnd = (r: Route) => this.triggerEvent(new RouteConfigLoadEnd(r));
|
||||
|
||||
this.ngModule = injector.get(NgModuleRef);
|
||||
|
||||
this.resetConfig(config);
|
||||
this.currentUrlTree = createEmptyUrlTree();
|
||||
this.rawUrlTree = this.currentUrlTree;
|
||||
@ -607,8 +610,9 @@ export class Router {
|
||||
// this operation do not result in any side effects
|
||||
let urlAndSnapshot$: Observable<{appliedUrl: UrlTree, snapshot: RouterStateSnapshot}>;
|
||||
if (!precreatedState) {
|
||||
const moduleInjector = this.ngModule.injector;
|
||||
const redirectsApplied$ =
|
||||
applyRedirects(this.injector, this.configLoader, this.urlSerializer, url, this.config);
|
||||
applyRedirects(moduleInjector, this.configLoader, this.urlSerializer, url, this.config);
|
||||
|
||||
urlAndSnapshot$ = mergeMap.call(redirectsApplied$, (appliedUrl: UrlTree) => {
|
||||
return map.call(
|
||||
@ -636,8 +640,9 @@ export class Router {
|
||||
const preactivationTraverse$ = map.call(
|
||||
beforePreactivationDone$,
|
||||
({appliedUrl, snapshot}: {appliedUrl: string, snapshot: RouterStateSnapshot}) => {
|
||||
const moduleInjector = this.ngModule.injector;
|
||||
preActivation =
|
||||
new PreActivation(snapshot, this.currentRouterState.snapshot, this.injector);
|
||||
new PreActivation(snapshot, this.currentRouterState.snapshot, moduleInjector);
|
||||
preActivation.traverse(this.outletMap);
|
||||
return {appliedUrl, snapshot};
|
||||
});
|
||||
@ -771,7 +776,7 @@ export class PreActivation {
|
||||
private checks: Array<CanActivate|CanDeactivate> = [];
|
||||
constructor(
|
||||
private future: RouterStateSnapshot, private curr: RouterStateSnapshot,
|
||||
private injector: Injector) {}
|
||||
private moduleInjector: Injector) {}
|
||||
|
||||
traverse(parentOutletMap: RouterOutletMap): void {
|
||||
const futureRoot = this.future._root;
|
||||
@ -991,7 +996,7 @@ export class PreActivation {
|
||||
|
||||
private getToken(token: any, snapshot: ActivatedRouteSnapshot): any {
|
||||
const config = closestLoadedConfig(snapshot);
|
||||
const injector = config ? config.injector : this.injector;
|
||||
const injector = config ? config.module.injector : this.moduleInjector;
|
||||
return injector.get(token);
|
||||
}
|
||||
}
|
||||
@ -1102,26 +1107,10 @@ class ActivateRoutes {
|
||||
|
||||
private placeComponentIntoOutlet(
|
||||
outletMap: RouterOutletMap, future: ActivatedRoute, outlet: RouterOutlet): void {
|
||||
const resolved = <any[]>[{provide: ActivatedRoute, useValue: future}, {
|
||||
provide: RouterOutletMap,
|
||||
useValue: outletMap
|
||||
}];
|
||||
|
||||
const config = parentLoadedConfig(future.snapshot);
|
||||
const cmpFactoryResolver = config ? config.module.componentFactoryResolver : null;
|
||||
|
||||
let resolver: ComponentFactoryResolver = null;
|
||||
let injector: Injector = null;
|
||||
|
||||
if (config) {
|
||||
injector = config.injectorFactory(outlet.locationInjector);
|
||||
resolver = config.factoryResolver;
|
||||
resolved.push({provide: ComponentFactoryResolver, useValue: resolver});
|
||||
} else {
|
||||
injector = outlet.locationInjector;
|
||||
resolver = outlet.locationFactoryResolver;
|
||||
}
|
||||
|
||||
outlet.activate(future, resolver, injector, ReflectiveInjector.resolve(resolved), outletMap);
|
||||
outlet.activateWith(future, cmpFactoryResolver, outletMap);
|
||||
}
|
||||
|
||||
private deactiveRouteAndItsChildren(
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Compiler, ComponentFactoryResolver, InjectionToken, Injector, NgModuleFactory, NgModuleFactoryLoader} from '@angular/core';
|
||||
import {Compiler, InjectionToken, Injector, NgModuleFactory, NgModuleFactoryLoader, NgModuleRef} from '@angular/core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {fromPromise} from 'rxjs/observable/fromPromise';
|
||||
import {of } from 'rxjs/observable/of';
|
||||
@ -22,9 +22,7 @@ import {flatten, wrapIntoObservable} from './utils/collection';
|
||||
export const ROUTES = new InjectionToken<Route[][]>('ROUTES');
|
||||
|
||||
export class LoadedRouterConfig {
|
||||
constructor(
|
||||
public routes: Route[], public injector: Injector,
|
||||
public factoryResolver: ComponentFactoryResolver, public injectorFactory: Function) {}
|
||||
constructor(public routes: Route[], public module: NgModuleRef<any>) {}
|
||||
}
|
||||
|
||||
export class RouterConfigLoader {
|
||||
@ -46,11 +44,8 @@ export class RouterConfigLoader {
|
||||
}
|
||||
|
||||
const module = factory.create(parentInjector);
|
||||
const injectorFactory = (parent: Injector) => factory.create(parent).injector;
|
||||
|
||||
return new LoadedRouterConfig(
|
||||
flatten(module.injector.get(ROUTES)), module.injector, module.componentFactoryResolver,
|
||||
injectorFactory);
|
||||
return new LoadedRouterConfig(flatten(module.injector.get(ROUTES)), module);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
*found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Compiler, Injectable, Injector, NgModuleFactoryLoader} from '@angular/core';
|
||||
import {Compiler, Injectable, Injector, NgModuleFactoryLoader, NgModuleRef} from '@angular/core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {from} from 'rxjs/observable/from';
|
||||
@ -91,37 +91,40 @@ export class RouterPreloader {
|
||||
this.subscription = concatMap.call(navigations, () => this.preload()).subscribe(() => {});
|
||||
}
|
||||
|
||||
preload(): Observable<any> { return this.processRoutes(this.injector, this.router.config); }
|
||||
preload(): Observable<any> {
|
||||
const ngModule = this.injector.get(NgModuleRef);
|
||||
return this.processRoutes(ngModule, this.router.config);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this.subscription.unsubscribe(); }
|
||||
|
||||
private processRoutes(injector: Injector, routes: Routes): Observable<void> {
|
||||
private processRoutes(ngModule: NgModuleRef<any>, routes: Routes): Observable<void> {
|
||||
const res: Observable<any>[] = [];
|
||||
for (const c of routes) {
|
||||
// we already have the config loaded, just recurse
|
||||
if (c.loadChildren && !c.canLoad && (<any>c)._loadedConfig) {
|
||||
const childConfig = (<any>c)._loadedConfig;
|
||||
res.push(this.processRoutes(childConfig.injector, childConfig.routes));
|
||||
res.push(this.processRoutes(childConfig.module, childConfig.routes));
|
||||
|
||||
// no config loaded, fetch the config
|
||||
} else if (c.loadChildren && !c.canLoad) {
|
||||
res.push(this.preloadConfig(injector, c));
|
||||
res.push(this.preloadConfig(ngModule, c));
|
||||
|
||||
// recurse into children
|
||||
} else if (c.children) {
|
||||
res.push(this.processRoutes(injector, c.children));
|
||||
res.push(this.processRoutes(ngModule, c.children));
|
||||
}
|
||||
}
|
||||
return mergeAll.call(from(res));
|
||||
}
|
||||
|
||||
private preloadConfig(injector: Injector, route: Route): Observable<void> {
|
||||
private preloadConfig(ngModule: NgModuleRef<any>, route: Route): Observable<void> {
|
||||
return this.preloadingStrategy.preload(route, () => {
|
||||
const loaded = this.loader.load(injector, route);
|
||||
const loaded = this.loader.load(ngModule.injector, route);
|
||||
return mergeMap.call(loaded, (config: any): any => {
|
||||
const c: any = route;
|
||||
c._loadedConfig = config;
|
||||
return this.processRoutes(config.injector, config.routes);
|
||||
return this.processRoutes(config.module, config.routes);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user