feat(ivy): support injection even if no injector present (#23345)

- Remove default injection value from `inject` / `directiveInject` since
  it is not possible to set using annotations.
- Module `Injector` is stored on `LView` instead of `LInjector` data
  structure because it can change only at `LView` level. (More efficient)
- Add `ngInjectableDef` to `IterableDiffers` so that existing tests can
  pass as well as enable `IterableDiffers` to be injectable without
  `Injector`

PR Close #23345
This commit is contained in:
Misko Hevery 2018-04-12 15:54:16 -07:00 committed by Igor Minar
parent 6f213a74f2
commit da31db757b
21 changed files with 175 additions and 105 deletions

View File

@ -2237,7 +2237,7 @@ describe('ngc transformer command-line', () => {
constructor(e: Existing|null) {}
}
`);
expect(source).toMatch(/ngInjectableDef.*return ..\(..\.inject\(Existing, null, 0\)/);
expect(source).toMatch(/ngInjectableDef.*return ..\(..\.inject\(Existing, 8\)/);
});
it('compiles a useFactory InjectableDef with skip-self dep', () => {
@ -2257,7 +2257,7 @@ describe('ngc transformer command-line', () => {
constructor(e: Existing) {}
}
`);
expect(source).toMatch(/ngInjectableDef.*return ..\(..\.inject\(Existing, undefined, 4\)/);
expect(source).toMatch(/ngInjectableDef.*return ..\(..\.inject\(Existing, 4\)/);
});
it('compiles a service that depends on a token', () => {

View File

@ -38,7 +38,6 @@ export class InjectableCompiler {
private depsArray(deps: any[], ctx: OutputContext): o.Expression[] {
return deps.map(dep => {
let token = dep;
let defaultValue = undefined;
let args = [token];
let flags: InjectFlags = InjectFlags.Default;
if (Array.isArray(dep)) {
@ -46,7 +45,7 @@ export class InjectableCompiler {
const v = dep[i];
if (v) {
if (v.ngMetadataName === 'Optional') {
defaultValue = null;
flags |= InjectFlags.Optional;
} else if (v.ngMetadataName === 'SkipSelf') {
flags |= InjectFlags.SkipSelf;
} else if (v.ngMetadataName === 'Self') {
@ -69,8 +68,8 @@ export class InjectableCompiler {
tokenExpr = ctx.importExpr(token);
}
if (flags !== InjectFlags.Default || defaultValue !== undefined) {
args = [tokenExpr, o.literal(defaultValue), o.literal(flags)];
if (flags !== InjectFlags.Default) {
args = [tokenExpr, o.literal(flags)];
} else {
args = [tokenExpr];
}

View File

@ -948,7 +948,7 @@ export function createFactory(
const flags = extractFlags(dependency);
if (flags != InjectFlags.Default) {
// Append flag information if other than default.
directiveInjectArgs.push(o.literal(undefined), o.literal(flags));
directiveInjectArgs.push(o.literal(flags));
}
args.push(o.importExpr(R3.directiveInject).callFn(directiveInjectArgs));
}

View File

@ -53,11 +53,11 @@ describe('compiler compliance: dependency injection', () => {
return new MyComponent(
$r3$.ɵinjectAttribute('name'),
$r3$.ɵdirectiveInject(MyService),
$r3$.ɵdirectiveInject(MyService, (undefined as any), 1),
$r3$.ɵdirectiveInject(MyService, (undefined as any), 2),
$r3$.ɵdirectiveInject(MyService, (undefined as any), 4),
$r3$.ɵdirectiveInject(MyService, (undefined as any), 8),
$r3$.ɵdirectiveInject(MyService, (undefined as any), 10)
$r3$.ɵdirectiveInject(MyService, 1),
$r3$.ɵdirectiveInject(MyService, 2),
$r3$.ɵdirectiveInject(MyService, 4),
$r3$.ɵdirectiveInject(MyService, 8),
$r3$.ɵdirectiveInject(MyService, 10)
);
}`;

View File

@ -6,8 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectableDef, defineInjectable} from '../../di/defs';
import {Optional, SkipSelf} from '../../di/metadata';
import {StaticProvider} from '../../di/provider';
import {DefaultIterableDifferFactory} from '../differs/default_iterable_differ';
/**
@ -135,6 +137,11 @@ export interface IterableDifferFactory {
*
*/
export class IterableDiffers {
static ngInjectableDef: InjectableDef<IterableDiffers> = defineInjectable({
providedIn: 'root',
factory: () => new IterableDiffers([new DefaultIterableDifferFactory()])
});
/**
* @deprecated v4.0.0 - Should be private
*/

View File

@ -13,7 +13,7 @@ export {devModeEqual as ɵdevModeEqual} from './change_detection/change_detectio
export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/change_detection_util';
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
export {Console as ɵConsole} from './console';
export {setCurrentInjector as ɵsetCurrentInjector} from './di/injector';
export {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector';
export {APP_ROOT as ɵAPP_ROOT} from './di/scope';
export {ComponentFactory as ɵComponentFactory} from './linker/component_factory';
export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver';

View File

@ -25,8 +25,25 @@ import {ClassProvider, ClassSansProvider, ConstructorProvider, ConstructorSansPr
* @experimental
*/
export interface InjectableDef<T> {
/**
* Specifies that the given type belongs to a particular injector:
* - `InjectorType` such as `NgModule`,
* - `'root'` the root injector
* - `'any'` all injectors.
* - `null`, does not belong to any injector. Must be explicitly listed in the injector
* `providers`.
*/
providedIn: InjectorType<any>|'root'|'any'|null;
/**
* Factory method to execute to create an instance of the injectable.
*/
factory: () => T;
/**
* In a case of no explicit injector, a location where the instance of the injectable is stored.
*/
value: T|undefined;
}
/**
@ -101,12 +118,13 @@ export interface InjectorTypeWithProviders<T> {
* @experimental
*/
export function defineInjectable<T>(opts: {
providedIn?: Type<any>| 'root' | null,
providedIn?: Type<any>| 'root' | 'any' | null,
factory: () => T,
}): InjectableDef<T> {
return {
providedIn: (opts.providedIn as InjectorType<any>| 'root' | null | undefined) || null,
providedIn: opts.providedIn as any || null,
factory: opts.factory,
value: undefined,
};
}

View File

@ -428,9 +428,15 @@ export const enum InjectFlags {
Optional = 1 << 3,
}
let _currentInjector: Injector|null = null;
/**
* Current injector value used by `inject`.
* - `undefined`: it is an error to call `inject`
* - `null`: `inject` can be called but there is no injector (limp-mode).
* - Injector instance: Use the injector for resolution.
*/
let _currentInjector: Injector|undefined|null = undefined;
export function setCurrentInjector(injector: Injector | null): Injector|null {
export function setCurrentInjector(injector: Injector | null | undefined): Injector|undefined|null {
const former = _currentInjector;
_currentInjector = injector;
return former;
@ -450,19 +456,21 @@ export function setCurrentInjector(injector: Injector | null): Injector|null {
*
* @experimental
*/
export function inject<T>(
token: Type<T>| InjectionToken<T>, notFoundValue?: undefined, flags?: InjectFlags): T;
export function inject<T>(
token: Type<T>| InjectionToken<T>, notFoundValue: T, flags?: InjectFlags): T;
export function inject<T>(
token: Type<T>| InjectionToken<T>, notFoundValue: null, flags?: InjectFlags): T|null;
export function inject<T>(
token: Type<T>| InjectionToken<T>, notFoundValue?: T | null, flags = InjectFlags.Default): T|
null {
if (_currentInjector === null) {
export function inject<T>(token: Type<T>| InjectionToken<T>): T;
export function inject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
export function inject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
if (_currentInjector === undefined) {
throw new Error(`inject() must be called from an injection context`);
} else if (_currentInjector === null) {
const injectableDef: InjectableDef<T> = (token as any).ngInjectableDef;
if (injectableDef && injectableDef.providedIn == 'root') {
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
injectableDef.value;
}
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
} else {
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
}
return _currentInjector.get(token, notFoundValue, flags);
}
export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): any[] {
@ -474,13 +482,12 @@ export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): an
throw new Error('Arguments array must have arguments.');
}
let type: Type<any>|undefined = undefined;
let defaultValue: null|undefined = undefined;
let flags: InjectFlags = InjectFlags.Default;
for (let j = 0; j < arg.length; j++) {
const meta = arg[j];
if (meta instanceof Optional || meta.__proto__.ngMetadataName === 'Optional') {
defaultValue = null;
flags |= InjectFlags.Optional;
} else if (meta instanceof SkipSelf || meta.__proto__.ngMetadataName === 'SkipSelf') {
flags |= InjectFlags.SkipSelf;
} else if (meta instanceof Self || meta.__proto__.ngMetadataName === 'Self') {
@ -492,7 +499,7 @@ export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): an
}
}
args.push(inject(type !, defaultValue, InjectFlags.Default));
args.push(inject(type !, flags));
} else {
args.push(inject(arg));
}

View File

@ -132,10 +132,11 @@ export function renderComponent<T>(
scheduler: opts.scheduler || requestAnimationFrame.bind(window),
clean: CLEAN_PROMISE,
};
const rootView = createLView(
const rootView: LView = createLView(
-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType),
createTView(null, null), null, rootContext,
componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
rootView.injector = opts.injector || null;
const oldView = enterView(rootView, null !);
let elementNode: LElementNode;

View File

@ -9,7 +9,7 @@
// We are temporarily importing the existing viewEngine_from core so we can be sure we are
// correctly implementing its interfaces for backwards compatibility.
import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {InjectFlags, Injector} from '../di/injector';
import {InjectFlags, Injector, inject, setCurrentInjector} from '../di/injector';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
@ -125,7 +125,6 @@ export function getOrCreateNodeInjectorForNode(node: LElementNode | LContainerNo
cbf5: parentInjector == null ? 0 : parentInjector.cbf5 | parentInjector.bf5,
cbf6: parentInjector == null ? 0 : parentInjector.cbf6 | parentInjector.bf6,
cbf7: parentInjector == null ? 0 : parentInjector.cbf7 | parentInjector.bf7,
injector: null,
templateRef: null,
viewContainerRef: null,
elementRef: null,
@ -133,16 +132,6 @@ export function getOrCreateNodeInjectorForNode(node: LElementNode | LContainerNo
};
}
/**
* Constructs an injection error with the given text and token.
*
* @param text The text of the error
* @param token The token associated with the error
* @returns The error that was created
*/
function createInjectionError(text: string, token: any) {
return new Error(`ElementInjector: ${text} [${stringify(token)}]`);
}
/**
* Makes a directive public to the DI system by adding it to an injector's bloom filter.
@ -188,14 +177,10 @@ export function diPublic(def: DirectiveDef<any>): void {
* @param flags Injection flags (e.g. CheckParent)
* @returns The instance found
*/
export function directiveInject<T>(
token: Type<T>, notFoundValue?: undefined, flags?: InjectFlags): T;
export function directiveInject<T>(token: Type<T>, notFoundValue: T, flags?: InjectFlags): T;
export function directiveInject<T>(token: Type<T>, notFoundValue: null, flags?: InjectFlags): T|
null;
export function directiveInject<T>(
token: Type<T>, notFoundValue?: T | null, flags = InjectFlags.Default): T|null {
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags, notFoundValue);
export function directiveInject<T>(token: Type<T>): T;
export function directiveInject<T>(token: Type<T>, flags?: InjectFlags): T|null;
export function directiveInject<T>(token: Type<T>, flags = InjectFlags.Default): T|null {
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags);
}
/**
@ -344,21 +329,20 @@ function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNo
* @param flags Injection flags (e.g. CheckParent)
* @returns The instance found
*/
export function getOrCreateInjectable<T>(
di: LInjector, token: Type<T>, flags?: InjectFlags, defaultValue?: T | null): T|null {
export function getOrCreateInjectable<T>(di: LInjector, token: Type<T>, flags?: InjectFlags): T|
null {
const bloomHash = bloomHashBit(token);
// If the token has a bloom hash, then it is a directive that is public to the injection system
// (diPublic). If there is no hash, fall back to the module injector.
if (bloomHash === null) {
const moduleInjector = di.injector;
if (!moduleInjector) {
if (defaultValue != null) {
return defaultValue;
}
throw createInjectionError('NotFound', token);
const moduleInjector = getPreviousOrParentNode().view.injector;
const formerInjector = setCurrentInjector(moduleInjector);
try {
return inject(token, flags);
} finally {
setCurrentInjector(formerInjector);
}
moduleInjector.get(token);
} else {
let injector: LInjector|null = di;
@ -409,7 +393,7 @@ export function getOrCreateInjectable<T>(
// No directive was found for the given token.
// TODO: implement optional, check-self, and check-parent.
throw createInjectionError('Not found', token);
throw new Error('Implement');
}
function searchMatchesQueuedForCreation<T>(node: LNode, token: any): T|null {

View File

@ -301,6 +301,7 @@ export function createLView<T>(
dynamicViewCount: 0,
lifecycleStage: LifecycleStage.Init,
queries: null,
injector: currentView && currentView.injector,
};
return newView;

View File

@ -7,7 +7,6 @@
*/
import {ChangeDetectorRef} from '../../change_detection/change_detector_ref';
import {Injector} from '../../di/injector';
import {ElementRef} from '../../linker/element_ref';
import {TemplateRef} from '../../linker/template_ref';
import {ViewContainerRef} from '../../linker/view_container_ref';
@ -69,8 +68,6 @@ export interface LInjector {
cbf6: number;
cbf7: number;
injector: Injector|null;
/** Stores the TemplateRef so subsequent injections of the TemplateRef get the same instance. */
templateRef: TemplateRef<any>|null;

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injector} from '../../di/injector';
import {LContainer} from './container';
import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDef, PipeDefList} from './definition';
import {LElementNode, LViewNode, TNode} from './node';
@ -189,6 +190,11 @@ export interface LView {
* Queries active for this view - nodes from a view are reported to those queries
*/
queries: LQueries|null;
/**
* An optional Module Injector to be used as fall back after Element Injectors are consulted.
*/
injector: Injector|null;
}
/** Flags associated with an LView (saved in LView.flags) */

View File

@ -19,8 +19,7 @@ NgForOf.ngDirectiveDef = defineDirective({
type: NgForOfDef,
selectors: [['', 'ngForOf', '']],
factory: () => new NgForOfDef(
injectViewContainerRef(), injectTemplateRef(),
directiveInject(IterableDiffers, defaultIterableDiffers, InjectFlags.Default)),
injectViewContainerRef(), injectTemplateRef(), directiveInject(IterableDiffers)),
features: [NgOnChangesFeature()],
inputs: {
ngForOf: 'ngForOf',

View File

@ -185,7 +185,7 @@ describe('injection', () => {
// NORMATIVE
static ngInjectableDef = defineInjectable({
factory: function ServiceA_Factory() {
return new ServiceB(inject(ServiceA), inject(INJECTOR, undefined, InjectFlags.SkipSelf));
return new ServiceB(inject(ServiceA), inject(INJECTOR, InjectFlags.SkipSelf) !);
},
});
// /NORMATIVE

View File

@ -71,7 +71,7 @@ xdescribe('NgModule', () => {
static ngInjectableDef = defineInjectable({
providedIn: MyModule,
factory: () => new BurntToast(
$r3$.ɵdirectiveInject(Toast, undefined, $core$.InjectFlags.Optional),
$r3$.ɵdirectiveInject(Toast, $core$.InjectFlags.Optional),
$r3$.ɵdirectiveInject(String)),
});
// /NORMATIVE

View File

@ -7,15 +7,15 @@
*/
import {DoCheck, ViewEncapsulation} from '../../src/core';
import {ComponentFactory, DoCheck, ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
import {getRenderedText} from '../../src/render3/component';
import {LifecycleHooksFeature, defineComponent, markDirty} from '../../src/render3/index';
import {LifecycleHooksFeature, defineComponent, directiveInject, markDirty} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, text, textBinding, tick} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {createRendererType2} from '../../src/view/index';
import {getRendererFactory2} from './imported_renderer2';
import {containerEl, renderComponent, renderToHtml, requestAnimationFrame, toHtml} from './render_util';
import {ComponentFixture, containerEl, renderComponent, renderToHtml, requestAnimationFrame, toHtml} from './render_util';
describe('component', () => {
class CounterComponent {
@ -60,6 +60,45 @@ describe('component', () => {
expect(toHtml(containerEl)).toEqual('124');
});
class MyService {
constructor(public value: string) {}
static ngInjectableDef =
defineInjectable({providedIn: 'root', factory: () => new MyService('no-injector')});
}
class MyComponent {
constructor(public myService: MyService) {}
static ngComponentDef = defineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: () => new MyComponent(directiveInject(MyService)),
template: function(fs: RenderFlags, ctx: MyComponent) {
if (fs & RenderFlags.Create) {
text(0);
}
if (fs & RenderFlags.Update) {
textBinding(0, bind(ctx.myService.value));
}
}
});
}
class MyModule {
static ngInjectorDef = defineInjector({
factory: () => new MyModule(),
providers: [{provide: MyService, useValue: new MyService('injector')}]
});
}
it('should support bootstrapping without injector', () => {
const fixture = new ComponentFixture(MyComponent);
expect(fixture.html).toEqual('no-injector');
});
it('should support bootstrapping with injector', () => {
const fixture = new ComponentFixture(MyComponent, {injector: createInjector(MyModule)});
expect(fixture.html).toEqual('injector');
});
});
});

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef, ElementRef, InjectFlags, TemplateRef, ViewContainerRef} from '@angular/core';
import {ChangeDetectorRef, ElementRef, InjectFlags, TemplateRef, ViewContainerRef, defineInjectable} from '@angular/core';
import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
import {defineComponent} from '../../src/render3/definition';
@ -397,6 +397,35 @@ describe('di', () => {
expect(log).toEqual(['DirB', 'DirB', 'DirA (dep: DirB - 2)']);
});
it('should create instance even when no injector present', () => {
class MyService {
value = 'MyService';
static ngInjectableDef =
defineInjectable({providedIn: 'root', factory: () => new MyService()});
}
class MyComponent {
constructor(public myService: MyService) {}
static ngComponentDef = defineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: () => new MyComponent(directiveInject(MyService)),
template: function(rf: RenderFlags, ctx: MyComponent) {
if (rf & RenderFlags.Create) {
text(0);
}
if (rf & RenderFlags.Update) {
textBinding(0, bind(ctx.myService.value));
}
}
});
}
const fixture = new ComponentFixture(MyComponent);
fixture.update();
expect(fixture.html).toEqual('MyService');
});
it('should throw if directive is not found', () => {
class Dir {
constructor(siblingDir: OtherDir) {}
@ -426,8 +455,7 @@ describe('di', () => {
}
}, [Dir, OtherDir]);
expect(() => new ComponentFixture(App))
.toThrowError(/ElementInjector: NotFound \[OtherDir\]/);
expect(() => new ComponentFixture(App)).toThrowError(/Injector: NOT_FOUND \[OtherDir\]/);
});
it('should throw if directives try to inject each other', () => {
@ -1010,24 +1038,6 @@ describe('di', () => {
});
});
describe('flags', () => {
it('should return defaultValue not found', () => {
class MyApp {
constructor(public value: string) {}
static ngComponentDef = defineComponent({
type: MyApp,
selectors: [['my-app']],
factory: () => new MyApp(
directiveInject(String as any, 'DefaultValue', InjectFlags.Default)),
template: () => null
});
}
const myApp = renderComponent(MyApp);
expect(myApp.value).toEqual('DefaultValue');
});
});
it('should inject from parent view', () => {
const ParentDirective = createDirective('parentDir');

View File

@ -8,6 +8,7 @@
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
import {Injector} from '../../src/di/injector';
import {CreateComponentOptions} from '../../src/render3/component';
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, PublicFeature, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
@ -93,7 +94,7 @@ export class ComponentFixture<T> extends BaseFixture {
component: T;
requestAnimationFrame: {(fn: () => void): void; flush(): void; queue: (() => void)[];};
constructor(private componentType: ComponentType<T>) {
constructor(private componentType: ComponentType<T>, opts: {injector?: Injector} = {}) {
super();
this.requestAnimationFrame = function(fn: () => void) {
requestAnimationFrame.queue.push(fn);
@ -105,10 +106,9 @@ export class ComponentFixture<T> extends BaseFixture {
}
};
this.component = _renderComponent(componentType, {
host: this.hostElement,
scheduler: this.requestAnimationFrame,
});
this.component = _renderComponent(
componentType,
{host: this.hostElement, scheduler: this.requestAnimationFrame, injector: opts.injector});
}
update(): void {

View File

@ -59,7 +59,7 @@ class HasOptionalDep {
constructor(public baz: Baz|null) {}
static ngInjectableDef: InjectableDef<HasOptionalDep> = defineInjectable({
factory: () => new HasOptionalDep(inject(Baz, null)),
factory: () => new HasOptionalDep(inject(Baz, InjectFlags.Optional)),
providedIn: MyModule,
});
}
@ -74,7 +74,7 @@ class ChildDep {
class FromChildWithOptionalDep {
constructor(public baz: Baz|null) {}
static ngInjectableDef: InjectableDef<FromChildWithOptionalDep> = defineInjectable({
factory: () => new FromChildWithOptionalDep(inject(Baz, null, InjectFlags.Default)),
factory: () => new FromChildWithOptionalDep(inject(Baz, InjectFlags.Default)),
providedIn: MyChildModule,
});
}
@ -83,7 +83,8 @@ class FromChildWithSkipSelfDep {
constructor(public depFromParent: ChildDep|null, public depFromChild: Bar|null) {}
static ngInjectableDef: InjectableDef<FromChildWithSkipSelfDep> = defineInjectable({
factory: () => new FromChildWithSkipSelfDep(
inject(ChildDep, null, InjectFlags.SkipSelf), inject(Bar, null, InjectFlags.Self)),
inject(ChildDep, InjectFlags.SkipSelf|InjectFlags.Optional),
inject(Bar, InjectFlags.Self|InjectFlags.Optional)),
providedIn: MyChildModule,
});
}

View File

@ -242,7 +242,7 @@ export declare class DefaultIterableDiffer<V> implements IterableDiffer<V>, Iter
/** @experimental */
export declare function defineInjectable<T>(opts: {
providedIn?: Type<any> | 'root' | null;
providedIn?: Type<any> | 'root' | 'any' | null;
factory: () => T;
}): InjectableDef<T>;
@ -336,9 +336,8 @@ export interface HostDecorator {
export declare const HostListener: HostListenerDecorator;
/** @experimental */
export declare function inject<T>(token: Type<T> | InjectionToken<T>, notFoundValue?: undefined, flags?: InjectFlags): T;
export declare function inject<T>(token: Type<T> | InjectionToken<T>, notFoundValue: T, flags?: InjectFlags): T;
export declare function inject<T>(token: Type<T> | InjectionToken<T>, notFoundValue: null, flags?: InjectFlags): T | null;
export declare function inject<T>(token: Type<T> | InjectionToken<T>): T;
export declare function inject<T>(token: Type<T> | InjectionToken<T>, flags?: InjectFlags): T | null;
export declare const Inject: InjectDecorator;
@ -359,6 +358,7 @@ export interface InjectableDecorator {
export interface InjectableDef<T> {
factory: () => T;
providedIn: InjectorType<any> | 'root' | 'any' | null;
value: T | undefined;
}
/** @experimental */
@ -462,6 +462,7 @@ export declare class IterableDiffers {
/** @deprecated */ factories: IterableDifferFactory[];
constructor(factories: IterableDifferFactory[]);
find(iterable: any): IterableDifferFactory;
static ngInjectableDef: InjectableDef<IterableDiffers>;
static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers;
static extend(factories: IterableDifferFactory[]): StaticProvider;
}