Skip to content

Commit 13686bb

Browse files
vicbchuckjaz
authored andcommitted
fix: element injector vs module injector (angular#15044)
fixes angular#12869 fixes angular#12889 fixes angular#13885 fixes angular#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`
1 parent f093501 commit 13686bb

29 files changed

Lines changed: 627 additions & 242 deletions

modules/benchmarks/src/tree/ng2_next/tree.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {NgIf} from '@angular/common';
10-
import {ComponentFactory, ComponentRef, Injector, RendererFactory2, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef} from '@angular/core';
10+
import {ComponentFactory, ComponentFactoryResolver, ComponentRef, Injector, NgModuleRef, RendererFactory2, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef} from '@angular/core';
1111
import {ArgumentType, BindingType, NodeFlags, ViewDefinition, ViewFlags, anchorDef, createComponentFactory, directiveDef, elementDef, initServicesIfNeeded, textDef, viewDef} from '@angular/core/src/view/index';
1212
import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_renderer';
1313
import {DomSanitizerImpl, SafeStyle} from '@angular/platform-browser/src/security/dom_sanitization_service';
@@ -84,7 +84,7 @@ function TreeComponent_0(): ViewDefinition {
8484
});
8585
}
8686

87-
export class AppModule implements Injector {
87+
export class AppModule implements Injector, NgModuleRef<any> {
8888
private sanitizer: DomSanitizerImpl;
8989
private componentFactory: ComponentFactory<TreeComponent>;
9090
private renderer2: RendererFactory2;
@@ -108,12 +108,22 @@ export class AppModule implements Injector {
108108
return this.sanitizer;
109109
case RootRenderer:
110110
return null;
111+
case NgModuleRef:
112+
return this;
111113
}
112114
return Injector.NULL.get(token, notFoundValue);
113115
}
114116

115117
bootstrap() {
116-
this.componentRef = this.componentFactory.create(this, [], this.componentFactory.selector);
118+
this.componentRef =
119+
this.componentFactory.create(Injector.NULL, [], this.componentFactory.selector, this);
117120
}
121+
118122
tick() { this.componentRef.changeDetectorRef.detectChanges(); }
123+
124+
get injector() { return this; }
125+
get componentFactoryResolver(): ComponentFactoryResolver { return null; }
126+
get instance() { return this; }
127+
destroy() {}
128+
onDestroy(callback: () => void) {}
119129
}

packages/common/src/directives/ng_component_outlet.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgModuleFactory, NgModuleRef, OnChanges, OnDestroy, Provider, SimpleChanges, Type, ViewContainerRef} from '@angular/core';
1010

