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
@ -8,20 +8,21 @@
|
||||
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Observer} from 'rxjs/Observer';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {merge} from 'rxjs/observable/merge';
|
||||
import {share} from 'rxjs/operator/share';
|
||||
|
||||
import {ErrorHandler} from '../src/error_handler';
|
||||
import {scheduleMicroTask, stringify} from '../src/util';
|
||||
import {isPromise} from '../src/util/lang';
|
||||
|
||||
import {ApplicationInitStatus} from './application_init';
|
||||
import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
|
||||
import {Console} from './console';
|
||||
import {Injectable, InjectionToken, Injector, Optional, Provider, ReflectiveInjector} from './di';
|
||||
import {Injectable, InjectionToken, Injector, Provider, ReflectiveInjector} from './di';
|
||||
import {CompilerFactory, CompilerOptions} from './linker/compiler';
|
||||
import {ComponentFactory, ComponentRef} from './linker/component_factory';
|
||||
import {ComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||
import {NgModuleFactory, NgModuleInjector, NgModuleRef} from './linker/ng_module_factory';
|
||||
import {InternalViewRef, ViewRef} from './linker/view_ref';
|
||||
import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile';
|
||||
@ -328,7 +329,7 @@ export class PlatformRef_ extends PlatformRef {
|
||||
private _moduleDoBootstrap(moduleRef: NgModuleInjector<any>): void {
|
||||
const appRef = moduleRef.injector.get(ApplicationRef);
|
||||
if (moduleRef.bootstrapFactories.length > 0) {
|
||||
moduleRef.bootstrapFactories.forEach((compFactory) => appRef.bootstrap(compFactory));
|
||||
moduleRef.bootstrapFactories.forEach(f => appRef.bootstrap(f));
|
||||
} else if (moduleRef.instance.ngDoBootstrap) {
|
||||
moduleRef.instance.ngDoBootstrap(appRef);
|
||||
} else {
|
||||
@ -502,7 +503,13 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
|
||||
}
|
||||
this._rootComponentTypes.push(componentFactory.componentType);
|
||||
const compRef = componentFactory.create(this._injector, [], componentFactory.selector);
|
||||
|
||||
// Create a factory associated with the current module if it's not bound to some other
|
||||
const ngModule = componentFactory instanceof ComponentFactoryBoundToModule ?
|
||||
null :
|
||||
this._injector.get(NgModuleRef);
|
||||
const compRef = componentFactory.create(Injector.NULL, [], componentFactory.selector, ngModule);
|
||||
|
||||
compRef.onDestroy(() => { this._unloadComponent(compRef); });
|
||||
const testability = compRef.injector.get(Testability, null);
|
||||
if (testability) {
|
||||
|
@ -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>;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -133,7 +133,6 @@ export function createPipeInstance(view: ViewData, def: NodeDef): any {
|
||||
export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
|
||||
// components can see other private services, other directives can't.
|
||||
const allowPrivateServices = (def.flags & NodeFlags.Component) > 0;
|
||||
const providerDef = def.provider;
|
||||
// directives are always eager and classes!
|
||||
const instance =
|
||||
createClass(view, def.parent, allowPrivateServices, def.provider.value, def.provider.deps);
|
||||
@ -325,6 +324,25 @@ function callFactory(
|
||||
return injectable;
|
||||
}
|
||||
|
||||
// This default value is when checking the hierarchy for a token.
|
||||
//
|
||||
// It means both:
|
||||
// - the token is not provided by the current injector,
|
||||
// - only the element injectors should be checked (ie do not check module injectors
|
||||
//
|
||||
// mod1
|
||||
// /
|
||||
// el1 mod2
|
||||
// \ /
|
||||
// el2
|
||||
//
|
||||
// When requesting el2.injector.get(token), we should check in the following order and return the
|
||||
// first found value:
|
||||
// - el2.injector.get(token, default)
|
||||
// - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
|
||||
// - mod2.injector.get(token, default)
|
||||
const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
|
||||
|
||||
export function resolveDep(
|
||||
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef,
|
||||
notFoundValue = Injector.THROW_IF_NOT_FOUND): any {
|
||||
@ -386,7 +404,20 @@ export function resolveDep(
|
||||
elDef = viewParentEl(view);
|
||||
view = view.parent;
|
||||
}
|
||||
return startView.root.injector.get(depDef.token, notFoundValue);
|
||||
|
||||
const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);
|
||||
|
||||
if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
|
||||
notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
|
||||
// Return the value from the root element injector when
|
||||
// - it provides it
|
||||
// (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
||||
// - the module injector should not be checked
|
||||
// (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
||||
return value;
|
||||
}
|
||||
|
||||
return startView.root.ngModule.injector.get(depDef.token, notFoundValue);
|
||||
}
|
||||
|
||||
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
|
||||
|
@ -11,6 +11,7 @@ import {ChangeDetectorRef} from '../change_detection/change_detection';
|
||||
import {Injector} from '../di';
|
||||
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
||||
import {ElementRef} from '../linker/element_ref';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {EmbeddedViewRef, InternalViewRef, ViewRef} from '../linker/view_ref';
|
||||
@ -52,12 +53,15 @@ class ComponentFactory_ extends ComponentFactory<any> {
|
||||
* Creates a new component.
|
||||
*/
|
||||
create(
|
||||
injector: Injector, projectableNodes: any[][] = null,
|
||||
rootSelectorOrNode: string|any = null): ComponentRef<any> {
|
||||
injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any,
|
||||
ngModule?: NgModuleRef<any>): ComponentRef<any> {
|
||||
if (!ngModule) {
|
||||
throw new Error('ngModule should be provided');
|
||||
}
|
||||
const viewDef = resolveViewDefinition(this.viewDefFactory);
|
||||
const componentNodeIndex = viewDef.nodes[0].element.componentProvider.index;
|
||||
const view = Services.createRootView(
|
||||
injector, projectableNodes || [], rootSelectorOrNode, viewDef, EMPTY_CONTEXT);
|
||||
injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT);
|
||||
const component = asProviderData(view, componentNodeIndex).instance;
|
||||
view.renderer.setAttribute(asElementData(view, 0).renderElement, 'ng-version', VERSION.full);
|
||||
|
||||
@ -107,6 +111,7 @@ class ViewContainerRef_ implements ViewContainerData {
|
||||
elDef = viewParentEl(view);
|
||||
view = view.parent;
|
||||
}
|
||||
|
||||
return view ? new Injector_(view, elDef) : this._view.root.injector;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
import {isDevMode} from '../application_ref';
|
||||
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
|
||||
import {Injector} from '../di';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
|
||||
import {Sanitizer, SecurityContext} from '../security';
|
||||
|
||||
@ -20,6 +21,7 @@ import {ArgumentType, BindingType, CheckType, DebugContext, DepFlags, ElementDat
|
||||
import {NOOP, checkBinding, isComponentView, renderNode, viewParentEl} from './util';
|
||||
import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
|
||||
|
||||
|
||||
let initialized = false;
|
||||
|
||||
export function initServicesIfNeeded() {
|
||||
@ -80,31 +82,32 @@ function createDebugServices() {
|
||||
}
|
||||
|
||||
function createProdRootView(
|
||||
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
|
||||
def: ViewDefinition, context?: any): ViewData {
|
||||
const rendererFactory: RendererFactory2 = injector.get(RendererFactory2);
|
||||
elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
|
||||
def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData {
|
||||
const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2);
|
||||
return createRootView(
|
||||
createRootData(injector, rendererFactory, projectableNodes, rootSelectorOrNode), def,
|
||||
context);
|
||||
createRootData(elInjector, ngModule, rendererFactory, projectableNodes, rootSelectorOrNode),
|
||||
def, context);
|
||||
}
|
||||
|
||||
function debugCreateRootView(
|
||||
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
|
||||
def: ViewDefinition, context?: any): ViewData {
|
||||
const rendererFactory: RendererFactory2 = injector.get(RendererFactory2);
|
||||
elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
|
||||
def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData {
|
||||
const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2);
|
||||
const root = createRootData(
|
||||
injector, new DebugRendererFactory2(rendererFactory), projectableNodes, rootSelectorOrNode);
|
||||
elInjector, ngModule, new DebugRendererFactory2(rendererFactory), projectableNodes,
|
||||
rootSelectorOrNode);
|
||||
return callWithDebugContext(DebugAction.create, createRootView, null, [root, def, context]);
|
||||
}
|
||||
|
||||
function createRootData(
|
||||
injector: Injector, rendererFactory: RendererFactory2, projectableNodes: any[][],
|
||||
rootSelectorOrNode: any): RootData {
|
||||
const sanitizer = injector.get(Sanitizer);
|
||||
elInjector: Injector, ngModule: NgModuleRef<any>, rendererFactory: RendererFactory2,
|
||||
projectableNodes: any[][], rootSelectorOrNode: any): RootData {
|
||||
const sanitizer = ngModule.injector.get(Sanitizer);
|
||||
const renderer = rendererFactory.createRenderer(null, null);
|
||||
return {
|
||||
injector,
|
||||
projectableNodes,
|
||||
ngModule,
|
||||
injector: elInjector, projectableNodes,
|
||||
selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer
|
||||
};
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
import {PipeTransform} from '../change_detection/change_detection';
|
||||
import {Injector} from '../di';
|
||||
import {ComponentRef} from '../linker/component_factory';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {QueryList} from '../linker/query_list';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
@ -424,6 +425,7 @@ export function asQueryList(view: ViewData, index: number): QueryList<any> {
|
||||
|
||||
export interface RootData {
|
||||
injector: Injector;
|
||||
ngModule: NgModuleRef<any>;
|
||||
projectableNodes: any[][];
|
||||
selectorOrNode: any;
|
||||
renderer: Renderer2;
|
||||
@ -454,7 +456,7 @@ export interface Services {
|
||||
setCurrentNode(view: ViewData, nodeIndex: number): void;
|
||||
createRootView(
|
||||
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string|any,
|
||||
def: ViewDefinition, context?: any): ViewData;
|
||||
def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData;
|
||||
createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData;
|
||||
checkAndUpdateView(view: ViewData): void;
|
||||
checkNoChangesView(view: ViewData): void;
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {APP_BOOTSTRAP_LISTENER, APP_INITIALIZER, CompilerFactory, Component, NgModule, NgZone, PlatformRef, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
import {APP_BOOTSTRAP_LISTENER, APP_INITIALIZER, Compiler, CompilerFactory, Component, NgModule, NgZone, PlatformRef, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
import {ApplicationRef, ApplicationRef_} from '@angular/core/src/application_ref';
|
||||
import {ErrorHandler} from '@angular/core/src/error_handler';
|
||||
import {ComponentRef} from '@angular/core/src/linker/component_factory';
|
||||
@ -72,6 +72,33 @@ export function main() {
|
||||
return MyModule;
|
||||
}
|
||||
|
||||
it('should bootstrap a component from a child module',
|
||||
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||
@Component({
|
||||
selector: 'bootstrap-app',
|
||||
template: '',
|
||||
})
|
||||
class SomeComponent {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
providers: [{provide: 'hello', useValue: 'component'}],
|
||||
declarations: [SomeComponent],
|
||||
entryComponents: [SomeComponent],
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
createRootEl();
|
||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||
const module = modFactory.create(TestBed);
|
||||
const cmpFactory = module.componentFactoryResolver.resolveComponentFactory(SomeComponent);
|
||||
const component = app.bootstrap(cmpFactory);
|
||||
|
||||
// The component should see the child module providers
|
||||
expect(component.injector.get('hello')).toEqual('component');
|
||||
})));
|
||||
|
||||
describe('ApplicationRef', () => {
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); });
|
||||
|
||||
|
@ -1264,13 +1264,12 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
TestBed.createComponent(SomeComponent);
|
||||
|
||||
expect(noSelectorComponentFactory.selector).toBe('ng-component');
|
||||
|
||||
expect(
|
||||
getDOM()
|
||||
.nodeName(
|
||||
noSelectorComponentFactory.create(TestBed.get(Injector)).location.nativeElement)
|
||||
.nodeName(noSelectorComponentFactory.create(Injector.NULL).location.nativeElement)
|
||||
.toLowerCase())
|
||||
.toEqual('ng-component');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -130,9 +130,13 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
function createComp<T>(compType: Type<T>, moduleType: Type<any>): ComponentFixture<T> {
|
||||
const ngModule = createModule(moduleType);
|
||||
const ngModule = createModule(moduleType, injector);
|
||||
|
||||
const cf = ngModule.componentFactoryResolver.resolveComponentFactory(compType);
|
||||
return new ComponentFixture(cf.create(injector), null, false);
|
||||
|
||||
const comp = cf.create(Injector.NULL);
|
||||
|
||||
return new ComponentFixture(comp, null, false);
|
||||
}
|
||||
|
||||
describe('errors', () => {
|
||||
@ -417,6 +421,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
const compFixture = createComp(CompUsingModuleDirectiveAndPipe, SomeModule);
|
||||
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, ElementRef, Host, Inject, InjectionToken, Input, Optional, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewContainerRef} from '@angular/core';
|
||||
import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, DebugElement, Directive, ElementRef, Host, Inject, InjectionToken, Injector, Input, NgModule, Optional, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewContainerRef} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
@ -642,6 +642,28 @@ export function main() {
|
||||
.toBe(el.children[0].nativeElement);
|
||||
});
|
||||
|
||||
it('should inject ViewContainerRef', () => {
|
||||
@Component({template: ''})
|
||||
class TestComp {
|
||||
constructor(public vcr: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [TestComp],
|
||||
entryComponents: [TestComp],
|
||||
})
|
||||
class TestModule {
|
||||
}
|
||||
|
||||
const testInjector = {};
|
||||
|
||||
const compFactory = TestBed.configureTestingModule({imports: [TestModule]})
|
||||
.get(ComponentFactoryResolver)
|
||||
.resolveComponentFactory(TestComp);
|
||||
const component = compFactory.create(<Injector>testInjector);
|
||||
expect(component.instance.vcr.parentInjector).toBe(testInjector);
|
||||
});
|
||||
|
||||
it('should inject TemplateRef', () => {
|
||||
TestBed.configureTestingModule({declarations: [NeedsViewContainerRef, NeedsTemplateRef]});
|
||||
const el =
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector, RootRenderer, Sanitizer} from '@angular/core';
|
||||
import {Injector, NgModuleRef, RootRenderer, Sanitizer} from '@angular/core';
|
||||
import {ArgumentType, NodeCheckFn, RootData, Services, ViewData, ViewDefinition, initServicesIfNeeded} from '@angular/core/src/view/index';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
@ -33,7 +33,8 @@ export function createRootView(
|
||||
rootSelectorOrNode?: any): ViewData {
|
||||
initServicesIfNeeded();
|
||||
return Services.createRootView(
|
||||
TestBed.get(Injector), projectableNodes || [], rootSelectorOrNode, def, context);
|
||||
TestBed.get(Injector), projectableNodes || [], rootSelectorOrNode, def,
|
||||
TestBed.get(NgModuleRef), context);
|
||||
}
|
||||
|
||||
export let removeNodes: Node[];
|
||||
|
@ -292,8 +292,7 @@ export class TestBed implements Injector {
|
||||
const imports = [this.ngModule, this._imports];
|
||||
const schemas = this._schemas;
|
||||
|
||||
@NgModule(
|
||||
{providers: providers, declarations: declarations, imports: imports, schemas: schemas})
|
||||
@NgModule({providers, declarations, imports, schemas})
|
||||
class DynamicTestModule {
|
||||
}
|
||||
|
||||
@ -359,10 +358,12 @@ export class TestBed implements Injector {
|
||||
this._initIfNeeded();
|
||||
const componentFactory = this._moduleWithComponentFactories.componentFactories.find(
|
||||
(compFactory) => compFactory.componentType === component);
|
||||
|
||||
if (!componentFactory) {
|
||||
throw new Error(
|
||||
`Cannot create the component ${stringify(component)} as it was not imported into the testing module!`);
|
||||
}
|
||||
|
||||
const noNgZone = this.get(ComponentFixtureNoNgZone, false);
|
||||
const autoDetect: boolean = this.get(ComponentFixtureAutoDetect, false);
|
||||
const ngZone: NgZone = noNgZone ? null : this.get(NgZone, null);
|
||||
@ -371,7 +372,8 @@ export class TestBed implements Injector {
|
||||
testComponentRenderer.insertRootElement(rootElId);
|
||||
|
||||
const initComponent = () => {
|
||||
const componentRef = componentFactory.create(this, [], `#${rootElId}`);
|
||||
const componentRef =
|
||||
componentFactory.create(Injector.NULL, [], `#${rootElId}`, this._moduleRef);
|
||||
return new ComponentFixture<T>(componentRef, ngZone, autoDetect);
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user