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

@ -11,6 +11,7 @@ import {Injector} from '../di/injector';
import {Type} from '../type';
import {ElementRef} from './element_ref';
import {NgModuleRef} from './ng_module_factory';
import {ViewRef} from './view_ref';
/**
@ -72,6 +73,7 @@ export abstract class ComponentFactory<C> {
/**
* Creates a new component.
*/
abstract create(injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any):
ComponentRef<C>;
abstract create(
injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any,
ngModule?: NgModuleRef<any>): ComponentRef<C>;
}

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injector} from '../di/injector';
import {Type} from '../type';
import {stringify} from '../util';
import {ComponentFactory} from './component_factory';
import {ComponentFactory, ComponentRef} from './component_factory';
import {NgModuleRef} from './ng_module_factory';
export function noComponentFactoryError(component: Function) {
const error = Error(
@ -44,7 +44,9 @@ export abstract class ComponentFactoryResolver {
export class CodegenComponentFactoryResolver implements ComponentFactoryResolver {
private _factories = new Map<any, ComponentFactory<any>>();
constructor(factories: ComponentFactory<any>[], private _parent: ComponentFactoryResolver) {
constructor(
factories: ComponentFactory<any>[], private _parent: ComponentFactoryResolver,
private _ngModule: NgModuleRef<any>) {
for (let i = 0; i < factories.length; i++) {
const factory = factories[i];
this._factories.set(factory.componentType, factory);
@ -52,10 +54,22 @@ export class CodegenComponentFactoryResolver implements ComponentFactoryResolver
}
resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {
let result = this._factories.get(component);
if (!result) {
result = this._parent.resolveComponentFactory(component);
}
return result;
let factory = this._factories.get(component) || this._parent.resolveComponentFactory(component);
return factory ? new ComponentFactoryBoundToModule(factory, this._ngModule) : null;
}
}
export class ComponentFactoryBoundToModule<C> extends ComponentFactory<C> {
constructor(private factory: ComponentFactory<C>, private ngModule: NgModuleRef<any>) { super(); }
get selector() { return this.factory.selector; }
get componentType() { return this.factory.componentType; }
create(
injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any,
ngModule?: NgModuleRef<any>): ComponentRef<C> {
return this.factory.create(
injector, projectableNodes, rootSelectorOrNode, ngModule || this.ngModule);
}
}

View File

@ -11,8 +11,7 @@ import {Type} from '../type';
import {stringify} from '../util';
import {ComponentFactory} from './component_factory';
import {CodegenComponentFactoryResolver, ComponentFactoryResolver} from './component_factory_resolver';
import {CodegenComponentFactoryResolver, ComponentFactoryBoundToModule, ComponentFactoryResolver} from './component_factory_resolver';
/**
@ -62,10 +61,7 @@ export class NgModuleFactory<T> {
get moduleType(): Type<T> { return this._moduleType; }
create(parentInjector: Injector): NgModuleRef<T> {
if (!parentInjector) {
parentInjector = Injector.NULL;
}
const instance = new this._injectorClass(parentInjector);
const instance = new this._injectorClass(parentInjector || Injector.NULL);
instance.create();
return instance;
}
@ -73,18 +69,21 @@ export class NgModuleFactory<T> {
const _UNDEFINED = new Object();
export abstract class NgModuleInjector<T> extends CodegenComponentFactoryResolver implements
Injector,
NgModuleRef<T> {
export abstract class NgModuleInjector<T> implements Injector, NgModuleRef<T> {
bootstrapFactories: ComponentFactory<any>[];
instance: T;
private _destroyListeners: (() => void)[] = [];
private _destroyed: boolean = false;
public instance: T;
private _cmpFactoryResolver: CodegenComponentFactoryResolver;
constructor(
public parent: Injector, factories: ComponentFactory<any>[],
public bootstrapFactories: ComponentFactory<any>[]) {
super(factories, parent.get(ComponentFactoryResolver, ComponentFactoryResolver.NULL));
bootstrapFactories: ComponentFactory<any>[]) {
this.bootstrapFactories =
bootstrapFactories.map(f => new ComponentFactoryBoundToModule(f, this));
this._cmpFactoryResolver = new CodegenComponentFactoryResolver(
factories, parent.get(ComponentFactoryResolver, ComponentFactoryResolver.NULL), this);
}
create() { this.instance = this.createInternal(); }
@ -92,9 +91,14 @@ export abstract class NgModuleInjector<T> extends CodegenComponentFactoryResolve
abstract createInternal(): T;
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
if (token === Injector || token === ComponentFactoryResolver) {
if (token === Injector || token === NgModuleRef) {
return this;
}
if (token === ComponentFactoryResolver) {
return this._cmpFactoryResolver;
}
const result = this.getInternal(token, _UNDEFINED);
return result === _UNDEFINED ? this.parent.get(token, notFoundValue) : result;
}
@ -103,7 +107,7 @@ export abstract class NgModuleInjector<T> extends CodegenComponentFactoryResolve
get injector(): Injector { return this; }
get componentFactoryResolver(): ComponentFactoryResolver { return this; }
get componentFactoryResolver(): ComponentFactoryResolver { return this._cmpFactoryResolver; }
destroy(): void {
if (this._destroyed) {