feat(ivy): support injecting the injector (#26699)

PR Close #26699
This commit is contained in:
Kara Erickson 2018-10-23 14:28:15 -07:00 committed by Matias Niemelä
parent d5cbcef0ea
commit 2c7386c961
31 changed files with 305 additions and 217 deletions

View File

@ -64,18 +64,6 @@ export function getConstructorDependencies(
ErrorCode.PARAM_MISSING_TOKEN, param.nameNode,
`No suitable token for parameter ${param.name || idx} of class ${clazz.name!.text}`);
}
if (ts.isIdentifier(tokenExpr)) {
const importedSymbol = reflector.getImportOfIdentifier(tokenExpr);
if (importedSymbol !== null && importedSymbol.from === '@angular/core') {
switch (importedSymbol.name) {
case 'Injector':
resolved = R3ResolvedDependencyType.Injector;
break;
default:
// Leave as a Token or Attribute.
}
}
}
const token = new WrappedNodeExpr(tokenExpr);
useType.push({token, optional, self, skipSelf, host, resolved});
});

View File

@ -380,7 +380,7 @@ describe('ngtsc behavioral tests', () => {
const jsContents = env.getContents('test.js');
expect(jsContents)
.toContain(
`factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵdirectiveInject(ChangeDetectorRef), i0.ɵdirectiveInject(ElementRef), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵdirectiveInject(Renderer2), i0.ɵdirectiveInject(TemplateRef), i0.ɵdirectiveInject(ViewContainerRef)); }`);
`factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵdirectiveInject(ChangeDetectorRef), i0.ɵdirectiveInject(ElementRef), i0.ɵdirectiveInject(Injector), i0.ɵdirectiveInject(Renderer2), i0.ɵdirectiveInject(TemplateRef), i0.ɵdirectiveInject(ViewContainerRef)); }`);
});
it('should generate queries for components', () => {

View File

@ -95,11 +95,6 @@ export enum R3ResolvedDependencyType {
* The token expression is a string representing the attribute name.
*/
Attribute = 1,
/**
* The dependency is for the `Injector` type itself.
*/
Injector = 2,
}
/**
@ -230,22 +225,14 @@ function compileInjectDependency(
dep: R3DependencyMetadata, injectFn: o.ExternalReference): o.Expression {
// Interpret the dependency according to its resolved type.
switch (dep.resolved) {
case R3ResolvedDependencyType.Token:
case R3ResolvedDependencyType.Injector: {
case R3ResolvedDependencyType.Token: {
// Build up the injection flags according to the metadata.
const flags = InjectFlags.Default | (dep.self ? InjectFlags.Self : 0) |
(dep.skipSelf ? InjectFlags.SkipSelf : 0) | (dep.host ? InjectFlags.Host : 0) |
(dep.optional ? InjectFlags.Optional : 0);
// Determine the token used for injection. In almost all cases this is the given token, but
// if the dependency is resolved to the `Injector` then the special `INJECTOR` token is used
// instead.
let token: o.Expression = dep.token;
if (dep.resolved === R3ResolvedDependencyType.Injector) {
token = o.importExpr(Identifiers.INJECTOR);
}
// Build up the arguments to the injectFn call.
const injectArgs = [token];
const injectArgs = [dep.token];
// If this dependency is optional or otherwise has non-default flags, then additional
// parameters describing how to inject the dependency must be passed to the inject function
// that's being used.
@ -280,12 +267,9 @@ export function dependenciesFromGlobalMetadata(
for (let dependency of type.diDeps) {
if (dependency.token) {
const tokenRef = tokenReference(dependency.token);
let resolved: R3ResolvedDependencyType = R3ResolvedDependencyType.Token;
if (tokenRef === injectorRef) {
resolved = R3ResolvedDependencyType.Injector;
} else if (dependency.isAttribute) {
resolved = R3ResolvedDependencyType.Attribute;
}
let resolved: R3ResolvedDependencyType = dependency.isAttribute ?
R3ResolvedDependencyType.Attribute :
R3ResolvedDependencyType.Token;
// In the case of most dependencies, the token will be a reference to a type. Sometimes,
// however, it can be a string, in the case of older Angular code or @Attribute injection.

View File

@ -14,7 +14,7 @@ export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/cha
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
export {Console as ɵConsole} from './console';
export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef, getInjectableDef as ɵgetInjectableDef} from './di/defs';
export {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector';
export {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector_compatibility';
export {APP_ROOT as ɵAPP_ROOT} from './di/scope';
export {ivyEnabled as ɵivyEnabled} from './ivy_switch';
export {ComponentFactory as ɵComponentFactory} from './linker/component_factory';

View File

@ -226,4 +226,8 @@ export {
export {
publishGlobalUtil as ɵpublishGlobalUtil
} from './render3/publish_global_util';
export {
SWITCH_INJECTOR_FACTORY__POST_R3__ as ɵSWITCH_INJECTOR_FACTORY__POST_R3__,
} from './di/injector';
// clang-format on

View File

@ -16,7 +16,8 @@ export * from './di/metadata';
export {InjectableType, InjectorType, defineInjectable, defineInjector} from './di/defs';
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
export {Injectable, InjectableDecorator, InjectableProvider} from './di/injectable';
export {inject, InjectFlags, INJECTOR, Injector} from './di/injector';
export {INJECTOR, Injector} from './di/injector';
export {inject, InjectFlags} from './di/injector_compatibility';
export {ReflectiveInjector} from './di/reflective_injector';
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
export {createInjector} from './di/r3_injector';

View File

@ -6,13 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/
import {injectInjector} from '../render3/di';
import {Type} from '../type';
import {stringify} from '../util';
import {noop} from '../util/noop';
import {getClosureSafeProperty} from '../util/property';
import {InjectableDef, defineInjectable, getInjectableDef} from './defs';
import {defineInjectable} from './defs';
import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {InjectFlags, inject} from './injector_compatibility';
import {Inject, Optional, Self, SkipSelf} from './metadata';
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './provider';
@ -104,8 +107,16 @@ export abstract class Injector {
providedIn: 'any' as any,
factory: () => inject(INJECTOR),
});
/** @internal */
static __NG_ELEMENT_ID__: () => Injector = () => SWITCH_INJECTOR_FACTORY();
}
export const SWITCH_INJECTOR_FACTORY__POST_R3__ = function() {
return injectInjector();
};
const SWITCH_INJECTOR_FACTORY__PRE_R3__ = noop;
const SWITCH_INJECTOR_FACTORY: typeof injectInjector = SWITCH_INJECTOR_FACTORY__PRE_R3__;
const IDENT = function<T>(value: T): T {
@ -336,7 +347,6 @@ function resolveToken(
return value;
}
function computeDeps(provider: StaticProvider): DependencyRecord[] {
let deps: DependencyRecord[] = EMPTY;
const providerDeps: any[] =
@ -396,150 +406,3 @@ function formatError(text: string, obj: any, source: string | null = null): stri
function staticError(text: string, obj: any): Error {
return new Error(formatError(text, obj));
}
/**
* Injection flags for DI.
*
* @publicApi
*/
export const enum InjectFlags {
Default = 0b0000,
/**
* Specifies that an injector should retrieve a dependency from any injector until reaching the
* host element of the current component. (Only used with Element Injector)
*/
Host = 0b0001,
/** Don't descend into ancestors of the node requesting injection. */
Self = 0b0010,
/** Skip the node that is requesting injection. */
SkipSelf = 0b0100,
/** Inject `defaultValue` instead if token not found. */
Optional = 0b1000,
}
/**
* 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 | undefined): Injector|undefined|null {
const former = _currentInjector;
_currentInjector = injector;
return former;
}
/**
* Current implementation of inject.
*
* By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
* to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
* way for two reasons:
* 1. `Injector` should not depend on ivy logic.
* 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
*/
let _injectImplementation: (<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags) => T | null)|
undefined;
/**
* Injects a token from the currently active injector.
*
* This function must be used in the context of a factory function such as one defined for an
* `InjectionToken`, and will throw an error if not called from such a context.
*
* @usageNotes
* ### Example
*
* {@example core/di/ts/injector_spec.ts region='ShakeableInjectionToken'}
*
* Within such a factory function `inject` is utilized to request injection of a dependency, instead
* of providing an additional array of dependencies as was common to do with `useFactory` providers.
* `inject` is faster and more type-safe.
*
* @publicApi
*/
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 {
return (_injectImplementation || injectInjectorOnly)(token, flags);
}
/**
* Sets the current inject implementation.
*/
export function setInjectImplementation(
impl: (<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null) | undefined):
(<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined {
const previous = _injectImplementation;
_injectImplementation = impl;
return previous;
}
export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>): T;
export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|
null;
export function injectInjectorOnly<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) {
return injectRootLimpMode(token, undefined, flags);
} else {
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
}
}
/**
* Injects `root` tokens in limp mode.
*
* If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
* `"root"`. This is known as the limp mode injection. In such case the value is stored in the
* `InjectableDef`.
*/
export function injectRootLimpMode<T>(
token: Type<T>| InjectionToken<T>, notFoundValue: T | undefined, flags: InjectFlags): T|null {
const injectableDef: InjectableDef<T>|null = getInjectableDef(token);
if (injectableDef && injectableDef.providedIn == 'root') {
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
injectableDef.value;
}
if (flags & InjectFlags.Optional) return null;
if (notFoundValue !== undefined) return notFoundValue;
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
}
export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): any[] {
const args: any[] = [];
for (let i = 0; i < types.length; i++) {
const arg = types[i];
if (Array.isArray(arg)) {
if (arg.length === 0) {
throw new Error('Arguments array must have arguments.');
}
let type: Type<any>|undefined = undefined;
let flags: InjectFlags = InjectFlags.Default;
for (let j = 0; j < arg.length; j++) {
const meta = arg[j];
if (meta instanceof Optional || meta.ngMetadataName === 'Optional') {
flags |= InjectFlags.Optional;
} else if (meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf') {
flags |= InjectFlags.SkipSelf;
} else if (meta instanceof Self || meta.ngMetadataName === 'Self') {
flags |= InjectFlags.Self;
} else if (meta instanceof Inject) {
type = meta.token;
} else {
type = meta;
}
}
args.push(inject(type !, flags));
} else {
args.push(inject(arg));
}
}
return args;
}

