feat: introduce `TestBed.overrideProvider` (#16725)
This allows to overwrite all providers for a token, not matter where they were defined. This can be used to test JIT and AOT’ed code in the same way. Design doc: https://docs.google.com/document/d/1VmTkz0EbEVSWfEEWEvQ5sXyQXSCvtMOw4t7pKU-jOwc/edit?usp=sharing
This commit is contained in:
parent
1eba623d12
commit
39b92f7e54
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
|
import {ApplicationInitStatus} from './application_init';
|
||||||
import {ApplicationRef, ApplicationRef_} from './application_ref';
|
import {ApplicationRef, ApplicationRef_} from './application_ref';
|
||||||
import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
|
import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
|
||||||
import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection';
|
import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection';
|
||||||
|
@ -14,7 +14,6 @@ import {Inject, Optional, SkipSelf} from './di/metadata';
|
||||||
import {LOCALE_ID} from './i18n/tokens';
|
import {LOCALE_ID} from './i18n/tokens';
|
||||||
import {Compiler} from './linker/compiler';
|
import {Compiler} from './linker/compiler';
|
||||||
import {NgModule} from './metadata';
|
import {NgModule} from './metadata';
|
||||||
import {initServicesIfNeeded} from './view/index';
|
|
||||||
|
|
||||||
export function _iterableDiffersFactory() {
|
export function _iterableDiffersFactory() {
|
||||||
return defaultIterableDiffers;
|
return defaultIterableDiffers;
|
||||||
|
@ -28,10 +27,6 @@ export function _localeFactory(locale?: string): string {
|
||||||
return locale || 'en-US';
|
return locale || 'en-US';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _initViewEngine() {
|
|
||||||
initServicesIfNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This module includes the providers of @angular/core that are needed
|
* This module includes the providers of @angular/core that are needed
|
||||||
* to bootstrap components via `ApplicationRef`.
|
* to bootstrap components via `ApplicationRef`.
|
||||||
|
@ -52,7 +47,6 @@ export function _initViewEngine() {
|
||||||
useFactory: _localeFactory,
|
useFactory: _localeFactory,
|
||||||
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
|
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
|
||||||
},
|
},
|
||||||
{provide: APP_INITIALIZER, useValue: _initViewEngine, multi: true},
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ApplicationModule {
|
export class ApplicationModule {
|
||||||
|
|
|
@ -26,4 +26,5 @@ export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo
|
||||||
export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util';
|
export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util';
|
||||||
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
|
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
|
||||||
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
|
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
|
||||||
|
export {clearProviderOverrides as ɵclearProviderOverrides, overrideProvider as ɵoverrideProvider} from './view/index';
|
||||||
export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';
|
export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Injector} from '../di/injector';
|
||||||
|
import {NgModuleFactory, NgModuleRef} from '../linker/ng_module_factory';
|
||||||
|
import {Type} from '../type';
|
||||||
|
|
||||||
|
import {initServicesIfNeeded} from './services';
|
||||||
|
import {NgModuleDefinitionFactory, ProviderOverride, Services} from './types';
|
||||||
|
import {resolveDefinition} from './util';
|
||||||
|
|
||||||
|
export function overrideProvider(override: ProviderOverride) {
|
||||||
|
initServicesIfNeeded();
|
||||||
|
return Services.overrideProvider(override);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearProviderOverrides() {
|
||||||
|
initServicesIfNeeded();
|
||||||
|
return Services.clearProviderOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attention: this function is called as top level function.
|
||||||
|
// Putting any logic in here will destroy closure tree shaking!
|
||||||
|
export function createNgModuleFactory(
|
||||||
|
ngModuleType: Type<any>, bootstrapComponents: Type<any>[],
|
||||||
|
defFactory: NgModuleDefinitionFactory): NgModuleFactory<any> {
|
||||||
|
return new NgModuleFactory_(ngModuleType, bootstrapComponents, defFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
class NgModuleFactory_ extends NgModuleFactory<any> {
|
||||||
|
constructor(
|
||||||
|
public readonly moduleType: Type<any>, private _bootstrapComponents: Type<any>[],
|
||||||
|
private _ngModuleDefFactory: NgModuleDefinitionFactory) {
|
||||||
|
// Attention: this ctor is called as top level function.
|
||||||
|
// Putting any logic in here will destroy closure tree shaking!
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
create(parentInjector: Injector|null): NgModuleRef<any> {
|
||||||
|
initServicesIfNeeded();
|
||||||
|
const def = resolveDefinition(this._ngModuleDefFactory);
|
||||||
|
return Services.createNgModuleRef(
|
||||||
|
this.moduleType, parentInjector || Injector.NULL, this._bootstrapComponents, def);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,13 +7,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {anchorDef, elementDef} from './element';
|
export {anchorDef, elementDef} from './element';
|
||||||
|
export {clearProviderOverrides, createNgModuleFactory, overrideProvider} from './entrypoint';
|
||||||
export {ngContentDef} from './ng_content';
|
export {ngContentDef} from './ng_content';
|
||||||
export {moduleDef, moduleProvideDef} from './ng_module';
|
export {moduleDef, moduleProvideDef} from './ng_module';
|
||||||
export {directiveDef, pipeDef, providerDef} from './provider';
|
export {directiveDef, pipeDef, providerDef} from './provider';
|
||||||
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
||||||
export {queryDef} from './query';
|
export {queryDef} from './query';
|
||||||
export {ViewRef_, createComponentFactory, getComponentViewDefinitionFactory, nodeValue} from './refs';
|
export {ViewRef_, createComponentFactory, getComponentViewDefinitionFactory, nodeValue} from './refs';
|
||||||
export {createNgModuleFactory} from './refs';
|
|
||||||
export {initServicesIfNeeded} from './services';
|
export {initServicesIfNeeded} from './services';
|
||||||
export {textDef} from './text';
|
export {textDef} from './text';
|
||||||
export {EMPTY_ARRAY, EMPTY_MAP, createRendererType2, elementEventFullName, inlineInterpolate, interpolate, rootRenderNodes, tokenKey, unwrapValue} from './util';
|
export {EMPTY_ARRAY, EMPTY_MAP, createRendererType2, elementEventFullName, inlineInterpolate, interpolate, rootRenderNodes, tokenKey, unwrapValue} from './util';
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
|
||||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||||
|
|
||||||
import {DepDef, DepFlags, NgModuleData, NgModuleDefinition, NgModuleDefinitionFactory, NgModuleProviderDef, NodeFlags} from './types';
|
import {DepDef, DepFlags, NgModuleData, NgModuleDefinition, NgModuleDefinitionFactory, NgModuleProviderDef, NodeFlags} from './types';
|
||||||
import {tokenKey} from './util';
|
import {splitDepsDsl, tokenKey} from './util';
|
||||||
|
|
||||||
const NOT_CREATED = new Object();
|
const NOT_CREATED = new Object();
|
||||||
|
|
||||||
|
@ -20,17 +20,7 @@ const NgModuleRefTokenKey = tokenKey(NgModuleRef);
|
||||||
export function moduleProvideDef(
|
export function moduleProvideDef(
|
||||||
flags: NodeFlags, token: any, value: any,
|
flags: NodeFlags, token: any, value: any,
|
||||||
deps: ([DepFlags, any] | any)[]): NgModuleProviderDef {
|
deps: ([DepFlags, any] | any)[]): NgModuleProviderDef {
|
||||||
const depDefs: DepDef[] = deps.map(value => {
|
const depDefs = splitDepsDsl(deps);
|
||||||
let token: any;
|
|
||||||
let flags: DepFlags;
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
[flags, token] = value;
|
|
||||||
} else {
|
|
||||||
flags = DepFlags.None;
|
|
||||||
token = value;
|
|
||||||
}
|
|
||||||
return {flags, token, tokenKey: tokenKey(token)};
|
|
||||||
});
|
|
||||||
return {
|
return {
|
||||||
// will bet set by the module definition
|
// will bet set by the module definition
|
||||||
index: -1,
|
index: -1,
|
||||||
|
@ -97,16 +87,16 @@ function _createProviderInstance(ngModule: NgModuleData, providerDef: NgModulePr
|
||||||
let injectable: any;
|
let injectable: any;
|
||||||
switch (providerDef.flags & NodeFlags.Types) {
|
switch (providerDef.flags & NodeFlags.Types) {
|
||||||
case NodeFlags.TypeClassProvider:
|
case NodeFlags.TypeClassProvider:
|
||||||
injectable = _createClass(ngModule, providerDef !.value, providerDef !.deps);
|
injectable = _createClass(ngModule, providerDef.value, providerDef.deps);
|
||||||
break;
|
break;
|
||||||
case NodeFlags.TypeFactoryProvider:
|
case NodeFlags.TypeFactoryProvider:
|
||||||
injectable = _callFactory(ngModule, providerDef !.value, providerDef !.deps);
|
injectable = _callFactory(ngModule, providerDef.value, providerDef.deps);
|
||||||
break;
|
break;
|
||||||
case NodeFlags.TypeUseExistingProvider:
|
case NodeFlags.TypeUseExistingProvider:
|
||||||
injectable = resolveNgModuleDep(ngModule, providerDef !.deps[0]);
|
injectable = resolveNgModuleDep(ngModule, providerDef.deps[0]);
|
||||||
break;
|
break;
|
||||||
case NodeFlags.TypeValueProvider:
|
case NodeFlags.TypeValueProvider:
|
||||||
injectable = providerDef !.value;
|
injectable = providerDef.value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return injectable;
|
return injectable;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {Renderer as RendererV1, Renderer2} from '../render/api';
|
||||||
|
|
||||||
import {createChangeDetectorRef, createInjector, createRendererV1} from './refs';
|
import {createChangeDetectorRef, createInjector, createRendererV1} from './refs';
|
||||||
import {BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, ViewData, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
import {BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, ViewData, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
||||||
import {calcBindingFlags, checkBinding, dispatchEvent, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
|
import {calcBindingFlags, checkBinding, dispatchEvent, isComponentView, splitDepsDsl, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
|
||||||
|
|
||||||
const RendererV1TokenKey = tokenKey(RendererV1);
|
const RendererV1TokenKey = tokenKey(RendererV1);
|
||||||
const Renderer2TokenKey = tokenKey(Renderer2);
|
const Renderer2TokenKey = tokenKey(Renderer2);
|
||||||
|
@ -78,17 +78,7 @@ export function _def(
|
||||||
bindings = [];
|
bindings = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const depDefs: DepDef[] = deps.map(value => {
|
const depDefs = splitDepsDsl(deps);
|
||||||
let token: any;
|
|
||||||
let flags: DepFlags;
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
[flags, token] = value;
|
|
||||||
} else {
|
|
||||||
flags = DepFlags.None;
|
|
||||||
token = value;
|
|
||||||
}
|
|
||||||
return {flags, token, tokenKey: tokenKey(token)};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// will bet set by the view definition
|
// will bet set by the view definition
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {Injector} from '../di/injector';
|
||||||
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
||||||
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||||
import {ElementRef} from '../linker/element_ref';
|
import {ElementRef} from '../linker/element_ref';
|
||||||
import {InternalNgModuleRef, NgModuleFactory, NgModuleRef} from '../linker/ng_module_factory';
|
import {InternalNgModuleRef, NgModuleRef} from '../linker/ng_module_factory';
|
||||||
import {TemplateRef} from '../linker/template_ref';
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {EmbeddedViewRef, InternalViewRef, ViewRef} from '../linker/view_ref';
|
import {EmbeddedViewRef, InternalViewRef, ViewRef} from '../linker/view_ref';
|
||||||
|
@ -22,7 +22,7 @@ import {stringify} from '../util';
|
||||||
import {VERSION} from '../version';
|
import {VERSION} from '../version';
|
||||||
|
|
||||||
import {callNgModuleLifecycle, initNgModule, resolveNgModuleDep} from './ng_module';
|
import {callNgModuleLifecycle, initNgModule, resolveNgModuleDep} from './ng_module';
|
||||||
import {DepFlags, ElementData, NgModuleData, NgModuleDefinition, NgModuleDefinitionFactory, NodeDef, NodeFlags, Services, TemplateData, ViewContainerData, ViewData, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types';
|
import {DepFlags, ElementData, NgModuleData, NgModuleDefinition, NodeDef, NodeFlags, Services, TemplateData, ViewContainerData, ViewData, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types';
|
||||||
import {markParentViewsForCheck, resolveDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util';
|
import {markParentViewsForCheck, resolveDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util';
|
||||||
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachView} from './view_attach';
|
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachView} from './view_attach';
|
||||||
|
|
||||||
|
@ -43,14 +43,6 @@ export function getComponentViewDefinitionFactory(componentFactory: ComponentFac
|
||||||
return (componentFactory as ComponentFactory_).viewDefFactory;
|
return (componentFactory as ComponentFactory_).viewDefFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attention: this function is called as top level function.
|
|
||||||
// Putting any logic in here will destroy closure tree shaking!
|
|
||||||
export function createNgModuleFactory(
|
|
||||||
ngModuleType: Type<any>, bootstrapComponents: Type<any>[],
|
|
||||||
defFactory: NgModuleDefinitionFactory): NgModuleFactory<any> {
|
|
||||||
return new NgModuleFactory_(ngModuleType, bootstrapComponents, defFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ComponentFactory_ extends ComponentFactory<any> {
|
class ComponentFactory_ extends ComponentFactory<any> {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -312,7 +304,8 @@ class TemplateRef_ extends TemplateRef<any> implements TemplateData {
|
||||||
constructor(private _parentView: ViewData, private _def: NodeDef) { super(); }
|
constructor(private _parentView: ViewData, private _def: NodeDef) { super(); }
|
||||||
|
|
||||||
createEmbeddedView(context: any): EmbeddedViewRef<any> {
|
createEmbeddedView(context: any): EmbeddedViewRef<any> {
|
||||||
return new ViewRef_(Services.createEmbeddedView(this._parentView, this._def, context));
|
return new ViewRef_(Services.createEmbeddedView(
|
||||||
|
this._parentView, this._def, this._def.element !.template !, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
get elementRef(): ElementRef {
|
get elementRef(): ElementRef {
|
||||||
|
@ -464,22 +457,10 @@ class RendererAdapter implements RendererV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NgModuleFactory_ extends NgModuleFactory<any> {
|
export function createNgModuleRef(
|
||||||
constructor(
|
moduleType: Type<any>, parent: Injector, bootstrapComponents: Type<any>[],
|
||||||
private _moduleType: Type<any>, private _bootstrapComponents: Type<any>[],
|
def: NgModuleDefinition): NgModuleRef<any> {
|
||||||
private _ngModuleDefFactory: NgModuleDefinitionFactory, ) {
|
return new NgModuleRef_(moduleType, parent, bootstrapComponents, def);
|
||||||
// Attention: this ctor is called as top level function.
|
|
||||||
// Putting any logic in here will destroy closure tree shaking!
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
get moduleType(): Type<any> { return this._moduleType; }
|
|
||||||
|
|
||||||
create(parentInjector: Injector|null): NgModuleRef<any> {
|
|
||||||
const def = resolveDefinition(this._ngModuleDefFactory);
|
|
||||||
return new NgModuleRef_(
|
|
||||||
this._moduleType, parentInjector || Injector.NULL, this._bootstrapComponents, def);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> {
|
class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> {
|
||||||
|
@ -488,8 +469,8 @@ class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> {
|
||||||
public _providers: any[];
|
public _providers: any[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _moduleType: any, public _parent: Injector, public _bootstrapComponents: Type<any>[],
|
private _moduleType: Type<any>, public _parent: Injector,
|
||||||
public _def: NgModuleDefinition) {
|
public _bootstrapComponents: Type<any>[], public _def: NgModuleDefinition) {
|
||||||
initNgModule(this);
|
initNgModule(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,15 @@ import {ErrorHandler} from '../error_handler';
|
||||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||||
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
|
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
|
||||||
import {Sanitizer} from '../security';
|
import {Sanitizer} from '../security';
|
||||||
|
import {Type} from '../type';
|
||||||
|
|
||||||
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
||||||
import {resolveDep} from './provider';
|
import {resolveDep} from './provider';
|
||||||
import {dirtyParentQueries, getQueryValue} from './query';
|
import {dirtyParentQueries, getQueryValue} from './query';
|
||||||
import {createInjector} from './refs';
|
import {createInjector, createNgModuleRef} from './refs';
|
||||||
import {ArgumentType, BindingFlags, CheckType, DebugContext, ElementData, NodeDef, NodeFlags, NodeLogger, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types';
|
import {ArgumentType, BindingFlags, CheckType, DebugContext, DepDef, ElementData, NgModuleDefinition, NgModuleProviderDef, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types';
|
||||||
import {NOOP, isComponentView, renderNode, viewParentEl} from './util';
|
import {NOOP, isComponentView, renderNode, splitDepsDsl, viewParentEl} from './util';
|
||||||
import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
|
import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view';
|
||||||
|
|
||||||
|
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
|
@ -34,6 +35,10 @@ export function initServicesIfNeeded() {
|
||||||
Services.setCurrentNode = services.setCurrentNode;
|
Services.setCurrentNode = services.setCurrentNode;
|
||||||
Services.createRootView = services.createRootView;
|
Services.createRootView = services.createRootView;
|
||||||
Services.createEmbeddedView = services.createEmbeddedView;
|
Services.createEmbeddedView = services.createEmbeddedView;
|
||||||
|
Services.createComponentView = services.createComponentView;
|
||||||
|
Services.createNgModuleRef = services.createNgModuleRef;
|
||||||
|
Services.overrideProvider = services.overrideProvider;
|
||||||
|
Services.clearProviderOverrides = services.clearProviderOverrides;
|
||||||
Services.checkAndUpdateView = services.checkAndUpdateView;
|
Services.checkAndUpdateView = services.checkAndUpdateView;
|
||||||
Services.checkNoChangesView = services.checkNoChangesView;
|
Services.checkNoChangesView = services.checkNoChangesView;
|
||||||
Services.destroyView = services.destroyView;
|
Services.destroyView = services.destroyView;
|
||||||
|
@ -50,6 +55,10 @@ function createProdServices() {
|
||||||
setCurrentNode: () => {},
|
setCurrentNode: () => {},
|
||||||
createRootView: createProdRootView,
|
createRootView: createProdRootView,
|
||||||
createEmbeddedView: createEmbeddedView,
|
createEmbeddedView: createEmbeddedView,
|
||||||
|
createComponentView: createComponentView,
|
||||||
|
createNgModuleRef: createNgModuleRef,
|
||||||
|
overrideProvider: NOOP,
|
||||||
|
clearProviderOverrides: NOOP,
|
||||||
checkAndUpdateView: checkAndUpdateView,
|
checkAndUpdateView: checkAndUpdateView,
|
||||||
checkNoChangesView: checkNoChangesView,
|
checkNoChangesView: checkNoChangesView,
|
||||||
destroyView: destroyView,
|
destroyView: destroyView,
|
||||||
|
@ -72,13 +81,17 @@ function createDebugServices() {
|
||||||
setCurrentNode: debugSetCurrentNode,
|
setCurrentNode: debugSetCurrentNode,
|
||||||
createRootView: debugCreateRootView,
|
createRootView: debugCreateRootView,
|
||||||
createEmbeddedView: debugCreateEmbeddedView,
|
createEmbeddedView: debugCreateEmbeddedView,
|
||||||
|
createComponentView: debugCreateComponentView,
|
||||||
|
createNgModuleRef: debugCreateNgModuleRef,
|
||||||
|
overrideProvider: debugOverrideProvider,
|
||||||
|
clearProviderOverrides: debugClearProviderOverrides,
|
||||||
checkAndUpdateView: debugCheckAndUpdateView,
|
checkAndUpdateView: debugCheckAndUpdateView,
|
||||||
checkNoChangesView: debugCheckNoChangesView,
|
checkNoChangesView: debugCheckNoChangesView,
|
||||||
destroyView: debugDestroyView,
|
destroyView: debugDestroyView,
|
||||||
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
|
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
|
||||||
handleEvent: debugHandleEvent,
|
handleEvent: debugHandleEvent,
|
||||||
updateDirectives: debugUpdateDirectives,
|
updateDirectives: debugUpdateDirectives,
|
||||||
updateRenderer: debugUpdateRenderer
|
updateRenderer: debugUpdateRenderer,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +111,9 @@ function debugCreateRootView(
|
||||||
const root = createRootData(
|
const root = createRootData(
|
||||||
elInjector, ngModule, new DebugRendererFactory2(rendererFactory), projectableNodes,
|
elInjector, ngModule, new DebugRendererFactory2(rendererFactory), projectableNodes,
|
||||||
rootSelectorOrNode);
|
rootSelectorOrNode);
|
||||||
return callWithDebugContext(DebugAction.create, createRootView, null, [root, def, context]);
|
const defWithOverride = applyProviderOverridesToView(def);
|
||||||
|
return callWithDebugContext(
|
||||||
|
DebugAction.create, createRootView, null, [root, defWithOverride, context]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRootData(
|
function createRootData(
|
||||||
|
@ -114,6 +129,136 @@ function createRootData(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function debugCreateEmbeddedView(
|
||||||
|
parentView: ViewData, anchorDef: NodeDef, viewDef: ViewDefinition, context?: any): ViewData {
|
||||||
|
const defWithOverride = applyProviderOverridesToView(viewDef);
|
||||||
|
return callWithDebugContext(
|
||||||
|
DebugAction.create, createEmbeddedView, null,
|
||||||
|
[parentView, anchorDef, defWithOverride, context]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugCreateComponentView(
|
||||||
|
parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData {
|
||||||
|
const defWithOverride = applyProviderOverridesToView(viewDef);
|
||||||
|
return callWithDebugContext(
|
||||||
|
DebugAction.create, createComponentView, null,
|
||||||
|
[parentView, nodeDef, defWithOverride, hostElement]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugCreateNgModuleRef(
|
||||||
|
moduleType: Type<any>, parentInjector: Injector, bootstrapComponents: Type<any>[],
|
||||||
|
def: NgModuleDefinition): NgModuleRef<any> {
|
||||||
|
const defWithOverride = applyProviderOverridesToNgModule(def);
|
||||||
|
return createNgModuleRef(moduleType, parentInjector, bootstrapComponents, defWithOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerOverrides = new Map<any, ProviderOverride>();
|
||||||
|
|
||||||
|
function debugOverrideProvider(override: ProviderOverride) {
|
||||||
|
providerOverrides.set(override.token, override);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugClearProviderOverrides() {
|
||||||
|
providerOverrides.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notes about the algorithm:
|
||||||
|
// 1) Locate the providers of an element and check if one of them was overwritten
|
||||||
|
// 2) Change the providers of that element
|
||||||
|
//
|
||||||
|
// We only create new datastructures if we need to, to keep perf impact
|
||||||
|
// reasonable.
|
||||||
|
function applyProviderOverridesToView(def: ViewDefinition): ViewDefinition {
|
||||||
|
if (providerOverrides.size === 0) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
const elementIndicesWithOverwrittenProviders = findElementIndicesWithOverwrittenProviders(def);
|
||||||
|
if (elementIndicesWithOverwrittenProviders.length === 0) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
// clone the whole view definition,
|
||||||
|
// as it maintains references between the nodes that are hard to update.
|
||||||
|
def = def.factory !(() => NOOP);
|
||||||
|
for (let i = 0; i < elementIndicesWithOverwrittenProviders.length; i++) {
|
||||||
|
applyProviderOverridesToElement(def, elementIndicesWithOverwrittenProviders[i]);
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
|
||||||
|
function findElementIndicesWithOverwrittenProviders(def: ViewDefinition): number[] {
|
||||||
|
const elIndicesWithOverwrittenProviders: number[] = [];
|
||||||
|
let lastElementDef: NodeDef|null = null;
|
||||||
|
for (let i = 0; i < def.nodes.length; i++) {
|
||||||
|
const nodeDef = def.nodes[i];
|
||||||
|
if (nodeDef.flags & NodeFlags.TypeElement) {
|
||||||
|
lastElementDef = nodeDef;
|
||||||
|
}
|
||||||
|
if (lastElementDef && nodeDef.flags & NodeFlags.CatProviderNoDirective &&
|
||||||
|
providerOverrides.has(nodeDef.provider !.token)) {
|
||||||
|
elIndicesWithOverwrittenProviders.push(lastElementDef !.index);
|
||||||
|
lastElementDef = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return elIndicesWithOverwrittenProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyProviderOverridesToElement(viewDef: ViewDefinition, elIndex: number) {
|
||||||
|
for (let i = elIndex + 1; i < viewDef.nodes.length; i++) {
|
||||||
|
const nodeDef = viewDef.nodes[i];
|
||||||
|
if (nodeDef.flags & NodeFlags.TypeElement) {
|
||||||
|
// stop at the next element
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nodeDef.flags & NodeFlags.CatProviderNoDirective) {
|
||||||
|
// Make all providers lazy, so that we don't get into trouble
|
||||||
|
// with ordering problems of providers on the same element
|
||||||
|
nodeDef.flags |= NodeFlags.LazyProvider;
|
||||||
|
const provider = nodeDef.provider !;
|
||||||
|
const override = providerOverrides.get(provider.token);
|
||||||
|
if (override) {
|
||||||
|
nodeDef.flags = (nodeDef.flags & ~NodeFlags.CatProviderNoDirective) | override.flags;
|
||||||
|
provider.deps = splitDepsDsl(override.deps);
|
||||||
|
provider.value = override.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notes about the algorithm:
|
||||||
|
// We only create new datastructures if we need to, to keep perf impact
|
||||||
|
// reasonable.
|
||||||
|
function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefinition {
|
||||||
|
if (providerOverrides.size === 0 || !hasOverrrides(def)) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
// clone the whole view definition,
|
||||||
|
// as it maintains references between the nodes that are hard to update.
|
||||||
|
def = def.factory !(() => NOOP);
|
||||||
|
applyProviderOverrides(def);
|
||||||
|
return def;
|
||||||
|
|
||||||
|
function hasOverrrides(def: NgModuleDefinition): boolean {
|
||||||
|
return def.providers.some(
|
||||||
|
node =>
|
||||||
|
!!(node.flags & NodeFlags.CatProviderNoDirective) && providerOverrides.has(node.token));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyProviderOverrides(def: NgModuleDefinition) {
|
||||||
|
for (let i = 0; i < def.providers.length; i++) {
|
||||||
|
const provider = def.providers[i];
|
||||||
|
// Make all providers lazy, so that we don't get into trouble
|
||||||
|
// with ordering problems of providers on the same element
|
||||||
|
provider.flags |= NodeFlags.LazyProvider;
|
||||||
|
const override = providerOverrides.get(provider.token);
|
||||||
|
if (override) {
|
||||||
|
provider.flags = (provider.flags & ~NodeFlags.CatProviderNoDirective) | override.flags;
|
||||||
|
provider.deps = splitDepsDsl(override.deps);
|
||||||
|
provider.value = override.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function prodCheckAndUpdateNode(
|
function prodCheckAndUpdateNode(
|
||||||
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
|
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
|
||||||
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any {
|
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any {
|
||||||
|
@ -134,11 +279,6 @@ function prodCheckNoChangesNode(
|
||||||
undefined;
|
undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
|
||||||
return callWithDebugContext(
|
|
||||||
DebugAction.create, createEmbeddedView, null, [parent, anchorDef, context]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function debugCheckAndUpdateView(view: ViewData) {
|
function debugCheckAndUpdateView(view: ViewData) {
|
||||||
return callWithDebugContext(DebugAction.detectChanges, checkAndUpdateView, null, [view]);
|
return callWithDebugContext(DebugAction.detectChanges, checkAndUpdateView, null, [view]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {Renderer2, RendererFactory2, RendererType2} from '../render/api';
|
import {Renderer2, RendererFactory2, RendererType2} from '../render/api';
|
||||||
import {Sanitizer, SecurityContext} from '../security';
|
import {Sanitizer, SecurityContext} from '../security';
|
||||||
|
import {Type} from '../type';
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
// Defs
|
// Defs
|
||||||
|
@ -169,8 +170,9 @@ export const enum NodeFlags {
|
||||||
PrivateProvider = 1 << 13,
|
PrivateProvider = 1 << 13,
|
||||||
TypeDirective = 1 << 14,
|
TypeDirective = 1 << 14,
|
||||||
Component = 1 << 15,
|
Component = 1 << 15,
|
||||||
CatProvider = TypeValueProvider | TypeClassProvider | TypeFactoryProvider |
|
CatProviderNoDirective =
|
||||||
TypeUseExistingProvider | TypeDirective,
|
TypeValueProvider | TypeClassProvider | TypeFactoryProvider | TypeUseExistingProvider,
|
||||||
|
CatProvider = CatProviderNoDirective | TypeDirective,
|
||||||
OnInit = 1 << 16,
|
OnInit = 1 << 16,
|
||||||
OnDestroy = 1 << 17,
|
OnDestroy = 1 << 17,
|
||||||
DoCheck = 1 << 18,
|
DoCheck = 1 << 18,
|
||||||
|
@ -495,12 +497,27 @@ export abstract class DebugContext {
|
||||||
|
|
||||||
export const enum CheckType {CheckAndUpdate, CheckNoChanges}
|
export const enum CheckType {CheckAndUpdate, CheckNoChanges}
|
||||||
|
|
||||||
|
export interface ProviderOverride {
|
||||||
|
token: any;
|
||||||
|
flags: NodeFlags;
|
||||||
|
value: any;
|
||||||
|
deps: ([DepFlags, any]|any)[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface Services {
|
export interface Services {
|
||||||
setCurrentNode(view: ViewData, nodeIndex: number): void;
|
setCurrentNode(view: ViewData, nodeIndex: number): void;
|
||||||
createRootView(
|
createRootView(
|
||||||
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string|any,
|
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string|any,
|
||||||
def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData;
|
def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData;
|
||||||
createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData;
|
createEmbeddedView(parent: ViewData, anchorDef: NodeDef, viewDef: ViewDefinition, context?: any):
|
||||||
|
ViewData;
|
||||||
|
createComponentView(
|
||||||
|
parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData;
|
||||||
|
createNgModuleRef(
|
||||||
|
moduleType: Type<any>, parent: Injector, bootstrapComponents: Type<any>[],
|
||||||
|
def: NgModuleDefinition): NgModuleRef<any>;
|
||||||
|
overrideProvider(override: ProviderOverride): void;
|
||||||
|
clearProviderOverrides(): void;
|
||||||
checkAndUpdateView(view: ViewData): void;
|
checkAndUpdateView(view: ViewData): void;
|
||||||
checkNoChangesView(view: ViewData): void;
|
checkNoChangesView(view: ViewData): void;
|
||||||
destroyView(view: ViewData): void;
|
destroyView(view: ViewData): void;
|
||||||
|
@ -522,6 +539,10 @@ export const Services: Services = {
|
||||||
setCurrentNode: undefined !,
|
setCurrentNode: undefined !,
|
||||||
createRootView: undefined !,
|
createRootView: undefined !,
|
||||||
createEmbeddedView: undefined !,
|
createEmbeddedView: undefined !,
|
||||||
|
createComponentView: undefined !,
|
||||||
|
createNgModuleRef: undefined !,
|
||||||
|
overrideProvider: undefined !,
|
||||||
|
clearProviderOverrides: undefined !,
|
||||||
checkAndUpdateView: undefined !,
|
checkAndUpdateView: undefined !,
|
||||||
checkNoChangesView: undefined !,
|
checkNoChangesView: undefined !,
|
||||||
destroyView: undefined !,
|
destroyView: undefined !,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {RendererType2} from '../render/api';
|
||||||
import {looseIdentical, stringify} from '../util';
|
import {looseIdentical, stringify} from '../util';
|
||||||
|
|
||||||
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
||||||
import {BindingDef, BindingFlags, Definition, DefinitionFactory, ElementData, NodeDef, NodeFlags, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asTextData} from './types';
|
import {BindingDef, BindingFlags, Definition, DefinitionFactory, DepDef, DepFlags, ElementData, NodeDef, NodeFlags, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asTextData} from './types';
|
||||||
|
|
||||||
export const NOOP: any = () => {};
|
export const NOOP: any = () => {};
|
||||||
|
|
||||||
|
@ -203,6 +203,20 @@ export function splitMatchedQueriesDsl(
|
||||||
return {matchedQueries, references, matchedQueryIds};
|
return {matchedQueries, references, matchedQueryIds};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function splitDepsDsl(deps: ([DepFlags, any] | any)[]): DepDef[] {
|
||||||
|
return deps.map(value => {
|
||||||
|
let token: any;
|
||||||
|
let flags: DepFlags;
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
[flags, token] = value;
|
||||||
|
} else {
|
||||||
|
flags = DepFlags.None;
|
||||||
|
token = value;
|
||||||
|
}
|
||||||
|
return {flags, token, tokenKey: tokenKey(token)};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getParentRenderElement(view: ViewData, renderHost: any, def: NodeDef): any {
|
export function getParentRenderElement(view: ViewData, renderHost: any, def: NodeDef): any {
|
||||||
let renderParent = def.renderParent;
|
let renderParent = def.renderParent;
|
||||||
if (renderParent) {
|
if (renderParent) {
|
||||||
|
|
|
@ -184,11 +184,11 @@ function validateNode(parent: NodeDef | null, node: NodeDef, nodeCount: number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
export function createEmbeddedView(
|
||||||
|
parent: ViewData, anchorDef: NodeDef, viewDef: ViewDefinition, context?: any): ViewData {
|
||||||
// embedded views are seen as siblings to the anchor, so we need
|
// embedded views are seen as siblings to the anchor, so we need
|
||||||
// to get the parent of the anchor and use it as parentIndex.
|
// to get the parent of the anchor and use it as parentIndex.
|
||||||
const view =
|
const view = createView(parent.root, parent.renderer, parent, anchorDef, viewDef);
|
||||||
createView(parent.root, parent.renderer, parent, anchorDef, anchorDef.element !.template !);
|
|
||||||
initView(view, parent.component, context);
|
initView(view, parent.component, context);
|
||||||
createViewNodes(view);
|
createViewNodes(view);
|
||||||
return view;
|
return view;
|
||||||
|
@ -201,6 +201,19 @@ export function createRootView(root: RootData, def: ViewDefinition, context?: an
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createComponentView(
|
||||||
|
parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData {
|
||||||
|
const rendererType = nodeDef.element !.componentRendererType;
|
||||||
|
let compRenderer: Renderer2;
|
||||||
|
if (!rendererType) {
|
||||||
|
compRenderer = parentView.root.renderer;
|
||||||
|
} else {
|
||||||
|
compRenderer = parentView.root.rendererFactory.createRenderer(hostElement, rendererType);
|
||||||
|
}
|
||||||
|
return createView(
|
||||||
|
parentView.root, compRenderer, parentView, nodeDef.element !.componentProvider, viewDef);
|
||||||
|
}
|
||||||
|
|
||||||
function createView(
|
function createView(
|
||||||
root: RootData, renderer: Renderer2, parent: ViewData | null, parentNodeDef: NodeDef | null,
|
root: RootData, renderer: Renderer2, parent: ViewData | null, parentNodeDef: NodeDef | null,
|
||||||
def: ViewDefinition): ViewData {
|
def: ViewDefinition): ViewData {
|
||||||
|
@ -241,15 +254,7 @@ function createViewNodes(view: ViewData) {
|
||||||
let componentView: ViewData = undefined !;
|
let componentView: ViewData = undefined !;
|
||||||
if (nodeDef.flags & NodeFlags.ComponentView) {
|
if (nodeDef.flags & NodeFlags.ComponentView) {
|
||||||
const compViewDef = resolveDefinition(nodeDef.element !.componentView !);
|
const compViewDef = resolveDefinition(nodeDef.element !.componentView !);
|
||||||
const rendererType = nodeDef.element !.componentRendererType;
|
componentView = Services.createComponentView(view, nodeDef, compViewDef, el);
|
||||||
let compRenderer: Renderer2;
|
|
||||||
if (!rendererType) {
|
|
||||||
compRenderer = view.root.renderer;
|
|
||||||
} else {
|
|
||||||
compRenderer = view.root.rendererFactory.createRenderer(el, rendererType);
|
|
||||||
}
|
|
||||||
componentView = createView(
|
|
||||||
view.root, compRenderer, view, nodeDef.element !.componentProvider, compViewDef);
|
|
||||||
}
|
}
|
||||||
listenToElementOutputs(view, componentView, nodeDef, el);
|
listenToElementOutputs(view, componentView, nodeDef, el);
|
||||||
nodeData = <ElementData>{
|
nodeData = <ElementData>{
|
||||||
|
|
|
@ -787,14 +787,45 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
expect(child.get(Injector)).toBe(child);
|
expect(child.get(Injector)).toBe(child);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow to inject lazy providers via Injector.get from an eager provider that is declared earlier',
|
describe('injecting lazy providers into an eager provider via Injector.get', () => {
|
||||||
() => {
|
|
||||||
@NgModule({providers: [{provide: 'a', useFactory: () => 'aValue'}]})
|
it('should inject providers that were declared before it', () => {
|
||||||
class SomeModule {
|
@NgModule({
|
||||||
public a: string;
|
providers: [
|
||||||
constructor(injector: Injector) { this.a = injector.get('a'); }
|
{provide: 'lazy', useFactory: () => 'lazyValue'},
|
||||||
|
{
|
||||||
|
provide: 'eager',
|
||||||
|
useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`,
|
||||||
|
deps: [Injector]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyModule {
|
||||||
|
// NgModule is eager, which makes all of its deps eager
|
||||||
|
constructor(@Inject('eager') eager: any) {}
|
||||||
}
|
}
|
||||||
expect(createModule(SomeModule).instance.a).toBe('aValue');
|
|
||||||
|
expect(createModule(MyModule).injector.get('eager')).toBe('eagerValue: lazyValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inject providers that were declared after it', () => {
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: 'eager',
|
||||||
|
useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`,
|
||||||
|
deps: [Injector]
|
||||||
|
},
|
||||||
|
{provide: 'lazy', useFactory: () => 'lazyValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyModule {
|
||||||
|
// NgModule is eager, which makes all of its deps eager
|
||||||
|
constructor(@Inject('eager') eager: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(createModule(MyModule).injector.get('eager')).toBe('eagerValue: lazyValue');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw when no provider defined', () => {
|
it('should throw when no provider defined', () => {
|
||||||
|
|
|
@ -342,6 +342,53 @@ export function main() {
|
||||||
expect(created).toBe(true);
|
expect(created).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('injecting lazy providers into an eager provider via Injector.get', () => {
|
||||||
|
|
||||||
|
it('should inject providers that were declared before it', () => {
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
providers: [
|
||||||
|
{provide: 'lazy', useFactory: () => 'lazyValue'},
|
||||||
|
{
|
||||||
|
provide: 'eager',
|
||||||
|
useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`,
|
||||||
|
deps: [Injector]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
// Component is eager, which makes all of its deps eager
|
||||||
|
constructor(@Inject('eager') eager: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx =
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||||
|
expect(ctx.debugElement.injector.get('eager')).toBe('eagerValue: lazyValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inject providers that were declared after it', () => {
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: 'eager',
|
||||||
|
useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`,
|
||||||
|
deps: [Injector]
|
||||||
|
},
|
||||||
|
{provide: 'lazy', useFactory: () => 'lazyValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
// Component is eager, which makes all of its deps eager
|
||||||
|
constructor(@Inject('eager') eager: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx =
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||||
|
expect(ctx.debugElement.injector.get('eager')).toBe('eagerValue: lazyValue');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should allow injecting lazy providers via Injector.get from an eager provider that is declared earlier',
|
it('should allow injecting lazy providers via Injector.get from an eager provider that is declared earlier',
|
||||||
() => {
|
() => {
|
||||||
@Component({providers: [{provide: 'a', useFactory: () => 'aValue'}], template: ''})
|
@Component({providers: [{provide: 'a', useFactory: () => 'aValue'}], template: ''})
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {ArgumentType, BindingFlags, NodeCheckFn, NodeDef, NodeFlags, RootData, S
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootView, isBrowser} from './helper';
|
import {createEmbeddedView, createRootView, isBrowser} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe(`Embedded Views`, () => {
|
describe(`Embedded Views`, () => {
|
||||||
|
@ -45,8 +45,7 @@ export function main() {
|
||||||
]),
|
]),
|
||||||
parentContext);
|
parentContext);
|
||||||
|
|
||||||
const childView =
|
const childView = createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
||||||
Services.createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
|
||||||
expect(childView.component).toBe(parentContext);
|
expect(childView.component).toBe(parentContext);
|
||||||
expect(childView.context).toBe(childContext);
|
expect(childView.context).toBe(childContext);
|
||||||
});
|
});
|
||||||
|
@ -64,8 +63,8 @@ export function main() {
|
||||||
]));
|
]));
|
||||||
const viewContainerData = asElementData(parentView, 1);
|
const viewContainerData = asElementData(parentView, 1);
|
||||||
|
|
||||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
|
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||||
|
|
||||||
attachEmbeddedView(parentView, viewContainerData, 0, childView0);
|
attachEmbeddedView(parentView, viewContainerData, 0, childView0);
|
||||||
attachEmbeddedView(parentView, viewContainerData, 1, childView1);
|
attachEmbeddedView(parentView, viewContainerData, 1, childView1);
|
||||||
|
@ -95,8 +94,8 @@ export function main() {
|
||||||
]));
|
]));
|
||||||
const viewContainerData = asElementData(parentView, 1);
|
const viewContainerData = asElementData(parentView, 1);
|
||||||
|
|
||||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
|
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||||
|
|
||||||
attachEmbeddedView(parentView, viewContainerData, 0, childView0);
|
attachEmbeddedView(parentView, viewContainerData, 0, childView0);
|
||||||
attachEmbeddedView(parentView, viewContainerData, 1, childView1);
|
attachEmbeddedView(parentView, viewContainerData, 1, childView1);
|
||||||
|
@ -119,7 +118,7 @@ export function main() {
|
||||||
elementDef(NodeFlags.None, null !, null !, 0, 'span', [['name', 'after']])
|
elementDef(NodeFlags.None, null !, null !, 0, 'span', [['name', 'after']])
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[0]);
|
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[0]);
|
||||||
attachEmbeddedView(parentView, asElementData(parentView, 0), 0, childView0);
|
attachEmbeddedView(parentView, asElementData(parentView, 0), 0, childView0);
|
||||||
|
|
||||||
const rootNodes = rootRenderNodes(parentView);
|
const rootNodes = rootRenderNodes(parentView);
|
||||||
|
@ -146,7 +145,7 @@ export function main() {
|
||||||
update))
|
update))
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
|
|
||||||
attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0);
|
attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0);
|
||||||
|
|
||||||
|
@ -180,7 +179,7 @@ export function main() {
|
||||||
]))
|
]))
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
|
|
||||||
attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0);
|
attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0);
|
||||||
Services.destroyView(parentView);
|
Services.destroyView(parentView);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, NgModuleRef, 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 {ArgumentType, NodeCheckFn, NodeDef, RootData, Services, ViewData, ViewDefinition, initServicesIfNeeded} from '@angular/core/src/view/index';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@ -37,6 +37,10 @@ export function createRootView(
|
||||||
TestBed.get(NgModuleRef), context);
|
TestBed.get(NgModuleRef), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||||
|
return Services.createEmbeddedView(parent, anchorDef, anchorDef.element !.template !, context);
|
||||||
|
}
|
||||||
|
|
||||||
export let removeNodes: Node[];
|
export let removeNodes: Node[];
|
||||||
beforeEach(() => { removeNodes = []; });
|
beforeEach(() => { removeNodes = []; });
|
||||||
afterEach(() => { removeNodes.forEach((node) => getDOM().remove(node)); });
|
afterEach(() => { removeNodes.forEach((node) => getDOM().remove(node)); });
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext,
|
||||||
import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootView, isBrowser} from './helper';
|
import {createEmbeddedView, createRootView, isBrowser} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe(`View NgContent`, () => {
|
describe(`View NgContent`, () => {
|
||||||
|
@ -121,7 +121,7 @@ export function main() {
|
||||||
])));
|
])));
|
||||||
|
|
||||||
const componentView = asElementData(view, 0).componentView;
|
const componentView = asElementData(view, 0).componentView;
|
||||||
const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]);
|
const view0 = createEmbeddedView(componentView, componentView.def.nodes[1]);
|
||||||
|
|
||||||
attachEmbeddedView(view, asElementData(componentView, 1), 0, view0);
|
attachEmbeddedView(view, asElementData(componentView, 1), 0, view0);
|
||||||
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(3);
|
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(3);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {BindingFlags, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryV
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootView} from './helper';
|
import {createEmbeddedView, createRootView} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe(`Query Views`, () => {
|
describe(`Query Views`, () => {
|
||||||
|
@ -155,7 +155,7 @@ export function main() {
|
||||||
...contentQueryProviders(),
|
...contentQueryProviders(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||||
attachEmbeddedView(view, asElementData(view, 3), 0, childView);
|
attachEmbeddedView(view, asElementData(view, 3), 0, childView);
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ export function main() {
|
||||||
anchorDef(NodeFlags.EmbeddedViews, null !, null !, 0),
|
anchorDef(NodeFlags.EmbeddedViews, null !, null !, 0),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||||
// attach at a different place than the one where the template was defined
|
// attach at a different place than the one where the template was defined
|
||||||
attachEmbeddedView(view, asElementData(view, 7), 0, childView);
|
attachEmbeddedView(view, asElementData(view, 7), 0, childView);
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ export function main() {
|
||||||
const qs: QueryService = asProviderData(view, 1).instance;
|
const qs: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(qs.a.length).toBe(0);
|
expect(qs.a.length).toBe(0);
|
||||||
|
|
||||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||||
attachEmbeddedView(view, asElementData(view, 3), 0, childView);
|
attachEmbeddedView(view, asElementData(view, 3), 0, childView);
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ export function main() {
|
||||||
expect(comp.a.length).toBe(0);
|
expect(comp.a.length).toBe(0);
|
||||||
|
|
||||||
const compView = asElementData(view, 0).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
const childView = Services.createEmbeddedView(compView, compView.def.nodes[1]);
|
const childView = createEmbeddedView(compView, compView.def.nodes[1]);
|
||||||
attachEmbeddedView(view, asElementData(compView, 1), 0, childView);
|
attachEmbeddedView(view, asElementData(compView, 1), 0, childView);
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, ɵERROR_COMPONENT_TYPE, ɵstringify as stringify} from '@angular/core';
|
import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, SkipSelf, Type, ɵDepFlags as DepFlags, ɵERROR_COMPONENT_TYPE, ɵNodeFlags as NodeFlags, ɵclearProviderOverrides as clearProviderOverrides, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
|
||||||
|
|
||||||
import {AsyncTestCompleter} from './async_test_completer';
|
import {AsyncTestCompleter} from './async_test_completer';
|
||||||
import {ComponentFixture} from './component_fixture';
|
import {ComponentFixture} from './component_fixture';
|
||||||
|
@ -141,6 +141,24 @@ export class TestBed implements Injector {
|
||||||
return TestBed;
|
return TestBed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrites all providers for the given token with the given provider definition.
|
||||||
|
*/
|
||||||
|
static overrideProvider(token: any, provider: {
|
||||||
|
useFactory: Function,
|
||||||
|
deps: any[],
|
||||||
|
}): void;
|
||||||
|
static overrideProvider(token: any, provider: {useValue: any;}): void;
|
||||||
|
static overrideProvider(token: any, provider: {
|
||||||
|
useFactory?: Function,
|
||||||
|
useValue?: any,
|
||||||
|
deps?: any[],
|
||||||
|
}): typeof TestBed {
|
||||||
|
getTestBed().overrideProvider(token, provider as any);
|
||||||
|
return TestBed;
|
||||||
|
}
|
||||||
|
|
||||||
static get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND) {
|
static get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND) {
|
||||||
return getTestBed().get(token, notFoundValue);
|
return getTestBed().get(token, notFoundValue);
|
||||||
}
|
}
|
||||||
|
@ -212,6 +230,7 @@ export class TestBed implements Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
resetTestingModule() {
|
resetTestingModule() {
|
||||||
|
clearProviderOverrides();
|
||||||
this._compiler = null !;
|
this._compiler = null !;
|
||||||
this._moduleOverrides = [];
|
this._moduleOverrides = [];
|
||||||
this._componentOverrides = [];
|
this._componentOverrides = [];
|
||||||
|
@ -364,6 +383,49 @@ export class TestBed implements Injector {
|
||||||
this._pipeOverrides.push([pipe, override]);
|
this._pipeOverrides.push([pipe, override]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrites all providers for the given token with the given provider definition.
|
||||||
|
*/
|
||||||
|
overrideProvider(token: any, provider: {
|
||||||
|
useFactory: Function,
|
||||||
|
deps: any[],
|
||||||
|
}): void;
|
||||||
|
overrideProvider(token: any, provider: {useValue: any;}): void;
|
||||||
|
overrideProvider(token: any, provider: {
|
||||||
|
useFactory?: Function,
|
||||||
|
useValue?: any,
|
||||||
|
deps?: any[],
|
||||||
|
}): void {
|
||||||
|
let flags: NodeFlags = 0;
|
||||||
|
let value: any;
|
||||||
|
if (provider.useFactory) {
|
||||||
|
flags |= NodeFlags.TypeFactoryProvider;
|
||||||
|
value = provider.useFactory;
|
||||||
|
} else {
|
||||||
|
flags |= NodeFlags.TypeValueProvider;
|
||||||
|
value = provider.useValue;
|
||||||
|
}
|
||||||
|
const deps = (provider.deps || []).map((dep) => {
|
||||||
|
let depFlags: DepFlags = DepFlags.None;
|
||||||
|
let depToken: any;
|
||||||
|
if (Array.isArray(dep)) {
|
||||||
|
dep.forEach((entry: any) => {
|
||||||
|
if (entry instanceof Optional) {
|
||||||
|
depFlags |= DepFlags.Optional;
|
||||||
|
} else if (entry instanceof SkipSelf) {
|
||||||
|
depFlags |= DepFlags.SkipSelf;
|
||||||
|
} else {
|
||||||
|
depToken = entry;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
depToken = dep;
|
||||||
|
}
|
||||||
|
return [depFlags, depToken];
|
||||||
|
});
|
||||||
|
overrideProvider({token, flags, deps, value});
|
||||||
|
}
|
||||||
|
|
||||||
createComponent<T>(component: Type<T>): ComponentFixture<T> {
|
createComponent<T>(component: Type<T>): ComponentFixture<T> {
|
||||||
this._initIfNeeded();
|
this._initIfNeeded();
|
||||||
const componentFactory = this._compiler.getComponentFactory(component);
|
const componentFactory = this._compiler.getComponentFactory(component);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompilerConfig, ResourceLoader} from '@angular/compiler';
|
import {CompilerConfig, ResourceLoader} from '@angular/compiler';
|
||||||
import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, Injectable, Input, NgModule, Pipe, ɵstringify as stringify} from '@angular/core';
|
import {CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, Directive, Inject, Injectable, Injector, Input, NgModule, Optional, Pipe, SkipSelf, ɵstringify as stringify} from '@angular/core';
|
||||||
import {TestBed, async, fakeAsync, getTestBed, inject, tick, withModule} from '@angular/core/testing';
|
import {TestBed, async, fakeAsync, getTestBed, inject, tick, withModule} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
|
||||||
|
@ -401,6 +401,277 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('overriding providers', () => {
|
||||||
|
describe('in NgModules', () => {
|
||||||
|
it('should support useValue', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
TestBed.overrideProvider('a', {useValue: 'mockValue'});
|
||||||
|
expect(TestBed.get('a')).toBe('mockValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support useFactory', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{provide: 'dep', useValue: 'depValue'},
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (dep: any) => `mockA: ${dep}`, deps: ['dep']});
|
||||||
|
expect(TestBed.get('a')).toBe('mockA: depValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support @Optional without matches', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (dep: any) => `mockA: ${dep}`, deps: [[new Optional(), 'dep']]});
|
||||||
|
expect(TestBed.get('a')).toBe('mockA: null');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support Optional with matches', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{provide: 'dep', useValue: 'depValue'},
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (dep: any) => `mockA: ${dep}`, deps: [[new Optional(), 'dep']]});
|
||||||
|
expect(TestBed.get('a')).toBe('mockA: depValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support SkipSelf', () => {
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
{provide: 'dep', useValue: 'depValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (dep: any) => `mockA: ${dep}`, deps: [[new SkipSelf(), 'dep']]});
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{providers: [{provide: 'dep', useValue: 'parentDepValue'}]});
|
||||||
|
|
||||||
|
const compiler = TestBed.get(Compiler) as Compiler;
|
||||||
|
const modFactory = compiler.compileModuleSync(MyModule);
|
||||||
|
expect(modFactory.create(getTestBed()).injector.get('a')).toBe('mockA: parentDepValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('injecting eager providers into an eager overwritten provider', () => {
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useFactory: () => 'aValue'},
|
||||||
|
{provide: 'b', useFactory: () => 'bValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyModule {
|
||||||
|
// NgModule is eager, which makes all of its deps eager
|
||||||
|
constructor(@Inject('a') a: any, @Inject('b') b: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should inject providers that were declared before', () => {
|
||||||
|
TestBed.configureTestingModule({imports: [MyModule]});
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'b', {useFactory: (a: string) => `mockB: ${a}`, deps: ['a']});
|
||||||
|
|
||||||
|
expect(TestBed.get('b')).toBe('mockB: aValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inject providers that were declared afterwards', () => {
|
||||||
|
TestBed.configureTestingModule({imports: [MyModule]});
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (b: string) => `mockA: ${b}`, deps: ['b']});
|
||||||
|
|
||||||
|
expect(TestBed.get('a')).toBe('mockA: bValue');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('in Components', () => {
|
||||||
|
it('should support useValue', () => {
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.overrideProvider('a', {useValue: 'mockValue'});
|
||||||
|
const ctx =
|
||||||
|
TestBed.configureTestingModule({declarations: [MComp]}).createComponent(MComp);
|
||||||
|
|
||||||
|
expect(ctx.debugElement.injector.get('a')).toBe('mockValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support useFactory', () => {
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
providers: [
|
||||||
|
{provide: 'dep', useValue: 'depValue'},
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (dep: any) => `mockA: ${dep}`, deps: ['dep']});
|
||||||
|
const ctx =
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||||
|
|
||||||
|
expect(ctx.debugElement.injector.get('a')).toBe('mockA: depValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support @Optional without matches', () => {
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (dep: any) => `mockA: ${dep}`, deps: [[new Optional(), 'dep']]});
|
||||||
|
const ctx =
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||||
|
|
||||||
|
expect(ctx.debugElement.injector.get('a')).toBe('mockA: null');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support Optional with matches', () => {
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
providers: [
|
||||||
|
{provide: 'dep', useValue: 'depValue'},
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (dep: any) => `mockA: ${dep}`, deps: [[new Optional(), 'dep']]});
|
||||||
|
const ctx =
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||||
|
|
||||||
|
expect(ctx.debugElement.injector.get('a')).toBe('mockA: depValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support SkipSelf', () => {
|
||||||
|
@Directive({
|
||||||
|
selector: '[myDir]',
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useValue: 'aValue'},
|
||||||
|
{provide: 'dep', useValue: 'depValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyDir {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<div myDir></div>',
|
||||||
|
providers: [
|
||||||
|
{provide: 'dep', useValue: 'parentDepValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (dep: any) => `mockA: ${dep}`, deps: [[new SkipSelf(), 'dep']]});
|
||||||
|
const ctx = TestBed.configureTestingModule({declarations: [MyComp, MyDir]})
|
||||||
|
.createComponent(MyComp);
|
||||||
|
expect(ctx.debugElement.children[0].injector.get('a')).toBe('mockA: parentDepValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support multiple providers in a template', () => {
|
||||||
|
@Directive({
|
||||||
|
selector: '[myDir1]',
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useValue: 'aValue1'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyDir1 {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[myDir2]',
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useValue: 'aValue2'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyDir2 {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<div myDir1></div><div myDir2></div>',
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.overrideProvider('a', {useValue: 'mockA'});
|
||||||
|
const ctx = TestBed.configureTestingModule({declarations: [MyComp, MyDir1, MyDir2]})
|
||||||
|
.createComponent(MyComp);
|
||||||
|
expect(ctx.debugElement.children[0].injector.get('a')).toBe('mockA');
|
||||||
|
expect(ctx.debugElement.children[1].injector.get('a')).toBe('mockA');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('injecting eager providers into an eager overwritten provider', () => {
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
providers: [
|
||||||
|
{provide: 'a', useFactory: () => 'aValue'},
|
||||||
|
{provide: 'b', useFactory: () => 'bValue'},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
// Component is eager, which makes all of its deps eager
|
||||||
|
constructor(@Inject('a') a: any, @Inject('b') b: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should inject providers that were declared before it', () => {
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'b', {useFactory: (a: string) => `mockB: ${a}`, deps: ['a']});
|
||||||
|
const ctx =
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||||
|
|
||||||
|
expect(ctx.debugElement.injector.get('b')).toBe('mockB: aValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should inject providers that were declared after it', () => {
|
||||||
|
TestBed.overrideProvider(
|
||||||
|
'a', {useFactory: (b: string) => `mockA: ${b}`, deps: ['b']});
|
||||||
|
const ctx =
|
||||||
|
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||||
|
|
||||||
|
expect(ctx.debugElement.injector.get('a')).toBe('mockA: bValue');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset overrides when the testing modules is resetted', () => {
|
||||||
|
TestBed.overrideProvider('a', {useValue: 'mockValue'});
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
TestBed.configureTestingModule({providers: [{provide: 'a', useValue: 'aValue'}]});
|
||||||
|
expect(TestBed.get('a')).toBe('aValue');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('setting up the compiler', () => {
|
describe('setting up the compiler', () => {
|
||||||
|
|
||||||
describe('providers', () => {
|
describe('providers', () => {
|
||||||
|
|
|
@ -75,6 +75,13 @@ export declare class TestBed implements Injector {
|
||||||
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
|
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
|
||||||
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
|
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
|
||||||
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void;
|
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void;
|
||||||
|
overrideProvider(token: any, provider: {
|
||||||
|
useFactory: Function;
|
||||||
|
deps: any[];
|
||||||
|
}): void;
|
||||||
|
overrideProvider(token: any, provider: {
|
||||||
|
useValue: any;
|
||||||
|
}): void;
|
||||||
/** @experimental */ resetTestEnvironment(): void;
|
/** @experimental */ resetTestEnvironment(): void;
|
||||||
resetTestingModule(): void;
|
resetTestingModule(): void;
|
||||||
static compileComponents(): Promise<any>;
|
static compileComponents(): Promise<any>;
|
||||||
|
@ -90,6 +97,13 @@ export declare class TestBed implements Injector {
|
||||||
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): typeof TestBed;
|
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): typeof TestBed;
|
||||||
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed;
|
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed;
|
||||||
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): typeof TestBed;
|
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): typeof TestBed;
|
||||||
|
static overrideProvider(token: any, provider: {
|
||||||
|
useFactory: Function;
|
||||||
|
deps: any[];
|
||||||
|
}): void;
|
||||||
|
static overrideProvider(token: any, provider: {
|
||||||
|
useValue: any;
|
||||||
|
}): void;
|
||||||
static overrideTemplate(component: Type<any>, template: string): typeof TestBed;
|
static overrideTemplate(component: Type<any>, template: string): typeof TestBed;
|
||||||
/** @experimental */ static resetTestEnvironment(): void;
|
/** @experimental */ static resetTestEnvironment(): void;
|
||||||
static resetTestingModule(): typeof TestBed;
|
static resetTestingModule(): typeof TestBed;
|
||||||
|
|
Loading…
Reference in New Issue