11-
12-
1311
/**
1412
* Instantiates a single {@link Component} type and inserts its Host View into current View.
1513
* `NgComponentOutlet` provides a declarative approach for dynamic component creation.
@@ -81,34 +79,35 @@ export class NgComponentOutlet implements OnChanges, OnDestroy {
8179
constructor(private _viewContainerRef: ViewContainerRef) {}
8280

8381
ngOnChanges(changes: SimpleChanges) {
84-
if (this._componentRef) {
85-
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._componentRef.hostView));
86-
}
8782
this._viewContainerRef.clear();
8883
this._componentRef = null;
8984

9085
if (this.ngComponentOutlet) {
91-
let injector = this.ngComponentOutletInjector || this._viewContainerRef.parentInjector;
86+
const elInjector = this.ngComponentOutletInjector || this._viewContainerRef.parentInjector;
9287

93-
if ((changes as any).ngComponentOutletNgModuleFactory) {
88+
if (changes['ngComponentOutletNgModuleFactory']) {
9489
if (this._moduleRef) this._moduleRef.destroy();
90+
9591
if (this.ngComponentOutletNgModuleFactory) {
96-
this._moduleRef = this.ngComponentOutletNgModuleFactory.create(injector);
92+
const parentModule = elInjector.get(NgModuleRef);
93+
this._moduleRef = this.ngComponentOutletNgModuleFactory.create(parentModule.injector);
9794
} else {
9895
this._moduleRef = null;
9996
}
10097
}
101-
if (this._moduleRef) {
102-
injector = this._moduleRef.injector;
103-
}
10498

105-
let componentFactory =
106-
injector.get(ComponentFactoryResolver).resolveComponentFactory(this.ngComponentOutlet);
99+
const componentFactoryResolver = this._moduleRef ? this._moduleRef.componentFactoryResolver :
100+
elInjector.get(ComponentFactoryResolver);
101+
102+
const componentFactory =
103+
componentFactoryResolver.resolveComponentFactory(this.ngComponentOutlet);
107104

108105
this._componentRef = this._viewContainerRef.createComponent(
109-
componentFactory, this._viewContainerRef.length, injector, this.ngComponentOutletContent);
106+
componentFactory, this._viewContainerRef.length, elInjector,
107+
this.ngComponentOutletContent);
110108
}
111109
}
110+
112111
ngOnDestroy() {
113112
if (this._moduleRef) this._moduleRef.destroy();
114113
}

packages/common/test/directives/ng_component_outlet_spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,13 @@ export function main() {
177177
it('should not re-create moduleRef when it didn\'t actually change', async(() => {
178178
const compiler = TestBed.get(Compiler) as Compiler;
179179
const fixture = TestBed.createComponent(TestComponent);
180+
180181
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
181182
fixture.componentInstance.currentComponent = Module2InjectedComponent;
182183
fixture.detectChanges();
183-
184184
expect(fixture.nativeElement).toHaveText('baz');
185-
186185
const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef'];
186+
187187
fixture.componentInstance.currentComponent = Module2InjectedComponent2;
188188
fixture.detectChanges();
189189

@@ -247,11 +247,11 @@ class TestComponent {
247247
export class TestModule {
248248
}
249249

250-
@Component({selector: 'mdoule-2-injected-component', template: 'baz'})
250+
@Component({selector: 'module-2-injected-component', template: 'baz'})
251251
class Module2InjectedComponent {
252252
}
253253

254-
@Component({selector: 'mdoule-2-injected-component-2', template: 'baz2'})
254+
@Component({selector: 'module-2-injected-component-2', template: 'baz2'})
255255
class Module2InjectedComponent2 {
256256
}
257257

@@ -264,7 +264,7 @@ class Module2InjectedComponent2 {
264264
export class TestModule2 {
265265
}
266266

267-
@Component({selector: 'mdoule-3-injected-component', template: 'bat'})
267+
@Component({selector: 'module-3-injected-component', template: 'bat'})
268268
class Module3InjectedComponent {
269269
}
270270

packages/compiler/src/identifiers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵChangeDetectorStatus, ɵCodegenComponentFactoryResolver, ɵEMPTY_ARRAY, ɵEMPTY_MAP, ɵNgModuleInjector, ɵValueUnwrapper, ɵand, ɵccf, ɵcrt, ɵdevModeEqual, ɵdid, ɵeld, ɵinlineInterpolate, ɵinterpolate, ɵncd, ɵnov, ɵpad, ɵpid, ɵpod, ɵppd, ɵprd, ɵqud, ɵreflector, ɵregisterModuleFactory, ɵted, ɵunv, ɵvid} from '@angular/core';
9+
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, NgModuleRef, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵChangeDetectorStatus, ɵCodegenComponentFactoryResolver, ɵEMPTY_ARRAY, ɵEMPTY_MAP, ɵNgModuleInjector, ɵValueUnwrapper, ɵand, ɵccf, ɵcrt, ɵdevModeEqual, ɵdid, ɵeld, ɵinlineInterpolate, ɵinterpolate, ɵncd, ɵnov, ɵpad, ɵpid, ɵpod, ɵppd, ɵprd, ɵqud, ɵreflector, ɵregisterModuleFactory, ɵted, ɵunv, ɵvid} from '@angular/core';
1010

1111
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
1212

@@ -26,6 +26,7 @@ export class Identifiers {
2626
runtime: ANALYZE_FOR_ENTRY_COMPONENTS
2727
};
2828
static ElementRef: IdentifierSpec = {name: 'ElementRef', moduleUrl: CORE, runtime: ElementRef};
29+
static NgModuleRef: IdentifierSpec = {name: 'NgModuleRef', moduleUrl: CORE, runtime: NgModuleRef};
2930
static ViewContainerRef:
3031
IdentifierSpec = {name: 'ViewContainerRef', moduleUrl: CORE, runtime: ViewContainerRef};
3132
static ChangeDetectorRef:

packages/compiler/src/ng_module_compiler.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,15 @@ class _InjectorBuilder implements ClassBuilder {
216216
result = o.literal(dep.value);
217217
}
218218
if (!dep.isSkipSelf) {
219-
if (dep.token &&
220-
(tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector) ||
221-
tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver))) {
222-
result = o.THIS_EXPR;
219+
if (dep.token) {
220+
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector)) {
221+
result = o.THIS_EXPR;
222+
} else if (
223+
tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver)) {
224+
result = o.THIS_EXPR.prop('componentFactoryResolver');
225+
}
223226
}
227+
224228
if (!result) {
225229
result = this._instances.get(tokenReference(dep.token));
226230
}

packages/compiler/src/view_compiler/view_compiler.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,13 +1125,14 @@ function createComponentFactoryResolver(directives: DirectiveAst[]): ProviderAst
11251125
if (componentDirMeta && componentDirMeta.directive.entryComponents.length) {
11261126
const entryComponentFactories = componentDirMeta.directive.entryComponents.map(
11271127
(entryComponent) => o.importExpr({reference: entryComponent.componentFactory}));
1128-
const cfrExpr = o.importExpr(createIdentifier(Identifiers.CodegenComponentFactoryResolver))
1129-
.instantiate([o.literalArr(entryComponentFactories)]);
1128+
11301129
const token = createIdentifierToken(Identifiers.ComponentFactoryResolver);
1130+
11311131
const classMeta: CompileTypeMetadata = {
11321132
diDeps: [
11331133
{isValue: true, value: o.literalArr(entryComponentFactories)},
1134-
{token: token, isSkipSelf: true, isOptional: true}
1134+
{token: token, isSkipSelf: true, isOptional: true},
1135+
{token: createIdentifierToken(Identifiers.NgModuleRef)},
11351136
],
11361137
lifecycleHooks: [],
11371138
reference: resolveIdentifier(Identifiers.CodegenComponentFactoryResolver)

packages/core/src/application_ref.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@
88

99
import {Observable} from 'rxjs/Observable';
1010
import {Observer} from 'rxjs/Observer';
11-
import {Subject} from 'rxjs/Subject';
1211
import {Subscription} from 'rxjs/Subscription';
1312
import {merge} from 'rxjs/observable/merge';
1413
import {share} from 'rxjs/operator/share';
14+
1515
import {ErrorHandler} from '../src/error_handler';
1616
import {scheduleMicroTask, stringify} from '../src/util';
1717
import {isPromise} from '../src/util/lang';
18+
1819
import {ApplicationInitStatus} from './application_init';
1920
import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
2021
import {Console} from './console';
21-
import {Injectable, InjectionToken, Injector, Optional, Provider, ReflectiveInjector} from './di';
22+
import {Injectable, InjectionToken, Injector, Provider, ReflectiveInjector} from './di';
2223
import {CompilerFactory, CompilerOptions} from './linker/compiler';
2324
import {ComponentFactory, ComponentRef} from './linker/component_factory';
24-
import {ComponentFactoryResolver} from './linker/component_factory_resolver';
25+
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from './linker/component_factory_resolver';
2526
import {NgModuleFactory, NgModuleInjector, NgModuleRef} from './linker/ng_module_factory';
2627
import {InternalViewRef, ViewRef} from './linker/view_ref';
2728
import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile';
@@ -328,7 +329,7 @@ export class PlatformRef_ extends PlatformRef {
328329
private _moduleDoBootstrap(moduleRef: NgModuleInjector<any>): void {
329330
const appRef = moduleRef.injector.get(ApplicationRef);
330331
if (moduleRef.bootstrapFactories.length > 0) {
331-
moduleRef.bootstrapFactories.forEach((compFactory) => appRef.bootstrap(compFactory));
332+
moduleRef.bootstrapFactories.forEach(f => appRef.bootstrap(f));
332333
} else if (moduleRef.instance.ngDoBootstrap) {
333334
moduleRef.instance.ngDoBootstrap(appRef);
334335
} else {
@@ -502,7 +503,13 @@ export class ApplicationRef_ extends ApplicationRef {
502503
componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
503504
}
504505
this._rootComponentTypes.push(componentFactory.componentType);
505-
const compRef = componentFactory.create(this._injector, [], componentFactory.selector);
506+
507+
// Create a factory associated with the current module if it's not bound to some other
508+
const ngModule = componentFactory instanceof ComponentFactoryBoundToModule ?
509+
null :
510+
this._injector.get(NgModuleRef);
511+
const compRef = componentFactory.create(Injector.NULL, [], componentFactory.selector, ngModule);
512+
506513
compRef.onDestroy(() => { this._unloadComponent(compRef); });
507514
const testability = compRef.injector.get(Testability, null);
508515
if (testability) {

packages/core/src/linker/component_factory.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {Injector} from '../di/injector';
1111
import {Type} from '../type';
1212

1313
import {ElementRef} from './element_ref';
14+
import {NgModuleRef} from './ng_module_factory';
1415
import {ViewRef} from './view_ref';
1516

1617
/**
@@ -72,6 +73,7 @@ export abstract class ComponentFactory<C> {
7273
/**
7374
* Creates a new component.
7475
*/
75-
abstract create(injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any):
76-
ComponentRef<C>;
76+
abstract create(
77+
injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any,
78+
ngModule?: NgModuleRef<any>): ComponentRef<C>;
7779
}