View File

@ -0,0 +1,165 @@
/**
* @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 {Type} from '../type';
import {stringify} from '../util';
import {InjectableDef, getInjectableDef} from './defs';
import {InjectionToken} from './injection_token';
import {Injector} from './injector';
import {Inject, Optional, Self, SkipSelf} from './metadata';
/**
* Injection flags for DI.
*
* @publicApi
*/
export const enum InjectFlags {
Default = 0b0000,
/**
* Specifies that an injector should retrieve a dependency from any injector until reaching the
* host element of the current component. (Only used with Element Injector)
*/
Host = 0b0001,
/** Don't descend into ancestors of the node requesting injection. */
Self = 0b0010,
/** Skip the node that is requesting injection. */
SkipSelf = 0b0100,
/** Inject `defaultValue` instead if token not found. */
Optional = 0b1000,
}
/**
* 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 | undefined): Injector|undefined|null {
const former = _currentInjector;
_currentInjector = injector;
return former;
}
/**
* Current implementation of inject.
*
* By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
* to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
* way for two reasons:
* 1. `Injector` should not depend on ivy logic.
* 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
*/
let _injectImplementation: (<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags) => T | null)|
undefined;
/**
* Sets the current inject implementation.
*/
export function setInjectImplementation(
impl: (<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null) | undefined):
(<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined {
const previous = _injectImplementation;
_injectImplementation = impl;
return previous;
}
export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>): T;
export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|
null;
export function injectInjectorOnly<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) {
return injectRootLimpMode(token, undefined, flags);
} else {
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
}
}
/**
* Injects a token from the currently active injector.
*
* This function must be used in the context of a factory function such as one defined for an
* `InjectionToken`, and will throw an error if not called from such a context.
*
* @usageNotes
* ### Example
*
* {@example core/di/ts/injector_spec.ts region='ShakeableInjectionToken'}
*
* Within such a factory function `inject` is utilized to request injection of a dependency, instead
* of providing an additional array of dependencies as was common to do with `useFactory` providers.
* `inject` is faster and more type-safe.
*
* @publicApi
*/
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 {
return (_injectImplementation || injectInjectorOnly)(token, flags);
}
/**
* Injects `root` tokens in limp mode.
*
* If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
* `"root"`. This is known as the limp mode injection. In such case the value is stored in the
* `InjectableDef`.
*/
export function injectRootLimpMode<T>(
token: Type<T>| InjectionToken<T>, notFoundValue: T | undefined, flags: InjectFlags): T|null {
const injectableDef: InjectableDef<T>|null = getInjectableDef(token);
if (injectableDef && injectableDef.providedIn == 'root') {
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
injectableDef.value;
}
if (flags & InjectFlags.Optional) return null;
if (notFoundValue !== undefined) return notFoundValue;
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
}
export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): any[] {
const args: any[] = [];
for (let i = 0; i < types.length; i++) {
const arg = types[i];
if (Array.isArray(arg)) {
if (arg.length === 0) {
throw new Error('Arguments array must have arguments.');
}
let type: Type<any>|undefined = undefined;
let flags: InjectFlags = InjectFlags.Default;
for (let j = 0; j < arg.length; j++) {
const meta = arg[j];
if (meta instanceof Optional || meta.ngMetadataName === 'Optional') {
flags |= InjectFlags.Optional;
} else if (meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf') {
flags |= InjectFlags.SkipSelf;
} else if (meta instanceof Self || meta.ngMetadataName === 'Self') {
flags |= InjectFlags.Self;
} else if (meta instanceof Inject) {
type = meta.token;
} else {
type = meta;
}
}
args.push(inject(type !, flags));
} else {
args.push(inject(arg));
}
}
return args;
}

