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:
Victor Berchet
2017-03-14 16:26:17 -07:00
committed by Chuck Jazdzewski
parent f093501501
commit 13686bb518
29 changed files with 627 additions and 242 deletions

View File

@ -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);
}
}