packages/core/src/linker/component_factory_resolver.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {Injector} from '../di/injector';
910
import {Type} from '../type';
1011
import {stringify} from '../util';
1112

12-
import {ComponentFactory} from './component_factory';
13-
14-
13+
import {ComponentFactory, ComponentRef} from './component_factory';
14+
import {NgModuleRef} from './ng_module_factory';
1515

1616
export function noComponentFactoryError(component: Function) {
1717
const error = Error(
@@ -44,18 +44,32 @@ export abstract class ComponentFactoryResolver {
4444
export class CodegenComponentFactoryResolver implements ComponentFactoryResolver {
4545
private _factories = new Map<any, ComponentFactory<any>>();
4646

47-
constructor(factories: ComponentFactory<any>[], private _parent: ComponentFactoryResolver) {
47+
constructor(
48+
factories: ComponentFactory<any>[], private _parent: ComponentFactoryResolver,
49+
private _ngModule: NgModuleRef<any>) {
4850
for (let i = 0; i < factories.length; i++) {
4951
const factory = factories[i];
5052
this._factories.set(factory.componentType, factory);
5153
}
5254
}
5355

5456
resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {
55-
let result = this._factories.get(component);
56-
if (!result) {
57-
result = this._parent.resolveComponentFactory(component);
58-
}
59-
return result;
57+
let factory = this._factories.get(component) || this._parent.resolveComponentFactory(component);
58+
59+
return factory ? new ComponentFactoryBoundToModule(factory, this._ngModule) : null;
60+
}
61+
}
62+
63+
export class ComponentFactoryBoundToModule<C> extends ComponentFactory<C> {
64+
constructor(private factory: ComponentFactory<C>, private ngModule: NgModuleRef<any>) { super(); }
65+
66+
get selector() { return this.factory.selector; }
67+
get componentType() { return this.factory.componentType; }
68+
69+
create(
70+
injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any,
71+
ngModule?: NgModuleRef<any>): ComponentRef<C> {
72+
return this.factory.create(
73+
injector, projectableNodes, rootSelectorOrNode, ngModule || this.ngModule);
6074
}
6175
}

packages/core/src/linker/ng_module_factory.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import {Type} from '../type';
1111
import {stringify} from '../util';
1212

1313
import {ComponentFactory} from './component_factory';
14-
import {CodegenComponentFactoryResolver, ComponentFactoryResolver} from './component_factory_resolver';
15-
14+
import {CodegenComponentFactoryResolver, ComponentFactoryBoundToModule, ComponentFactoryResolver} from './component_factory_resolver';
1615

1716

1817
/**
@@ -62,39 +61,44 @@ export class NgModuleFactory<T> {
6261
get moduleType(): Type<T> { return this._moduleType; }
6362

6463
create(parentInjector: Injector): NgModuleRef<T> {
65-
if (!parentInjector) {
66-
parentInjector = Injector.NULL;
67-
}
68-
const instance = new this._injectorClass(parentInjector);
64+
const instance = new this._injectorClass(parentInjector || Injector.NULL);
6965
instance.create();
7066
return instance;
7167
}
7268
}
7369

7470
const _UNDEFINED = new Object();
7571

76-
export abstract class NgModuleInjector<T> extends CodegenComponentFactoryResolver implements
77-
Injector,
78-
NgModuleRef<T> {
72+
export abstract class NgModuleInjector<T> implements Injector, NgModuleRef<T> {
73+
bootstrapFactories: ComponentFactory<any>[];
74+
instance: T;
75+
7976
private _destroyListeners: (() => void)[] = [];
8077
private _destroyed: boolean = false;
81-
82-
public instance: T;
78+
private _cmpFactoryResolver: CodegenComponentFactoryResolver;
8379

8480
constructor(
8581
public parent: Injector, factories: ComponentFactory<any>[],
86-
public bootstrapFactories: ComponentFactory<any>[]) {
87-
super(factories, parent.get(ComponentFactoryResolver, ComponentFactoryResolver.NULL));
82+
bootstrapFactories: ComponentFactory<any>[]) {
83+
this.bootstrapFactories =
84+
bootstrapFactories.map(f => new ComponentFactoryBoundToModule(f, this));
85+
this._cmpFactoryResolver = new CodegenComponentFactoryResolver(
86+
factories, parent.get(ComponentFactoryResolver, ComponentFactoryResolver.NULL), this);
8887
}
8988

9089
create() { this.instance = this.createInternal(); }
9190

9291
abstract createInternal(): T;
9392

9493
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
95-
if (token === Injector || token === ComponentFactoryResolver) {
94+
if (token === Injector || token === NgModuleRef) {
9695
return this;
9796
}
97+
98+
if (token === ComponentFactoryResolver) {
99+
return this._cmpFactoryResolver;
100+
}
101+
98102
const result = this.getInternal(token, _UNDEFINED);
99103
return result === _UNDEFINED ? this.parent.get(token, notFoundValue) : result;
100104
}
@@ -103,7 +107,7 @@ export abstract class NgModuleInjector<T> extends CodegenComponentFactoryResolve
103107

104108
get injector(): Injector { return this; }
105109

106-
get componentFactoryResolver(): ComponentFactoryResolver { return this; }
110+
get componentFactoryResolver(): ComponentFactoryResolver { return this._cmpFactoryResolver; }
107111

108112
destroy(): void {
109113
if (this._destroyed) {

0 commit comments

Comments
 (0)