fix(ivy): walk the node injector tree and then the module injector tree (#25166)

- `directiveInjector()` is used to inject anything in the directive / component
/ pipe factories so adding `InjectionToken<T>` as a supported token type.
- `getOrCreateInjectable()` should search first in the node injector tree and
then in the module injector tree (was either or before the PR).

PR Close #25166
This commit is contained in:
Victor Berchet
2018-07-27 09:56:35 -07:00
committed by Miško Hevery
parent d3c0915598
commit 15df853622
5 changed files with 83 additions and 56 deletions

View File

@ -8,7 +8,7 @@
import {Type} from '../type';
import {InjectableDef, defineInjectable} from './defs';
import {defineInjectable} from './defs';
/**
* Creates a token that can be used in a DI Provider.

View File

@ -8,7 +8,9 @@
// 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 {InjectionToken} from '../di/injection_token';
import {InjectFlags, Injector, inject, setCurrentInjector} from '../di/injector';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
@ -31,7 +33,6 @@ import {Renderer3} from './interfaces/renderer';
import {DIRECTIVES, HOST_NODE, INJECTOR, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {addRemoveViewFromContainer, appendChild, detachView, getChildLNode, getParentLNode, insertView, removeView} from './node_manipulation';
import {stringify} from './util';
import {ViewRef} from './view_ref';
@ -61,7 +62,7 @@ let nextNgElementId = 0;
* @param type The directive to register
*/
export function bloomAdd(injector: LInjector, type: Type<any>): void {
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
let id = (type as any)[NG_ELEMENT_ID];
// Set a unique ID on the directive type, so if something tries to inject the directive,
// we can easily retrieve the ID and hash it into the bloom bit that should be checked.
@ -177,10 +178,10 @@ export function diPublic(def: DirectiveDefInternal<any>): void {
* @param flags Injection flags
* @returns the value from the injector or `null` when not found
*/
export function directiveInject<T>(token: Type<T>): T;
export function directiveInject<T>(token: Type<T>, flags: InjectFlags.Optional): T|null;
export function directiveInject<T>(token: Type<T>, flags: InjectFlags): T;
export function directiveInject<T>(token: Type<T>, flags = InjectFlags.Default): T|null {
export function directiveInject<T>(token: Type<T>| InjectionToken<T>): T;
export function directiveInject<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags): T;
export function directiveInject<T>(
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags);
}
@ -339,21 +340,14 @@ function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNo
* @returns the value from the injector or `null` when not found
*/
export function getOrCreateInjectable<T>(
di: LInjector, token: Type<T>, flags: InjectFlags = InjectFlags.Default): T|null {
nodeInjector: LInjector, token: Type<T>| InjectionToken<T>,
flags: InjectFlags = InjectFlags.Default): 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 = getPreviousOrParentNode().view[INJECTOR];
const formerInjector = setCurrentInjector(moduleInjector);
try {
return inject(token, flags);
} finally {
setCurrentInjector(formerInjector);
}
} else {
let injector: LInjector|null = di;
// (diPublic) otherwise fall back to the module injector.
if (bloomHash !== null) {
let injector: LInjector|null = nodeInjector;
while (injector) {
// Get the closest potential matching injector (upwards in the injector tree) that
@ -390,7 +384,8 @@ export function getOrCreateInjectable<T>(
// If we *didn't* find the directive for the token and we are searching the current node's
// injector, it's possible the directive is on this node and hasn't been created yet.
let instance: T|null;
if (injector === di && (instance = searchMatchesQueuedForCreation<T>(node, token))) {
if (injector === nodeInjector &&
(instance = searchMatchesQueuedForCreation<T>(node, token))) {
return instance;
}
@ -404,9 +399,13 @@ export function getOrCreateInjectable<T>(
}
}
// No directive was found for the given token.
if (flags & InjectFlags.Optional) return null;
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
const moduleInjector = getPreviousOrParentNode().view[INJECTOR];
const formerInjector = setCurrentInjector(moduleInjector);
try {
return inject(token, flags);
} finally {
setCurrentInjector(formerInjector);
}
}
function searchMatchesQueuedForCreation<T>(node: LNode, token: any): T|null {
@ -433,8 +432,8 @@ function searchMatchesQueuedForCreation<T>(node: LNode, token: any): T|null {
* @param token the injection token
* @returns the matching bit to check in the bloom filter or `null` if the token is not known.
*/
function bloomHashBit(type: Type<any>): number|null {
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
function bloomHashBit(token: Type<any>| InjectionToken<any>): number|null {
let id: number|undefined = (token as any)[NG_ELEMENT_ID];
return typeof id === 'number' ? id % BLOOM_SIZE : null;
}
@ -694,7 +693,7 @@ class ViewContainerRef implements viewEngine_ViewContainerRef {
detach(index?: number): viewEngine_ViewRef|null {
const adjustedIdx = this._adjustIndex(index, -1);
const lViewNode = detachView(this._lContainerNode, adjustedIdx);
detachView(this._lContainerNode, adjustedIdx);
return this._viewRefs.splice(adjustedIdx, 1)[0] || null;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Expression, WrappedNodeExpr, compilePipeFromMetadata, jitExpression} from '@angular/compiler';
import {WrappedNodeExpr, compilePipeFromMetadata, jitExpression} from '@angular/compiler';
import {Pipe} from '../../metadata/directives';
import {Type} from '../../type';