fix(core): unable to NgModuleRef.injector in module constructor (#35731)

This is a follow up to #35637 which resolved a similar issue for `ComponentFactoryResolver`, but not the root cause. When a `NgModuleRef` is created, it instantiates an `Injector` internally which in turn resolves all of injector types. This can result in a circular call that results in an error, because the module is one of the injector types being resolved.

These changes work around the issue by allowing the constructor to run before resolving the injector types.

Fixes #35677.
Fixes #35639.

PR Close #35731
This commit is contained in:
crisbeto
2020-03-02 20:23:50 +01:00
committed by atscott
parent 17cf04ebea
commit 1f8a243b67
4 changed files with 54 additions and 13 deletions

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {CUSTOM_ELEMENTS_SCHEMA, Component, ComponentFactory, Injectable, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core';
import {CUSTOM_ELEMENTS_SCHEMA, Component, Injectable, InjectionToken, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
@ -475,9 +475,28 @@ describe('NgModule', () => {
});
it('should be able to use ComponentFactoryResolver from the NgModuleRef inside the module constructor',
it('should be able to use DI through the NgModuleRef inside the module constructor', () => {
const token = new InjectionToken<string>('token');
let value: string|undefined;
@NgModule({
imports: [CommonModule],
providers: [{provide: token, useValue: 'foo'}],
})
class TestModule {
constructor(ngRef: NgModuleRef<TestModule>) { value = ngRef.injector.get(token); }
}
TestBed.configureTestingModule({imports: [TestModule], declarations: [TestCmp]});
const fixture = TestBed.createComponent(TestCmp);
fixture.detectChanges();
expect(value).toBe('foo');
});
it('should be able to create a component through the ComponentFactoryResolver of an NgModuleRef in a module constructor',
() => {
let factory: ComponentFactory<TestCmp>;
let componentInstance: TestCmp|undefined;
@NgModule({
declarations: [TestCmp],
@ -486,13 +505,14 @@ describe('NgModule', () => {
})
class MyModule {
constructor(ngModuleRef: NgModuleRef<any>) {
factory = ngModuleRef.componentFactoryResolver.resolveComponentFactory(TestCmp);
const factory = ngModuleRef.componentFactoryResolver.resolveComponentFactory(TestCmp);
componentInstance = factory.create(ngModuleRef.injector).instance;
}
}
TestBed.configureTestingModule({imports: [MyModule]});
TestBed.createComponent(TestCmp);
expect(factory !.componentType).toBe(TestCmp);
expect(componentInstance).toBeAnInstanceOf(TestCmp);
});
});