View File

@ -13,7 +13,8 @@ import {stringify} from '../util';
import {InjectableDef, InjectableType, InjectorType, InjectorTypeWithProviders, getInjectableDef, getInjectorDef} from './defs';
import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {INJECTOR, InjectFlags, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, inject, injectArgs, setCurrentInjector} from './injector';
import {INJECTOR, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE} from './injector';
import {InjectFlags, inject, injectArgs, setCurrentInjector} from './injector_compatibility';
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './provider';
import {APP_ROOT} from './scope';

View File

@ -10,7 +10,7 @@ import {ReflectionCapabilities} from '../reflection/reflection_capabilities';
import {Type} from '../type';
import {getClosureSafeProperty} from '../util/property';
import {inject, injectArgs} from './injector';
import {inject, injectArgs} from './injector_compatibility';
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';
const USE_VALUE =

View File

@ -20,7 +20,7 @@
*/
export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef, defineInjectable, defineInjector} from './di/defs';
export {inject} from './di/injector';
export {inject} from './di/injector_compatibility';
export {NgModuleDef as ɵNgModuleDef, NgModuleDefWithMeta as ɵNgModuleDefWithMeta} from './metadata/ng_module';
export {defineNgModule as ɵdefineNgModule} from './render3/definition';
export {NgModuleFactory as ɵNgModuleFactory} from './render3/ng_module_ref';

