refactor(ivy): special injection tokens should not be cached (#26048)

PR Close #26048
This commit is contained in:
Kara Erickson 2018-09-20 11:48:06 -07:00
parent 64aa6701f6
commit d5f47d6b71
11 changed files with 268 additions and 306 deletions

View File

@ -14,7 +14,7 @@ import {Sanitizer} from '../sanitization/security';
import {assertComponentType, assertDefined} from './assert'; import {assertComponentType, assertDefined} from './assert';
import {queueInitHooks, queueLifecycleHooks} from './hooks'; import {queueInitHooks, queueLifecycleHooks} from './hooks';
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions'; import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions';
import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition'; import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition';
import {LElementNode} from './interfaces/node'; import {LElementNode} from './interfaces/node';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
@ -150,7 +150,6 @@ export function createRootComponent<T>(
if (componentDef.hostBindings) queueHostBindingForCheck(0, componentDef.hostVars); if (componentDef.hostBindings) queueHostBindingForCheck(0, componentDef.hostVars);
rootContext.components.push(component); rootContext.components.push(component);
(elementNode.data as LViewData)[CONTEXT] = component; (elementNode.data as LViewData)[CONTEXT] = component;
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data as LViewData);
hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef)); hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef));
setHostBindings(rootView[TVIEW].hostBindings); setHostBindings(rootView[TVIEW].hostBindings);

View File