View File

@ -8,7 +8,8 @@
import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {InjectionToken} from '../di/injection_token';
import {Injector, inject} from '../di/injector';
import {Injector} from '../di/injector';
import {inject} from '../di/injector_compatibility';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';

View File

@ -8,7 +8,8 @@
import {getInjectableDef, getInjectorDef} from '../di/defs';
import {InjectionToken} from '../di/injection_token';
import {InjectFlags, Injector, NullInjector, injectRootLimpMode, setInjectImplementation} from '../di/injector';
import {Injector} from '../di/injector';
import {InjectFlags, injectRootLimpMode, setInjectImplementation} from '../di/injector_compatibility';
import {Type} from '../type';
import {assertDefined, assertEqual} from './assert';
@ -506,6 +507,26 @@ function shouldSearchParent(flags: InjectFlags, parentLocation: RelativeInjector
(flags & InjectFlags.Host && getParentInjectorViewOffset(parentLocation) > 0));
}
export function injectInjector() {
const tNode = getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode;
return new NodeInjector(tNode, getViewData());
}
export class NodeInjector implements Injector {
private _injectorIndex: number;
constructor(
private _tNode: TElementNode|TContainerNode|TElementContainerNode,
private _hostView: LViewData) {
this._injectorIndex = getOrCreateNodeInjectorForNode(_tNode, _hostView);
}
get(token: any): any {
setTNodeAndViewData(this._tNode, this._hostView);
return getOrCreateInjectable(this._tNode, this._hostView, token);
}
}
export function getFactoryOf<T>(type: Type<any>): ((type: Type<T>| null) => T)|null {
const typeAny = type as any;
const def = getComponentDef<T>(typeAny) || getDirectiveDef<T>(typeAny) ||

View File

@ -38,14 +38,14 @@ export function getComponent<T = {}>(target: {}): T|null {
const context = loadContext(target) !;
if (context.component === undefined) {
let lViewData = context.lViewData;
let lViewData: LViewData|null = context.lViewData;
while (lViewData) {
const ctx = lViewData ![CONTEXT] !as{};
if (ctx && isComponentInstance(ctx)) {
context.component = ctx;
break;
}
lViewData = lViewData ![PARENT] !;
lViewData = lViewData[FLAGS] & LViewFlags.IsRoot ? null : lViewData ![PARENT] !;
}
if (context.component === undefined) {
context.component = null;

View File

@ -9,7 +9,7 @@
import './ng_dev_mode';
import {InjectionToken} from '../di/injection_token';
import {InjectFlags} from '../di/injector';
import {InjectFlags} from '../di/injector_compatibility';
import {QueryList} from '../linker';
import {Sanitizer} from '../sanitization/security';
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';

View File

@ -7,7 +7,7 @@
*/
import {InjectionToken} from '../../di/injection_token';
import {InjectFlags} from '../../di/injector';
import {InjectFlags} from '../../di/injector_compatibility';
import {Type} from '../../type';
import {TElementNode} from './node';
import {LViewData, TData} from './view';

View File

@ -7,7 +7,7 @@
*/
import {defineInjectable, defineInjector,} from '../../di/defs';
import {inject} from '../../di/injector';
import {inject} from '../../di/injector_compatibility';
import * as r3 from '../index';
import * as sanitization from '../../sanitization/sanitization';

View File

@ -42,11 +42,7 @@ function reflectDependency(dep: any | any[]): R3DependencyMetadata {
};
function setTokenAndResolvedType(token: any): void {
if (token === Injector) {
meta.resolved = R3ResolvedDependencyType.Injector;
} else {
meta.resolved = R3ResolvedDependencyType.Token;
}
meta.resolved = R3ResolvedDependencyType.Token;
meta.token = new WrappedNodeExpr(token);
}

View File

@ -8,7 +8,8 @@
import {InjectableDef, getInjectableDef} from '../di/defs';
import {resolveForwardRef} from '../di/forward_ref';
import {INJECTOR, InjectFlags, Injector, setCurrentInjector} from '../di/injector';
import {INJECTOR, Injector} from '../di/injector';
import {setCurrentInjector} from '../di/injector_compatibility';
import {APP_ROOT} from '../di/scope';
import {NgModuleRef} from '../linker/ng_module_factory';
import {stringify} from '../util';

View File

@ -8,7 +8,8 @@
import {ApplicationRef} from '../application_ref';
import {ChangeDetectorRef} from '../change_detection/change_detection';
import {InjectFlags, Injector} from '../di/injector';
import {Injector} from '../di/injector';
import {InjectFlags} from '../di/injector_compatibility';
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from '../linker/component_factory_resolver';
import {ElementRef} from '../linker/element_ref';

View File

@ -48,7 +48,7 @@
"name": "DefaultIterableDifferFactory"
},
{
"name": "EMPTY$1"
"name": "EMPTY"
},
{
"name": "EMPTY_ARR"
@ -81,7 +81,7 @@
"name": "HOST_NODE"
},
{
"name": "INJECTOR$1"
"name": "INJECTOR"
},
{
"name": "INJECTOR_SIZE"
@ -138,7 +138,7 @@
"name": "NgModuleRef"
},
{
"name": "NodeInjector"
"name": "NodeInjector$1"
},
{
"name": "NodeInjectorFactory"

View File

@ -24,7 +24,7 @@
"name": "DECLARATION_VIEW"
},
{
"name": "EMPTY$1"
"name": "EMPTY"
},
{
"name": "EMPTY_ARRAY$1"
@ -48,7 +48,7 @@
"name": "HOST_NODE"
},
{
"name": "INJECTOR$1"
"name": "INJECTOR"
},
{
"name": "INJECTOR_SIZE"

View File

@ -570,7 +570,7 @@
"name": "DomElementSchemaRegistry"
},
{
"name": "EMPTY"
"name": "EMPTY$1"
},
{
"name": "EMPTY_ARRAY$4"
@ -780,7 +780,7 @@
"name": "INHERITED_CLASS_WITH_CTOR"
},
{
"name": "INJECTOR"
"name": "INJECTOR$1"
},
{
"name": "INJECTORRefTokenKey"
@ -1343,6 +1343,9 @@
{
"name": "SWITCH_ELEMENT_REF_FACTORY"
},
{
"name": "SWITCH_INJECTOR_FACTORY"
},
{
"name": "SWITCH_RENDERER2_FACTORY"
},

View File

@ -12,7 +12,7 @@
"name": "EmptyErrorImpl"
},
{
"name": "INJECTOR"
"name": "INJECTOR$1"
},
{
"name": "Inject"

View File

@ -42,7 +42,7 @@
"name": "DefaultIterableDifferFactory"
},
{
"name": "EMPTY$1"
"name": "EMPTY"
},
{
"name": "EMPTY_ARRAY$1"
@ -69,7 +69,7 @@
"name": "HOST_NODE"
},
{
"name": "INJECTOR$1"
"name": "INJECTOR"
},
{
"name": "INJECTOR_SIZE"
@ -132,7 +132,7 @@
"name": "NgModuleRef"
},
{
"name": "NodeInjector"
"name": "NodeInjector$1"
},
{
"name": "NodeInjectorFactory"

View File

@ -593,6 +593,9 @@
{
"name": "NodeInjector"
},
{
"name": "NodeInjector$1"
},
{
"name": "NodeInjectorFactory"
},
@ -752,6 +755,9 @@
{
"name": "SWITCH_ELEMENT_REF_FACTORY"
},
{
"name": "SWITCH_INJECTOR_FACTORY"
},
{
"name": "SWITCH_RENDERER2_FACTORY"
},
@ -1982,6 +1988,9 @@
{
"name": "injectElementRef"
},
{
"name": "injectInjector"
},
{
"name": "injectInjectorOnly"
},

View File

@ -8,7 +8,8 @@
import {defineInjectable, defineInjector} from '../../src/di/defs';
import {InjectionToken} from '../../src/di/injection_token';
import {INJECTOR, Injector, inject} from '../../src/di/injector';
import {INJECTOR, Injector} from '../../src/di/injector';
import {inject} from '../../src/di/injector_compatibility';
import {R3Injector, createInjector} from '../../src/di/r3_injector';
describe('InjectorDef-based createInjector()', () => {

View File

@ -1049,6 +1049,53 @@ describe('di', () => {
describe('Special tokens', () => {
describe('Injector', () => {
it('should inject the injector', () => {
let injectorDir !: InjectorDir;
let otherInjectorDir !: OtherInjectorDir;
let divElement !: HTMLElement;
class InjectorDir {
constructor(public injector: Injector) {}
static ngDirectiveDef = defineDirective({
type: InjectorDir,
selectors: [['', 'injectorDir', '']],
factory: () => injectorDir = new InjectorDir(directiveInject(Injector as any))
});
}
class OtherInjectorDir {
constructor(public otherDir: InjectorDir, public injector: Injector) {}
static ngDirectiveDef = defineDirective({
type: OtherInjectorDir,
selectors: [['', 'otherInjectorDir', '']],
factory: () => otherInjectorDir = new OtherInjectorDir(
directiveInject(InjectorDir), directiveInject(Injector as any))
});
}
/** <div injectorDir otherInjectorDir></div> */
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
element(0, 'div', ['injectorDir', '', 'otherInjectorDir', '']);
}
// testing only
divElement = load(0);
}, 1, 0, [InjectorDir, OtherInjectorDir]);
const fixture = new ComponentFixture(App);
expect(injectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
expect(otherInjectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
expect(otherInjectorDir.injector.get(InjectorDir)).toBe(injectorDir);
expect(injectorDir.injector).not.toBe(otherInjectorDir.injector);
});
});
describe('ElementRef', () => {
it('should create directive with ElementRef dependencies', () => {

View File

@ -10,7 +10,7 @@ import 'reflect-metadata';
import {InjectorDef, defineInjectable} from '@angular/core/src/di/defs';
import {Injectable} from '@angular/core/src/di/injectable';
import {inject, setCurrentInjector} from '@angular/core/src/di/injector';
import {inject, setCurrentInjector} from '@angular/core/src/di/injector_compatibility';
import {ivyEnabled} from '@angular/core/src/ivy_switch';
import {Component, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata/directives';
import {NgModule, NgModuleDef} from '@angular/core/src/metadata/ng_module';

View File

@ -14,7 +14,7 @@ import {Renderer2} from '@angular/core/src/render/api';
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
import {SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as R3_CHANGE_DETECTOR_REF_FACTORY} from '../../src/change_detection/change_detector_ref';
import {Injector} from '../../src/di/injector';
import {Injector, SWITCH_INJECTOR_FACTORY__POST_R3__ as R3_INJECTOR_FACTORY} from '../../src/di/injector';
import {SWITCH_ELEMENT_REF_FACTORY__POST_R3__ as R3_ELEMENT_REF_FACTORY} from '../../src/linker/element_ref';
import {SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as R3_TEMPLATE_REF_FACTORY} from '../../src/linker/template_ref';
import {SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ as R3_VIEW_CONTAINER_REF_FACTORY} from '../../src/linker/view_container_ref';
@ -322,4 +322,5 @@ export function enableIvyInjectableFactories() {
R3_VIEW_CONTAINER_REF_FACTORY(ViewContainerRef, ElementRef);
(ChangeDetectorRef as any)[NG_ELEMENT_ID] = () => R3_CHANGE_DETECTOR_REF_FACTORY();
(Renderer2 as any)[NG_ELEMENT_ID] = () => R3_RENDERER2_FACTORY();
(Injector as any)[NG_ELEMENT_ID] = () => R3_INJECTOR_FACTORY();
}

View File

@ -8,7 +8,8 @@
import {NgModuleRef} from '@angular/core';
import {InjectableDef, defineInjectable} from '@angular/core/src/di/defs';
import {INJECTOR, InjectFlags, Injector, inject} from '@angular/core/src/di/injector';
import {INJECTOR, Injector} from '@angular/core/src/di/injector';
import {InjectFlags, inject} from '@angular/core/src/di/injector_compatibility';
import {makePropDecorator} from '@angular/core/src/util/decorators';
import {NgModuleDefinition, NgModuleProviderDef, NodeFlags} from '@angular/core/src/view';
import {moduleDef, moduleProvideDef, resolveNgModuleDep} from '@angular/core/src/view/ng_module';