@ -145,10 +145,6 @@ export function getOrCreateNodeInjectorForNode(
cbf5: parentInjector == null ? 0 : parentInjector.cbf5 | parentInjector.bf5, cbf5: parentInjector == null ? 0 : parentInjector.cbf5 | parentInjector.bf5,
cbf6: parentInjector == null ? 0 : parentInjector.cbf6 | parentInjector.bf6, cbf6: parentInjector == null ? 0 : parentInjector.cbf6 | parentInjector.bf6,
cbf7: parentInjector == null ? 0 : parentInjector.cbf7 | parentInjector.bf7, cbf7: parentInjector == null ? 0 : parentInjector.cbf7 | parentInjector.bf7,
templateRef: null,
viewContainerRef: null,
elementRef: null,
changeDetectorRef: null,
}; };
} }
@ -207,7 +203,7 @@ export function directiveInject<T>(
* @returns The ElementRef instance to use * @returns The ElementRef instance to use
*/ */
export function injectElementRef(): viewEngine_ElementRef { export function injectElementRef(): viewEngine_ElementRef {
return getOrCreateElementRef(getOrCreateNodeInjector()); return createElementRef(getPreviousOrParentTNode(), _getViewData());
} }
/** /**
@ -217,7 +213,7 @@ export function injectElementRef(): viewEngine_ElementRef {
* @returns The TemplateRef instance to use * @returns The TemplateRef instance to use
*/ */
export function injectTemplateRef<T>(): viewEngine_TemplateRef<T> { export function injectTemplateRef<T>(): viewEngine_TemplateRef<T> {
return getOrCreateTemplateRef<T>(getOrCreateNodeInjector()); return createTemplateRef<T>(getPreviousOrParentTNode(), _getViewData());
} }
/** /**
@ -227,12 +223,14 @@ export function injectTemplateRef<T>(): viewEngine_TemplateRef<T> {
* @returns The ViewContainerRef instance to use * @returns The ViewContainerRef instance to use
*/ */
export function injectViewContainerRef(): viewEngine_ViewContainerRef { export function injectViewContainerRef(): viewEngine_ViewContainerRef {
return getOrCreateContainerRef(getOrCreateNodeInjector()); const previousTNode =
getPreviousOrParentTNode() as TElementNode | TElementContainerNode | TContainerNode;
return createContainerRef(previousTNode, _getViewData());
} }
/** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */ /** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */
export function injectChangeDetectorRef(): viewEngine_ChangeDetectorRef { export function injectChangeDetectorRef(): viewEngine_ChangeDetectorRef {
return getOrCreateChangeDetectorRef(getOrCreateNodeInjector(), null); return createViewRef(getPreviousOrParentTNode(), _getViewData(), null);
} }
/** /**
@ -302,34 +300,25 @@ export function injectAttribute(attrNameToInject: string): string|undefined {
/** /**
* Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias). * Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias).
* Or, if it already exists, retrieves the existing instance.
* *
* @param hostTNode The node that is requesting a ChangeDetectorRef
* @param hostView The view to which the node belongs
* @param context The context for this change detector ref
* @returns The ChangeDetectorRef to use * @returns The ChangeDetectorRef to use
*/ */
export function getOrCreateChangeDetectorRef( export function createViewRef(
di: LInjector, context: any): viewEngine_ChangeDetectorRef { hostTNode: TNode, hostView: LViewData, context: any): viewEngine_ChangeDetectorRef {
if (di.changeDetectorRef) return di.changeDetectorRef; if (isComponent(hostTNode)) {
const componentIndex = hostTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
const currentTNode = di.tNode; const componentView = getLNode(hostTNode, hostView).data as LViewData;
if (isComponent(currentTNode)) { return new ViewRef(componentView, context, componentIndex);
return di.changeDetectorRef = } else if (hostTNode.type === TNodeType.Element) {
new ViewRef(getLNode(currentTNode, di.view).data as LViewData, context); const hostComponentView = findComponentView(hostView);
} else if (currentTNode.type === TNodeType.Element) { return new ViewRef(hostComponentView, hostComponentView[CONTEXT], -1);
return di.changeDetectorRef = getOrCreateHostChangeDetector(di.view);
} }
return null !; return null !;
} }
/** Gets or creates ChangeDetectorRef for the closest host component */
function getOrCreateHostChangeDetector(currentView: LViewData): viewEngine_ChangeDetectorRef {
const hostComponentView = findComponentView(currentView);
const hostNode = getHostElementNode(hostComponentView) !;
const hostInjector = hostNode.nodeInjector;
const existingRef = hostInjector && hostInjector.changeDetectorRef;
return existingRef ? existingRef : new ViewRef(hostComponentView, hostComponentView[CONTEXT]);
}
function getOrCreateRenderer2(di: LInjector): Renderer2 { function getOrCreateRenderer2(di: LInjector): Renderer2 {
const renderer = di.view[RENDERER]; const renderer = di.view[RENDERER];
if (isProceduralRenderer(renderer)) { if (isProceduralRenderer(renderer)) {
@ -537,44 +526,44 @@ function sameHostView(injector: LInjector): boolean {
} }
export class ReadFromInjectorFn<T> { export class ReadFromInjectorFn<T> {
constructor(readonly read: (injector: LInjector, tNode: TNode, directiveIndex?: number) => T) {} constructor(readonly read: (tNode: TNode, view: LViewData, directiveIndex?: number) => T) {}
} }
/** /**
* Creates an ElementRef for a given node injector and stores it on the injector. * Creates an ElementRef for a given node injector and stores it on the injector.
* Or, if the ElementRef already exists, retrieves the existing ElementRef.
* *
* @param di The node injector where we should store a created ElementRef * @param di The node injector where we should store a created ElementRef
* @returns The ElementRef instance to use * @returns The ElementRef instance to use
*/ */
export function getOrCreateElementRef(di: LInjector): viewEngine_ElementRef { export function createElementRef(tNode: TNode, view: LViewData): viewEngine_ElementRef {
return di.elementRef || (di.elementRef = new ElementRef(getLNode(di.tNode, di.view).native)); return new ElementRef(getLNode(tNode, view).native);
} }
export const QUERY_READ_TEMPLATE_REF = <QueryReadType<viewEngine_TemplateRef<any>>>( export const QUERY_READ_TEMPLATE_REF = <QueryReadType<viewEngine_TemplateRef<any>>>(
new ReadFromInjectorFn<viewEngine_TemplateRef<any>>( new ReadFromInjectorFn<viewEngine_TemplateRef<any>>(
(injector: LInjector) => getOrCreateTemplateRef(injector)) as any); (tNode: TNode, view: LViewData) => { return createTemplateRef(tNode, view);}) as any);
export const QUERY_READ_CONTAINER_REF = <QueryReadType<viewEngine_ViewContainerRef>>( export const QUERY_READ_CONTAINER_REF = <QueryReadType<viewEngine_ViewContainerRef>>(
new ReadFromInjectorFn<viewEngine_ViewContainerRef>( new ReadFromInjectorFn<viewEngine_ViewContainerRef>(
(injector: LInjector) => getOrCreateContainerRef(injector)) as any); (tNode: TNode, view: LViewData) => createContainerRef(
tNode as TElementNode | TContainerNode | TElementContainerNode, view)) as any);
export const QUERY_READ_ELEMENT_REF = export const QUERY_READ_ELEMENT_REF =
<QueryReadType<viewEngine_ElementRef>>(new ReadFromInjectorFn<viewEngine_ElementRef>( <QueryReadType<viewEngine_ElementRef>>(new ReadFromInjectorFn<viewEngine_ElementRef>(
(injector: LInjector) => getOrCreateElementRef(injector)) as any); (tNode: TNode, view: LViewData) => createElementRef(tNode, view)) as any);
export const QUERY_READ_FROM_NODE = export const QUERY_READ_FROM_NODE =
(new ReadFromInjectorFn<any>((injector: LInjector, tNode: TNode, directiveIdx: number) => { (new ReadFromInjectorFn<any>((tNode: TNode, view: LViewData, directiveIdx: number) => {
ngDevMode && assertNodeOfPossibleTypes( ngDevMode && assertNodeOfPossibleTypes(
tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
if (directiveIdx > -1) { if (directiveIdx > -1) {
return injector.view[DIRECTIVES] ![directiveIdx]; return view[DIRECTIVES] ![directiveIdx];
} }
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) { if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
return getOrCreateElementRef(injector); return createElementRef(tNode, view);
} }
if (tNode.type === TNodeType.Container) { if (tNode.type === TNodeType.Container) {
return getOrCreateTemplateRef(injector); return createTemplateRef(tNode, view);
} }
if (ngDevMode) { if (ngDevMode) {
// should never happen // should never happen
@ -586,20 +575,19 @@ export const QUERY_READ_FROM_NODE =
class ElementRef extends viewEngine_ElementRef {} class ElementRef extends viewEngine_ElementRef {}
/** /**
* Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef * Creates a ViewContainerRef and stores it on the injector.
* already exists, retrieves the existing ViewContainerRef.
* *
* @param hostTNode The node that is requesting a ViewContainerRef
* @param hostView The view to which the node belongs
* @returns The ViewContainerRef instance to use * @returns The ViewContainerRef instance to use
*/ */
export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainerRef { export function createContainerRef(
if (!di.viewContainerRef) { hostTNode: TElementNode | TContainerNode | TElementContainerNode,
const hostLNode = hostView: LViewData): viewEngine_ViewContainerRef {
getPreviousOrParentNode() as LElementNode | LContainerNode | LElementContainerNode; const hostLNode = getLNode(hostTNode, hostView);
const hostTNode = getPreviousOrParentTNode() as TElementNode | TContainerNode;
ngDevMode && assertNodeOfPossibleTypes( ngDevMode && assertNodeOfPossibleTypes(
hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
const hostView = di.view;
const lContainer = createLContainer(hostView, true); const lContainer = createLContainer(hostView, true);
const comment = hostView[RENDERER].createComment(ngDevMode ? 'container' : ''); const comment = hostView[RENDERER].createComment(ngDevMode ? 'container' : '');
const lContainerNode: LContainerNode = const lContainerNode: LContainerNode =
@ -617,11 +605,8 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainer
hostLNode.dynamicLContainerNode = lContainerNode; hostLNode.dynamicLContainerNode = lContainerNode;
addToViewTree(hostView, hostTNode.index as number, lContainer); addToViewTree(hostView, hostTNode.index as number, lContainer);
di.viewContainerRef = new ViewContainerRef( return new ViewContainerRef(
lContainer, hostTNode.dynamicContainerNode as TContainerNode, hostTNode, hostView); lContainer, hostTNode.dynamicContainerNode as TContainerNode, hostTNode, hostView);
}
return di.viewContainerRef;
} }
export class NodeInjector implements Injector { export class NodeInjector implements Injector {
@ -629,16 +614,16 @@ export class NodeInjector implements Injector {
get(token: any): any { get(token: any): any {
if (token === viewEngine_TemplateRef) { if (token === viewEngine_TemplateRef) {
return getOrCreateTemplateRef(this._lInjector); return createTemplateRef(this._lInjector.tNode, this._lInjector.view);
} }
if (token === viewEngine_ViewContainerRef) { if (token === viewEngine_ViewContainerRef) {
return getOrCreateContainerRef(this._lInjector); return createContainerRef(this._lInjector.tNode, this._lInjector.view);
} }
if (token === viewEngine_ElementRef) { if (token === viewEngine_ElementRef) {
return getOrCreateElementRef(this._lInjector); return createElementRef(this._lInjector.tNode, this._lInjector.view);
} }
if (token === viewEngine_ChangeDetectorRef) { if (token === viewEngine_ChangeDetectorRef) {
return getOrCreateChangeDetectorRef(this._lInjector, null); return createViewRef(this._lInjector.tNode, this._lInjector.view, null);
} }
if (token === Renderer2) { if (token === Renderer2) {
return getOrCreateRenderer2(this._lInjector); return getOrCreateRenderer2(this._lInjector);
@ -666,7 +651,7 @@ class ViewContainerRef extends viewEngine_ViewContainerRef {
// TODO: Remove LNode lookup when removing LNode.nodeInjector // TODO: Remove LNode lookup when removing LNode.nodeInjector
const injector = const injector =
getOrCreateNodeInjectorForNode(this._getHostNode(), this._hostTNode, this._hostView); getOrCreateNodeInjectorForNode(this._getHostNode(), this._hostTNode, this._hostView);
return getOrCreateElementRef(injector); return createElementRef(injector.tNode, injector.view);
} }
get injector(): Injector { get injector(): Injector {
@ -776,23 +761,20 @@ class ViewContainerRef extends viewEngine_ViewContainerRef {
} }
/** /**
* Creates a TemplateRef and stores it on the injector. Or, if the TemplateRef already * Creates a TemplateRef and stores it on the injector.
* exists, retrieves the existing TemplateRef.
* *
* @param di The node injector where we should store a created TemplateRef * @param hostTNode The node that is requesting a TemplateRef
* @param hostView The view to which the node belongs
* @returns The TemplateRef instance to use * @returns The TemplateRef instance to use
*/ */
export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef<T> { export function createTemplateRef<T>(
if (!di.templateRef) { hostTNode: TNode, hostView: LViewData): viewEngine_TemplateRef<T> {
const hostNode = getPreviousOrParentNode() as LContainerNode; const hostNode = getLNode(hostTNode, hostView);
const hostTNode = getPreviousOrParentTNode() as TContainerNode;
ngDevMode && assertNodeType(hostTNode, TNodeType.Container); ngDevMode && assertNodeType(hostTNode, TNodeType.Container);
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated'); ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
di.templateRef = new TemplateRef<any>( return new TemplateRef<any>(
di.view, getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(), hostView, createElementRef(hostTNode, hostView), hostTNode.tViews as TView, getRenderer(),
hostNode.data ![QUERIES]); hostNode.data ![QUERIES]);
}
return di.templateRef;
} }
export function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null { export function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null {
@ -835,7 +817,7 @@ class TemplateRef<T> extends viewEngine_TemplateRef<T> {
insertView(lView, container, hostView !, index !, tContainerNode !.parent !.index); insertView(lView, container, hostView !, index !, tContainerNode !.parent !.index);
} }
renderEmbeddedTemplate(lView, this._tView, context, RenderFlags.Create); renderEmbeddedTemplate(lView, this._tView, context, RenderFlags.Create);
const viewRef = new ViewRef(lView, context); const viewRef = new ViewRef(lView, context, -1);
viewRef._tViewNode = lView[HOST_NODE] as TViewNode; viewRef._tViewNode = lView[HOST_NODE] as TViewNode;
return viewRef; return viewRef;
} }
@ -846,7 +828,5 @@ class TemplateRef<T> extends viewEngine_TemplateRef<T> {
* `<ng-template>` element. * `<ng-template>` element.
*/ */
export function templateRefExtractor(tNode: TContainerNode, currentView: LViewData) { export function templateRefExtractor(tNode: TContainerNode, currentView: LViewData) {
// TODO: remove this lookup with removing LNode.nodeInjector return createTemplateRef(tNode, currentView);
const lNode = getLNode(tNode, currentView) as LContainerNode;
return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode, tNode, currentView));
} }

View File

@ -899,12 +899,18 @@ function findDirectiveMatches(tNode: TNode): CurrentMatchesList|null {
for (let i = 0; i < registry.length; i++) { for (let i = 0; i < registry.length; i++) {
const def = registry[i]; const def = registry[i];
if (isNodeMatchingSelectorList(tNode, def.selectors !)) { if (isNodeMatchingSelectorList(tNode, def.selectors !)) {
matches || (matches = []);
if ((def as ComponentDefInternal<any>).template) { if ((def as ComponentDefInternal<any>).template) {
if (tNode.flags & TNodeFlags.isComponent) throwMultipleComponentError(tNode); if (tNode.flags & TNodeFlags.isComponent) throwMultipleComponentError(tNode);
addComponentLogic(def as ComponentDefInternal<any>);
tNode.flags = TNodeFlags.isComponent; tNode.flags = TNodeFlags.isComponent;
// The component is always stored first with directives after.
matches.unshift(def, null);
} else {
matches.push(def, null);
} }
if (def.diPublic) def.diPublic(def); if (def.diPublic) def.diPublic(def);
(matches || (matches = [])).push(def, null);
} }
} }
} }
@ -948,14 +954,6 @@ export function queueHostBindingForCheck(dirIndex: number, hostVars: number): vo
])).push(dirIndex, previousOrParentTNode.index - HEADER_OFFSET); ])).push(dirIndex, previousOrParentTNode.index - HEADER_OFFSET);
} }
/** Sets the context for a ChangeDetectorRef to the given instance. */
export function initChangeDetectorIfExisting(
injector: LInjector | null, instance: any, view: LViewData): void {
if (injector && injector.changeDetectorRef != null) {
(injector.changeDetectorRef as ViewRef<any>)._setComponentContext(view, instance);
}
}
/** /**
* This function instantiates the given directives. * This function instantiates the given directives.
*/ */
@ -976,6 +974,12 @@ function instantiateDirectivesDirectly() {
for (let i = start; i < end; i++) { for (let i = start; i < end; i++) {
const def: DirectiveDefInternal<any> = tDirectives[i]; const def: DirectiveDefInternal<any> = tDirectives[i];
// Component view must be set on node before the factory is created so
// ChangeDetectorRefs have a way to store component view on creation.
if ((def as ComponentDefInternal<any>).template) {
addComponentLogic(def as ComponentDefInternal<any>);
}
directiveCreate(i, def.factory(), def); directiveCreate(i, def.factory(), def);
} }
} }
@ -1698,13 +1702,11 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
export function directiveCreate<T>( export function directiveCreate<T>(
directiveDefIdx: number, directive: T, directiveDefIdx: number, directive: T,
directiveDef: DirectiveDefInternal<T>| ComponentDefInternal<T>): T { directiveDef: DirectiveDefInternal<T>| ComponentDefInternal<T>): T {
const hostNode = getPreviousOrParentNode() !; const hostNode = getLNode(previousOrParentTNode, viewData);
const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, hostNode); const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, hostNode);
const isComponent = (directiveDef as ComponentDefInternal<T>).template; if ((directiveDef as ComponentDefInternal<T>).template) {
if (isComponent) { hostNode.data ![CONTEXT] = directive;
addComponentLogic(
directiveDefIdx, directive, directiveDef as ComponentDefInternal<T>, hostNode);
} }
if (firstTemplatePass) { if (firstTemplatePass) {
@ -1727,8 +1729,9 @@ export function directiveCreate<T>(
return instance; return instance;
} }
function addComponentLogic<T>( function addComponentLogic<T>(def: ComponentDefInternal<T>): void {
directiveIndex: number, instance: T, def: ComponentDefInternal<T>, hostNode: LNode): void { const hostNode = getLNode(previousOrParentTNode, viewData);
const tView = getOrCreateTView( const tView = getOrCreateTView(
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery); def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery);
@ -1737,15 +1740,13 @@ function addComponentLogic<T>(
const componentView = addToViewTree( const componentView = addToViewTree(
viewData, previousOrParentTNode.index as number, viewData, previousOrParentTNode.index as number,
createLViewData( createLViewData(
rendererFactory.createRenderer(hostNode.native as RElement, def), tView, instance, rendererFactory.createRenderer(hostNode.native as RElement, def), tView, null,
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer())); def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer()));
// We need to set the host node/data here because when the component LNode was created, // We need to set the host node/data here because when the component LNode was created,
// we didn't yet know it was a component (just an element). // we didn't yet know it was a component (just an element).
(hostNode as{data: LViewData}).data = componentView; (hostNode as{data: LViewData}).data = componentView;
(componentView as LViewData)[HOST_NODE] = getPreviousOrParentTNode() as TElementNode; (componentView as LViewData)[HOST_NODE] = previousOrParentTNode as TElementNode;
initChangeDetectorIfExisting(hostNode.nodeInjector, instance, componentView);
if (firstTemplatePass) queueComponentIndexForCheck(); if (firstTemplatePass) queueComponentIndexForCheck();
} }

View File

@ -70,22 +70,6 @@ export interface LInjector {
cbf5: number; cbf5: number;
cbf6: number; cbf6: number;
cbf7: number; cbf7: number;
/** Stores the TemplateRef so subsequent injections of the TemplateRef get the same instance. */
templateRef: TemplateRef<any>|null;
/** Stores the ViewContainerRef so subsequent injections of the ViewContainerRef get the same
* instance. */
viewContainerRef: ViewContainerRef|null;
/** Stores the ElementRef so subsequent injections of the ElementRef get the same instance. */
elementRef: ElementRef|null;
/**
* Stores the ChangeDetectorRef so subsequent injections of the ChangeDetectorRef get the
* same instance.
*/
changeDetectorRef: ChangeDetectorRef|null;
} }
// Note: This hack is necessary so we don't erroneously get a circular dependency // Note: This hack is necessary so we don't erroneously get a circular dependency

View File

@ -265,10 +265,10 @@ function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: T
} }
function readFromNodeInjector( function readFromNodeInjector(
nodeInjector: LInjector, tNode: TNode, currentView: LViewData, tNode: TNode, currentView: LViewData, read: QueryReadType<any>| Type<any>,
read: QueryReadType<any>| Type<any>, directiveIdx: number): any { directiveIdx: number): any {
if (read instanceof ReadFromInjectorFn) { if (read instanceof ReadFromInjectorFn) {
return read.read(nodeInjector, tNode, directiveIdx); return read.read(tNode, currentView, directiveIdx);
} else { } else {
const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type<any>); const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type<any>);
if (matchingIdx !== null) { if (matchingIdx !== null) {
@ -282,10 +282,6 @@ function add(
query: LQuery<any>| null, tNode: TElementNode | TContainerNode | TElementContainerNode) { query: LQuery<any>| null, tNode: TElementNode | TContainerNode | TElementContainerNode) {
const currentView = _getViewData(); const currentView = _getViewData();
// TODO: remove this lookup when nodeInjector is removed from LNode
const nodeInjector =
getOrCreateNodeInjectorForNode(getLNode(tNode, currentView), tNode, currentView);
while (query) { while (query) {
const predicate = query.predicate; const predicate = query.predicate;
const type = predicate.type; const type = predicate.type;
@ -294,8 +290,8 @@ function add(
if (directiveIdx !== null) { if (directiveIdx !== null) {
// a node is matching a predicate - determine what to read // a node is matching a predicate - determine what to read
// if read token and / or strategy is not specified, use type as read token // if read token and / or strategy is not specified, use type as read token
const result = readFromNodeInjector( const result =
nodeInjector, tNode, currentView, predicate.read || type, directiveIdx); readFromNodeInjector(tNode, currentView, predicate.read || type, directiveIdx);
if (result !== null) { if (result !== null) {
addMatch(query, result); addMatch(query, result);
} }
@ -308,8 +304,7 @@ function add(
// a node is matching a predicate - determine what to read // a node is matching a predicate - determine what to read
// note that queries using name selector must specify read strategy // note that queries using name selector must specify read strategy
ngDevMode && assertDefined(predicate.read, 'the node should have a predicate'); ngDevMode && assertDefined(predicate.read, 'the node should have a predicate');
const result = readFromNodeInjector( const result = readFromNodeInjector(tNode, currentView, predicate.read !, directiveIdx);
nodeInjector, tNode, currentView, predicate.read !, directiveIdx);
if (result !== null) { if (result !== null) {
addMatch(query, result); addMatch(query, result);
} }

View File

@ -13,7 +13,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEn
import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions'; import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
import {TViewNode} from './interfaces/node'; import {TViewNode} from './interfaces/node';
import {FLAGS, LViewData, LViewFlags} from './interfaces/view'; import {DIRECTIVES, FLAGS, LViewData, LViewFlags, PARENT} from './interfaces/view';
import {destroyLView} from './node_manipulation'; import {destroyLView} from './node_manipulation';
@ -37,20 +37,14 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
*/ */
_tViewNode: TViewNode|null = null; _tViewNode: TViewNode|null = null;
context: T;
// TODO(issue/24571): remove '!'. // TODO(issue/24571): remove '!'.
rootNodes !: any[]; rootNodes !: any[];
constructor(_view: LViewData, context: T|null) { constructor(_view: LViewData, private _context: T|null, private _componentIndex: number) {
this.context = context !;
this._view = _view; this._view = _view;
} }
/** @internal */ get context(): T { return this._context ? this._context : this._lookUpContext(); }
_setComponentContext(view: LViewData, context: T) {
this._view = view;
this.context = context;
}
get destroyed(): boolean { get destroyed(): boolean {
return (this._view[FLAGS] & LViewFlags.Destroyed) === LViewFlags.Destroyed; return (this._view[FLAGS] & LViewFlags.Destroyed) === LViewFlags.Destroyed;
@ -260,11 +254,15 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
detachFromAppRef() { this._appRef = null; } detachFromAppRef() { this._appRef = null; }
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; } attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
private _lookUpContext(): T {
return this._context = this._view[PARENT] ![DIRECTIVES] ![this._componentIndex] as T;
}
} }
/** @internal */ /** @internal */
export class RootViewRef<T> extends ViewRef<T> { export class RootViewRef<T> extends ViewRef<T> {
constructor(public _view: LViewData) { super(_view, null); } constructor(public _view: LViewData) { super(_view, null, -1); }
detectChanges(): void { detectChangesInRootView(this._view); } detectChanges(): void { detectChangesInRootView(this._view); }

View File

@ -266,9 +266,6 @@
{ {
"name": "hostElement" "name": "hostElement"
}, },
{
"name": "initChangeDetectorIfExisting"
},
{ {
"name": "invertObject" "name": "invertObject"
}, },

View File

@ -404,9 +404,15 @@
{ {
"name": "contextViewData" "name": "contextViewData"
}, },
{
"name": "createContainerRef"
},
{ {
"name": "createDirectivesAndLocals" "name": "createDirectivesAndLocals"
}, },
{
"name": "createElementRef"
},
{ {
"name": "createEmbeddedViewAndNode" "name": "createEmbeddedViewAndNode"
}, },
@ -443,6 +449,9 @@
{ {
"name": "createTView" "name": "createTView"
}, },
{
"name": "createTemplateRef"
},
{ {
"name": "createTextNode" "name": "createTextNode"
}, },
@ -452,6 +461,9 @@
{ {
"name": "createViewQuery" "name": "createViewQuery"
}, },
{
"name": "createViewRef"
},
{ {
"name": "defineComponent" "name": "defineComponent"
}, },
@ -620,18 +632,6 @@
{ {
"name": "getMultiStartIndex" "name": "getMultiStartIndex"
}, },
{
"name": "getOrCreateChangeDetectorRef"
},
{
"name": "getOrCreateContainerRef"
},
{
"name": "getOrCreateElementRef"
},
{
"name": "getOrCreateHostChangeDetector"
},
{ {
"name": "getOrCreateInjectable" "name": "getOrCreateInjectable"
}, },
@ -647,9 +647,6 @@
{ {
"name": "getOrCreateTView" "name": "getOrCreateTView"
}, },
{
"name": "getOrCreateTemplateRef"
},
{ {
"name": "getParentLNode" "name": "getParentLNode"
}, },
@ -719,9 +716,6 @@
{ {
"name": "hostElement" "name": "hostElement"
}, },
{
"name": "initChangeDetectorIfExisting"
},
{ {
"name": "inject" "name": "inject"
}, },

View File

@ -1274,9 +1274,15 @@
{ {
"name": "couldBeInjectableType" "name": "couldBeInjectableType"
}, },
{
"name": "createContainerRef"
},
{ {
"name": "createDirectivesAndLocals" "name": "createDirectivesAndLocals"
}, },
{
"name": "createElementRef"
},
{ {
"name": "createEmbeddedViewAndNode" "name": "createEmbeddedViewAndNode"
}, },
@ -1325,6 +1331,9 @@
{ {
"name": "createTView" "name": "createTView"
}, },
{
"name": "createTemplateRef"
},
{ {
"name": "createTextNode" "name": "createTextNode"
}, },
@ -1334,6 +1343,9 @@
{ {
"name": "createViewQuery" "name": "createViewQuery"
}, },
{
"name": "createViewRef"
},
{ {
"name": "dateGetter" "name": "dateGetter"
}, },
@ -1706,18 +1718,6 @@
{ {
"name": "getNumberOfCurrencyDigits" "name": "getNumberOfCurrencyDigits"
}, },
{
"name": "getOrCreateChangeDetectorRef"
},
{
"name": "getOrCreateContainerRef"
},
{
"name": "getOrCreateElementRef"
},
{
"name": "getOrCreateHostChangeDetector"
},
{ {
"name": "getOrCreateInjectable" "name": "getOrCreateInjectable"
}, },
@ -1733,9 +1733,6 @@
{ {
"name": "getOrCreateTView" "name": "getOrCreateTView"
}, },
{
"name": "getOrCreateTemplateRef"
},
{ {
"name": "getOriginalError" "name": "getOriginalError"
}, },
@ -1844,9 +1841,6 @@
{ {
"name": "identity" "name": "identity"
}, },
{
"name": "initChangeDetectorIfExisting"
},
{ {
"name": "initDomAdapter" "name": "initDomAdapter"
}, },

View File

@ -11,17 +11,18 @@ import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
import {defineComponent} from '../../src/render3/definition'; import {defineComponent} from '../../src/render3/definition';
import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di'; import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di';
import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectRenderer2, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; import {PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectRenderer2, injectTemplateRef, injectViewContainerRef, load} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, loadDirective, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions'; import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, loadDirective, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions';
import {LInjector} from '../../src/render3/interfaces/injector'; import {LInjector} from '../../src/render3/interfaces/injector';
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node'; import {AttributeMarker, LContainerNode, LElementNode, TNodeType} from '../../src/render3/interfaces/node';
import {LViewFlags} from '../../src/render3/interfaces/view'; import {LViewFlags} from '../../src/render3/interfaces/view';
import {ViewRef} from '../../src/render3/view_ref'; import {ViewRef} from '../../src/render3/view_ref';
import {getRendererFactory2} from './imported_renderer2'; import {getRendererFactory2} from './imported_renderer2';
import {ComponentFixture, createComponent, createDirective, renderComponent, toHtml} from './render_util'; import {ComponentFixture, createComponent, createDirective, renderComponent, toHtml} from './render_util';
import {NgIf} from './common_with_def';
describe('di', () => { describe('di', () => {
describe('no dependencies', () => { describe('no dependencies', () => {
@ -787,6 +788,10 @@ describe('di', () => {
describe('ElementRef', () => { describe('ElementRef', () => {
it('should create directive with ElementRef dependencies', () => { it('should create directive with ElementRef dependencies', () => {
let dir !: Directive;
let dirSameInstance !: DirectiveSameInstance;
let divNode !: LElementNode;
class Directive { class Directive {
value: string; value: string;
constructor(public elementRef: ElementRef) { constructor(public elementRef: ElementRef) {
@ -795,46 +800,74 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: Directive, type: Directive,
selectors: [['', 'dir', '']], selectors: [['', 'dir', '']],
factory: () => new Directive(injectElementRef()), factory: () => dir = new Directive(injectElementRef()),
features: [PublicFeature], features: [PublicFeature],
exportAs: 'dir' exportAs: 'dir'
}); });
} }
class DirectiveSameInstance { class DirectiveSameInstance {
value: boolean; isSameInstance: boolean;
constructor(elementRef: ElementRef, directive: Directive) { constructor(public elementRef: ElementRef, directive: Directive) {
this.value = (elementRef === directive.elementRef) && elementRef instanceof ElementRef; this.isSameInstance = elementRef === directive.elementRef;
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
selectors: [['', 'dirSame', '']], selectors: [['', 'dirSame', '']],
factory: () => new DirectiveSameInstance(injectElementRef(), directiveInject(Directive)), factory: () => dirSameInstance =
new DirectiveSameInstance(injectElementRef(), directiveInject(Directive)),
exportAs: 'dirSame' exportAs: 'dirSame'
}); });
} }
/** /** <div dir dirSame></div> */
* <div dir dirSame #dirSame="dirSame" #dir="dir">
* {{ dir.value }} - {{ dirSame.value }}
* </div>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) { const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dirSame', 'dirSame', 'dir', 'dir']); elementStart(0, 'div', ['dir', '', 'dirSame', '']);
{ text(3); }
elementEnd(); elementEnd();
divNode = load(0);
} }
}, 1, 0, [Directive, DirectiveSameInstance]);
if (rf & RenderFlags.Update) {
const tmp1 = reference(1) as any;
const tmp2 = reference(2) as any;
textBinding(3, interpolation2('', tmp2.value, '-', tmp1.value, ''));
}
}, 4, 2, [Directive, DirectiveSameInstance]);
const fixture = new ComponentFixture(App); const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<div dir="" dirsame="">ElementRef-true</div>'); expect(dir.value).toEqual('ElementRef');
expect(dir.elementRef.nativeElement).toEqual(divNode.native);
expect(dirSameInstance.elementRef.nativeElement).toEqual(divNode.native);
// Each ElementRef instance should be unique
expect(dirSameInstance.isSameInstance).toBe(false);
});
it('should create ElementRef with comment if requesting directive is on <ng-template> node',
() => {
let dir !: Directive;
let commentNode !: LContainerNode;
class Directive {
value: string;
constructor(public elementRef: ElementRef) {
this.value = (elementRef.constructor as any).name;
}
static ngDirectiveDef = defineDirective({
type: Directive,
selectors: [['', 'dir', '']],
factory: () => dir = new Directive(injectElementRef()),
features: [PublicFeature],
exportAs: 'dir'
});
}
/** <ng-template dir></ng-template> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
template(0, () => {}, 0, 0, null, ['dir', '']);
commentNode = load(0);
}
}, 1, 0, [Directive]);
const fixture = new ComponentFixture(App);
expect(dir.value).toEqual('ElementRef');
expect(dir.elementRef.nativeElement).toEqual(commentNode.native);
}); });
}); });
@ -855,9 +888,9 @@ describe('di', () => {
} }
class DirectiveSameInstance { class DirectiveSameInstance {
value: boolean; isSameInstance: boolean;
constructor(templateRef: TemplateRef<any>, directive: Directive) { constructor(templateRef: TemplateRef<any>, directive: Directive) {
this.value = templateRef === directive.templateRef; this.isSameInstance = templateRef === directive.templateRef;
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
@ -881,12 +914,13 @@ describe('di', () => {
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
const tmp1 = reference(1) as any; const tmp1 = reference(1) as any;
const tmp2 = reference(2) as any; const tmp2 = reference(2) as any;
textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, '')); textBinding(3, interpolation2('', tmp1.value, '-', tmp2.isSameInstance, ''));
} }
}, 4, 2, [Directive, DirectiveSameInstance]); }, 4, 2, [Directive, DirectiveSameInstance]);
const fixture = new ComponentFixture(App); const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('TemplateRef-true'); // Each TemplateRef instance should be unique
expect(fixture.html).toEqual('TemplateRef-false');
}); });
}); });
@ -907,9 +941,9 @@ describe('di', () => {
} }
class DirectiveSameInstance { class DirectiveSameInstance {
value: boolean; isSameInstance: boolean;
constructor(viewContainerRef: ViewContainerRef, directive: Directive) { constructor(viewContainerRef: ViewContainerRef, directive: Directive) {
this.value = viewContainerRef === directive.viewContainerRef; this.isSameInstance = viewContainerRef === directive.viewContainerRef;
} }
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: DirectiveSameInstance, type: DirectiveSameInstance,
@ -934,12 +968,13 @@ describe('di', () => {
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
const tmp1 = reference(1) as any; const tmp1 = reference(1) as any;
const tmp2 = reference(2) as any; const tmp2 = reference(2) as any;
textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, '')); textBinding(3, interpolation2('', tmp1.value, '-', tmp2.isSameInstance, ''));
} }
}, 4, 2, [Directive, DirectiveSameInstance]); }, 4, 2, [Directive, DirectiveSameInstance]);
const fixture = new ComponentFixture(App); const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<div dir="" dirsame="">ViewContainerRef-true</div>'); // Each ViewContainerRef instance should be unique
expect(fixture.html).toEqual('<div dir="" dirsame="">ViewContainerRef-false</div>');
}); });
}); });
@ -990,31 +1025,10 @@ describe('di', () => {
}); });
} }
class IfDirective { const directives = [MyComp, Directive, DirectiveSameInstance, NgIf];
/* @Input */
myIf = true;
constructor(public template: TemplateRef<any>, public vcr: ViewContainerRef) {} it('should inject current component ChangeDetectorRef into directives on the same node as components',
() => {
ngOnChanges() {
if (this.myIf) {
this.vcr.createEmbeddedView(this.template);
}
}
static ngDirectiveDef = defineDirective({
type: IfDirective,
selectors: [['', 'myIf', '']],
factory: () => new IfDirective(injectTemplateRef(), injectViewContainerRef()),
inputs: {myIf: 'myIf'},
features: [PublicFeature, NgOnChangesFeature]
});
}
const directives = [MyComp, Directive, DirectiveSameInstance, IfDirective];
it('should inject current component ChangeDetectorRef into directives on components', () => {
/** <my-comp dir dirSameInstance #dir="dir"></my-comp> {{ dir.value }} */ /** <my-comp dir dirSameInstance #dir="dir"></my-comp> {{ dir.value }} */
const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
@ -1032,11 +1046,12 @@ describe('di', () => {
expect(toHtml(app)).toEqual('<my-comp dir="" dirsame=""></my-comp>ViewRef'); expect(toHtml(app)).toEqual('<my-comp dir="" dirsame=""></my-comp>ViewRef');
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp); expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
expect(dir !.cdr).toBe(comp !.cdr); // Each ChangeDetectorRef instance should be unique
expect(dir !.cdr).toBe(dirSameInstance !.cdr); expect(dir !.cdr).not.toBe(comp !.cdr);
expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
}); });
it('should inject host component ChangeDetectorRef into directives on elements', () => { it('should inject host component ChangeDetectorRef into directives on normal elements', () => {
class MyApp { class MyApp {
constructor(public cdr: ChangeDetectorRef) {} constructor(public cdr: ChangeDetectorRef) {}
@ -1067,11 +1082,13 @@ describe('di', () => {
expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>'); expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>');
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect(dir !.cdr).toBe(app.cdr); // Each ChangeDetectorRef instance should be unique
expect(dir !.cdr).toBe(dirSameInstance !.cdr); expect(dir !.cdr).not.toBe(app.cdr);
expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
}); });
it('should inject host component ChangeDetectorRef into directives in ContentChildren', () => { it('should inject host component ChangeDetectorRef into directives in a component\'s ContentChildren',
() => {
class MyApp { class MyApp {
constructor(public cdr: ChangeDetectorRef) {} constructor(public cdr: ChangeDetectorRef) {}
@ -1107,8 +1124,9 @@ describe('di', () => {
expect(toHtml(app)).toEqual('<my-comp><div dir="" dirsame=""></div></my-comp>ViewRef'); expect(toHtml(app)).toEqual('<my-comp><div dir="" dirsame=""></div></my-comp>ViewRef');
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect(dir !.cdr).toBe(app !.cdr); // Each ChangeDetectorRef instance should be unique
expect(dir !.cdr).toBe(dirSameInstance !.cdr); expect(dir !.cdr).not.toBe(app !.cdr);
expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
}); });
it('should inject host component ChangeDetectorRef into directives in embedded views', () => { it('should inject host component ChangeDetectorRef into directives in embedded views', () => {
@ -1161,8 +1179,9 @@ describe('di', () => {
expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>'); expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>');
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect(dir !.cdr).toBe(app.cdr); // Each ChangeDetectorRef instance should be unique
expect(dir !.cdr).toBe(dirSameInstance !.cdr); expect(dir !.cdr).not.toBe(app.cdr);
expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
}); });
it('should inject host component ChangeDetectorRef into directives on containers', () => { it('should inject host component ChangeDetectorRef into directives on containers', () => {
@ -1189,10 +1208,10 @@ describe('di', () => {
factory: () => new MyApp(injectChangeDetectorRef()), factory: () => new MyApp(injectChangeDetectorRef()),
consts: 1, consts: 1,
vars: 0, vars: 0,
/** <div *myIf="showing" dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */ /** <div *ngIf="showing" dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */
template: function(rf: RenderFlags, ctx: MyApp) { template: function(rf: RenderFlags, ctx: MyApp) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
template(0, C1, 3, 1, null, ['myIf', 'showing']); template(0, C1, 3, 1, null, ['ngIf', 'showing']);
} }
}, },
directives: directives directives: directives
@ -1203,8 +1222,9 @@ describe('di', () => {
expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>'); expect(toHtml(app)).toEqual('<div dir="" dirsame="">ViewRef</div>');
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect(dir !.cdr).toBe(app.cdr); // Each ChangeDetectorRef instance should be unique
expect(dir !.cdr).toBe(dirSameInstance !.cdr); expect(dir !.cdr).not.toBe(app.cdr);
expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
}); });
}); });

View File

@ -10,7 +10,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {RenderFlags} from '@angular/core/src/render3'; import {RenderFlags} from '@angular/core/src/render3';
import {RendererStyleFlags2, RendererType2} from '../../src/render/api'; import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di'; import {createTemplateRef, getOrCreateNodeInjectorForNode} from '../../src/render3/di';
import {AttributeMarker, defineComponent, defineDirective, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; import {AttributeMarker, defineComponent, defineDirective, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, text, textBinding, template} from '../../src/render3/instructions'; import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, text, textBinding, template} from '../../src/render3/instructions';