refactor(ivy): migrate previousOrParentNode to use TNodes (#25829)

PR Close #25829
This commit is contained in:
Kara Erickson 2018-09-05 16:15:37 -07:00 committed by Igor Minar
parent 2a21ca09d2
commit 83a1334876
10 changed files with 194 additions and 171 deletions

View File

@ -111,7 +111,7 @@ export function renderComponent<T>(
componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
rootView[INJECTOR] = opts.injector || null; rootView[INJECTOR] = opts.injector || null;
const oldView = enterView(rootView, null !); const oldView = enterView(rootView, null);
let elementNode: LElementNode; let elementNode: LElementNode;
let component: T; let component: T;
try { try {
@ -121,7 +121,7 @@ export function renderComponent<T>(
elementNode = hostElement(componentTag, hostNode, componentDef, sanitizer); elementNode = hostElement(componentTag, hostNode, componentDef, sanitizer);
// Create directive instance with factory() and store at index 0 in directives array // Create directive instance with factory() and store at index 0 in directives array
component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef); component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef, elementNode);
if (componentDef.hostBindings) { if (componentDef.hostBindings) {
queueHostBindingForCheck(0, componentDef.hostVars); queueHostBindingForCheck(0, componentDef.hostVars);
} }

View File

@ -126,7 +126,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
rootView[INJECTOR] = ngModule && ngModule.injector || null; rootView[INJECTOR] = ngModule && ngModule.injector || null;
// rootView is the parent when bootstrapping // rootView is the parent when bootstrapping
const oldView = enterView(rootView, null !); const oldView = enterView(rootView, null);
let component: T; let component: T;
let elementNode: LElementNode; let elementNode: LElementNode;
@ -137,7 +137,8 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
elementNode = hostElement(componentTag, hostNode, this.componentDef); elementNode = hostElement(componentTag, hostNode, this.componentDef);
// Create directive instance with factory() and store at index 0 in directives array // Create directive instance with factory() and store at index 0 in directives array
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef); component =
baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef, elementNode);
if (this.componentDef.hostBindings) { if (this.componentDef.hostBindings) {
queueHostBindingForCheck(0, this.componentDef.hostVars); queueHostBindingForCheck(0, this.componentDef.hostVars);
} }

View File

@ -24,7 +24,7 @@ import {Type} from '../type';
import {assertDefined, assertGreaterThan, assertLessThan} from './assert'; import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
import {ComponentFactoryResolver} from './component_ref'; import {ComponentFactoryResolver} from './component_ref';
import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions'; import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getPreviousOrParentTNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
import {VIEWS} from './interfaces/container'; import {VIEWS} from './interfaces/container';
import {DirectiveDefInternal, RenderFlags} from './interfaces/definition'; import {DirectiveDefInternal, RenderFlags} from './interfaces/definition';
import {LInjector} from './interfaces/injector'; import {LInjector} from './interfaces/injector';
@ -273,10 +273,9 @@ export function injectRenderer2(): Renderer2 {
* @experimental * @experimental
*/ */
export function injectAttribute(attrNameToInject: string): string|undefined { export function injectAttribute(attrNameToInject: string): string|undefined {
const lNode = getPreviousOrParentNode(); const tNode = getPreviousOrParentTNode();
ngDevMode && assertNodeOfPossibleTypes( ngDevMode && assertNodeOfPossibleTypes(
lNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
const tNode = lNode.tNode;
ngDevMode && assertDefined(tNode, 'expecting tNode'); ngDevMode && assertDefined(tNode, 'expecting tNode');
const attrs = tNode.attrs; const attrs = tNode.attrs;
if (attrs) { if (attrs) {
@ -573,8 +572,9 @@ export const QUERY_READ_ELEMENT_REF =
export const QUERY_READ_FROM_NODE = export const QUERY_READ_FROM_NODE =
(new ReadFromInjectorFn<any>((injector: LInjector, node: LNode, directiveIdx: number) => { (new ReadFromInjectorFn<any>((injector: LInjector, node: LNode, directiveIdx: number) => {
ngDevMode && assertNodeOfPossibleTypes( ngDevMode &&
node, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); assertNodeOfPossibleTypes(
node.tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
if (directiveIdx > -1) { if (directiveIdx > -1) {
return node.view[DIRECTIVES] ![directiveIdx]; return node.view[DIRECTIVES] ![directiveIdx];
} }
@ -605,17 +605,17 @@ class ElementRef implements viewEngine_ElementRef {
export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainerRef { export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainerRef {
if (!di.viewContainerRef) { if (!di.viewContainerRef) {
const vcRefHost = di.node; const vcRefHost = di.node;
const hostTNode = vcRefHost.tNode as TElementNode | TContainerNode;
ngDevMode && assertNodeOfPossibleTypes( ngDevMode && assertNodeOfPossibleTypes(
vcRefHost, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
const hostParent = getParentLNode(vcRefHost) !; const hostParent = getParentLNode(vcRefHost) !;
const lContainer = createLContainer(hostParent, vcRefHost.view, true); const lContainer = createLContainer(hostParent, vcRefHost.view, true);
const comment = vcRefHost.view[RENDERER].createComment(ngDevMode ? 'container' : ''); const comment = vcRefHost.view[RENDERER].createComment(ngDevMode ? 'container' : '');
const lContainerNode: LContainerNode = const lContainerNode: LContainerNode = createLNodeObject(
createLNodeObject(TNodeType.Container, vcRefHost.view, hostParent, comment, lContainer); TNodeType.Container, vcRefHost.view, vcRefHost.nodeInjector, comment, lContainer);
appendChild(hostParent, comment, vcRefHost.view); appendChild(hostParent, comment, vcRefHost.view);
const hostTNode = vcRefHost.tNode as TElementNode | TContainerNode;
if (!hostTNode.dynamicContainerNode) { if (!hostTNode.dynamicContainerNode) {
hostTNode.dynamicContainerNode = hostTNode.dynamicContainerNode =
createTNode(TNodeType.Container, -1, null, null, hostTNode, null); createTNode(TNodeType.Container, -1, null, null, hostTNode, null);
@ -785,9 +785,9 @@ class ViewContainerRef implements viewEngine_ViewContainerRef {
*/ */
export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef<T> { export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef<T> {
if (!di.templateRef) { if (!di.templateRef) {
ngDevMode && assertNodeType(di.node, TNodeType.Container);
const hostNode = di.node as LContainerNode; const hostNode = di.node as LContainerNode;
const hostTNode = hostNode.tNode; const hostTNode = hostNode.tNode;
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>( di.templateRef = new TemplateRef<any>(
hostNode.view, getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(), hostNode.view, getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(),
@ -845,4 +845,4 @@ class TemplateRef<T> implements viewEngine_TemplateRef<T> {
*/ */
export function templateRefExtractor(lNode: LNodeWithLocalRefs) { export function templateRefExtractor(lNode: LNodeWithLocalRefs) {
return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode)); return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode));
} }

View File

@ -19,7 +19,7 @@ import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} fro
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container'; import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition'; import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {LInjector} from './interfaces/injector'; import {LInjector} from './interfaces/injector';
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LNodeWithLocalRefs, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node'; import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LNodeWithLocalRefs, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node';
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'; import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
import {LQueries} from './interfaces/query'; import {LQueries} from './interfaces/query';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
@ -28,7 +28,7 @@ import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation'; import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling'; import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling';
import {assertDataInRangeInternal, isDifferent, loadElementInternal, loadInternal, stringify} from './util'; import {assertDataInRangeInternal, isDifferent, loadElementInternal, loadInternal, readElementValue, stringify} from './util';
import {ViewRef} from './view_ref'; import {ViewRef} from './view_ref';
@ -125,18 +125,25 @@ export function restoreView(viewToRestore: OpaqueViewState) {
contextViewData = viewToRestore as any as LViewData; contextViewData = viewToRestore as any as LViewData;
} }
/** Used to set the parent property when nodes are created. */ /** Used to set the parent property when nodes are created and track query results. */
let previousOrParentNode: LNode; let previousOrParentTNode: TNode;
export function getPreviousOrParentNode(): LNode { export function getPreviousOrParentNode(): LNode {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) return previousOrParentTNode == null || previousOrParentTNode === tView.node ?
return previousOrParentNode; viewData[HOST_NODE] :
readElementValue(viewData[previousOrParentTNode.index]);
} }
export function getPreviousOrParentTNode(): TNode {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return previousOrParentTNode;
}
/** /**
* If `isParent` is: * If `isParent` is:
* - `true`: then `previousOrParentNode` points to a parent node. * - `true`: then `previousOrParentTNode` points to a parent node.
* - `false`: then `previousOrParentNode` points to previous node (sibling). * - `false`: then `previousOrParentTNode` points to previous node (sibling).
*/ */
let isParent: boolean; let isParent: boolean;
@ -148,18 +155,17 @@ let currentQueries: LQueries|null;
* Query instructions can ask for "current queries" in 2 different cases: * Query instructions can ask for "current queries" in 2 different cases:
* - when creating view queries (at the root of a component view, before any node is created - in * - when creating view queries (at the root of a component view, before any node is created - in
* this case currentQueries points to view queries) * this case currentQueries points to view queries)
* - when creating content queries (inb this previousOrParentNode points to a node on which we * - when creating content queries (i.e. this previousOrParentTNode points to a node on which we
* create content queries). * create content queries).
*/ */
export function getOrCreateCurrentQueries( export function getOrCreateCurrentQueries(
QueryType: {new (parent: null, shallow: null, deep: null): LQueries}): LQueries { QueryType: {new (parent: null, shallow: null, deep: null): LQueries}): LQueries {
const tNode = previousOrParentNode.tNode;
// if this is the first content query on a node, any existing LQueries needs to be cloned // if this is the first content query on a node, any existing LQueries needs to be cloned
// in subsequent template passes, the cloning occurs before directive instantiation. // in subsequent template passes, the cloning occurs before directive instantiation.
if (previousOrParentNode.data !== viewData && !isContentQueryHost(tNode)) { if (previousOrParentTNode && previousOrParentTNode !== tView.node &&
!isContentQueryHost(previousOrParentTNode)) {
currentQueries && (currentQueries = currentQueries.clone()); currentQueries && (currentQueries = currentQueries.clone());
tNode.flags |= TNodeFlags.hasContentQuery; previousOrParentTNode.flags |= TNodeFlags.hasContentQuery;
} }
return currentQueries || (currentQueries = new QueryType(null, null, null)); return currentQueries || (currentQueries = new QueryType(null, null, null));
@ -256,7 +262,8 @@ const enum BindingDirection {
* @param host Element to which the View is a child of * @param host Element to which the View is a child of
* @returns the previous state; * @returns the previous state;
*/ */
export function enterView(newView: LViewData, host: LElementNode | LViewNode | null): LViewData { export function enterView(
newView: LViewData, hostTNode: TElementNode | TViewNode | null): LViewData {
const oldView: LViewData = viewData; const oldView: LViewData = viewData;
directives = newView && newView[DIRECTIVES]; directives = newView && newView[DIRECTIVES];
tView = newView && newView[TVIEW]; tView = newView && newView[TVIEW];
@ -266,10 +273,8 @@ export function enterView(newView: LViewData, host: LElementNode | LViewNode | n
bindingRootIndex = newView && tView.bindingStartIndex; bindingRootIndex = newView && tView.bindingStartIndex;
renderer = newView && newView[RENDERER]; renderer = newView && newView[RENDERER];
if (host != null) { previousOrParentTNode = hostTNode !;
previousOrParentNode = host; isParent = true;
isParent = true;
}
viewData = contextViewData = newView; viewData = contextViewData = newView;
oldView && (oldView[QUERIES] = currentQueries); oldView && (oldView[QUERIES] = currentQueries);
@ -387,13 +392,13 @@ export function createLViewData<T>(
* (same properties assigned in the same order). * (same properties assigned in the same order).
*/ */
export function createLNodeObject( export function createLNodeObject(
type: TNodeType, currentView: LViewData, parent: LNode | null, type: TNodeType, currentView: LViewData, nodeInjector: LInjector | null,
native: RText | RElement | RComment | null, native: RText | RElement | RComment | null,
state: any): LElementNode&LTextNode&LViewNode&LContainerNode&LProjectionNode { state: any): LElementNode&LTextNode&LViewNode&LContainerNode&LProjectionNode {
return { return {
native: native as any, native: native as any,
view: currentView, view: currentView,
nodeInjector: parent ? parent.nodeInjector : null, nodeInjector: nodeInjector,
data: state, data: state,
tNode: null !, tNode: null !,
dynamicLContainerNode: null dynamicLContainerNode: null
@ -431,15 +436,16 @@ export function createLNode(
index: number, type: TNodeType, native: RText | RElement | RComment | null, name: string | null, index: number, type: TNodeType, native: RText | RElement | RComment | null, name: string | null,
attrs: TAttributes | null, state?: null | LViewData | LContainer): LElementNode&LTextNode& attrs: TAttributes | null, state?: null | LViewData | LContainer): LElementNode&LTextNode&
LViewNode&LContainerNode&LProjectionNode { LViewNode&LContainerNode&LProjectionNode {
const parent = isParent ? previousOrParentNode : const parent =
previousOrParentNode && getParentLNode(previousOrParentNode) !as LNode; isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent;
// Parents cannot cross component boundaries because components will be used in multiple places, // Parents cannot cross component boundaries because components will be used in multiple places,
// so it's only set if the view is the same. // so it's only set if the view is the same.
const tParent = const parentInSameView = parent && tView && parent !== tView.node;
parent && parent.view === viewData ? parent.tNode as TElementNode | TContainerNode : null; const tParent = parentInSameView ? parent as TElementNode | TContainerNode : null;
const isState = state != null; const isState = state != null;
const node = createLNodeObject(type, viewData, parent, native, isState ? state as any : null); const node = createLNodeObject(type, viewData, null, native, isState ? state as any : null);
if (index === -1 || type === TNodeType.View) { if (index === -1 || type === TNodeType.View) {
// View nodes are not stored in data because they can be added / removed at runtime (which // View nodes are not stored in data because they can be added / removed at runtime (which
@ -459,10 +465,10 @@ export function createLNode(
if (tData[adjustedIndex] == null) { if (tData[adjustedIndex] == null) {
const tNode = tData[adjustedIndex] = const tNode = tData[adjustedIndex] =
createTNode(type, adjustedIndex, name, attrs, tParent, null); createTNode(type, adjustedIndex, name, attrs, tParent, null);
if (!isParent && previousOrParentNode) { if (!isParent && previousOrParentTNode) {
const previousTNode = previousOrParentNode.tNode; previousOrParentTNode.next = tNode;
previousTNode.next = tNode; if (previousOrParentTNode.dynamicContainerNode)
if (previousTNode.dynamicContainerNode) previousTNode.dynamicContainerNode.next = tNode; previousOrParentTNode.dynamicContainerNode.next = tNode;
} }
} }
@ -472,26 +478,31 @@ export function createLNode(
} }
// Now link ourselves into the tree. // Now link ourselves into the tree.
if (isParent) { if (isParent && previousOrParentTNode) {
if (previousOrParentNode.tNode.child == null && previousOrParentNode.view === viewData || if (previousOrParentTNode.child == null && parentInSameView ||
previousOrParentNode.tNode.type === TNodeType.View) { previousOrParentTNode.type === TNodeType.View) {
// We are in the same view, which means we are adding content node to the parent View. // We are in the same view, which means we are adding content node to the parent View.
previousOrParentNode.tNode.child = node.tNode; previousOrParentTNode.child = node.tNode;
} }
} }
} }
// TODO: temporary, remove when removing LNode.nodeInjector
const parentLNode = index === -1 ? null : getParentLNode(node);
if (parentLNode) node.nodeInjector = parentLNode.nodeInjector;
// View nodes and host elements need to set their host node (components set host nodes later) // View nodes and host elements need to set their host node (components do not save host TNodes)
if ((type & TNodeType.ViewOrElement) === TNodeType.ViewOrElement && isState) { if ((type & TNodeType.ViewOrElement) === TNodeType.ViewOrElement && isState) {
const lViewData = state as LViewData; const lViewData = state as LViewData;
ngDevMode && ngDevMode &&
assertEqual( assertEqual(
lViewData[HOST_NODE], null, 'lViewData[HOST_NODE] should not have been initialized'); lViewData[HOST_NODE], null, 'lViewData[HOST_NODE] should not have been initialized');
lViewData[HOST_NODE] = node; lViewData[HOST_NODE] = node;
if (firstTemplatePass) lViewData[TVIEW].node = node.tNode; if (lViewData[TVIEW].firstTemplatePass) {
lViewData[TVIEW].node = node.tNode as TViewNode | TElementNode;
}
} }
previousOrParentNode = node; previousOrParentTNode = node.tNode;
isParent = true; isParent = true;
return node; return node;
} }
@ -520,7 +531,7 @@ export function adjustBlueprintForNewNode(view: LViewData) {
*/ */
export function resetComponentState() { export function resetComponentState() {
isParent = false; isParent = false;
previousOrParentNode = null !; previousOrParentTNode = null !;
elementDepthCount = 0; elementDepthCount = 0;
} }
@ -553,7 +564,7 @@ export function renderTemplate<T>(
} }
const hostView = host.data !; const hostView = host.data !;
ngDevMode && assertDefined(hostView, 'Host node should have an LView defined in host.data.'); ngDevMode && assertDefined(hostView, 'Host node should have an LView defined in host.data.');
renderComponentOrTemplate(host, hostView, context, templateFn); renderComponentOrTemplate(hostView, context, templateFn);
return host; return host;
} }
@ -566,9 +577,9 @@ export function createEmbeddedViewNode<T>(
tView: TView, context: T, declarationView: LViewData, renderer: Renderer3, tView: TView, context: T, declarationView: LViewData, renderer: Renderer3,
queries?: LQueries | null): LViewNode { queries?: LQueries | null): LViewNode {
const _isParent = isParent; const _isParent = isParent;
const _previousOrParentNode = previousOrParentNode; const _previousOrParentTNode = previousOrParentTNode;
isParent = true; isParent = true;
previousOrParentNode = null !; previousOrParentTNode = null !;
const lView = const lView =
createLViewData(renderer, tView, context, LViewFlags.CheckAlways, getCurrentSanitizer()); createLViewData(renderer, tView, context, LViewFlags.CheckAlways, getCurrentSanitizer());
@ -580,7 +591,7 @@ export function createEmbeddedViewNode<T>(
const viewNode = createLNode(-1, TNodeType.View, null, null, null, lView); const viewNode = createLNode(-1, TNodeType.View, null, null, null, lView);
isParent = _isParent; isParent = _isParent;
previousOrParentNode = _previousOrParentNode; previousOrParentTNode = _previousOrParentTNode;
return viewNode; return viewNode;
} }
@ -598,7 +609,7 @@ export function renderEmbeddedTemplate<T>(
viewNode: LViewNode | LElementNode, tView: TView, context: T, rf: RenderFlags): LViewNode| viewNode: LViewNode | LElementNode, tView: TView, context: T, rf: RenderFlags): LViewNode|
LElementNode { LElementNode {
const _isParent = isParent; const _isParent = isParent;
const _previousOrParentNode = previousOrParentNode; const _previousOrParentTNode = previousOrParentTNode;
let oldView: LViewData; let oldView: LViewData;
if (viewNode.data ![PARENT] == null && viewNode.data ![CONTEXT] && !tView.template) { if (viewNode.data ![PARENT] == null && viewNode.data ![CONTEXT] && !tView.template) {
// This is a root view inside the view tree // This is a root view inside the view tree
@ -606,9 +617,9 @@ export function renderEmbeddedTemplate<T>(
} else { } else {
try { try {
isParent = true; isParent = true;
previousOrParentNode = null !; previousOrParentTNode = null !;
oldView = enterView(viewNode.data !, viewNode); oldView = enterView(viewNode.data !, tView.node);
namespaceHTML(); namespaceHTML();
tView.template !(rf, context); tView.template !(rf, context);
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
@ -622,7 +633,7 @@ export function renderEmbeddedTemplate<T>(
const isCreationOnly = (rf & RenderFlags.Create) === RenderFlags.Create; const isCreationOnly = (rf & RenderFlags.Create) === RenderFlags.Create;
leaveView(oldView !, isCreationOnly); leaveView(oldView !, isCreationOnly);
isParent = _isParent; isParent = _isParent;
previousOrParentNode = _previousOrParentNode; previousOrParentTNode = _previousOrParentTNode;
} }
} }
return viewNode; return viewNode;
@ -644,9 +655,8 @@ export function nextContext<T = any>(level: number = 1): T {
} }
export function renderComponentOrTemplate<T>( export function renderComponentOrTemplate<T>(
node: LElementNode, hostView: LViewData, componentOrContext: T, hostView: LViewData, componentOrContext: T, templateFn?: ComponentTemplate<T>) {
templateFn?: ComponentTemplate<T>) { const oldView = enterView(hostView, null);
const oldView = enterView(hostView, node);
try { try {
if (rendererFactory.begin) { if (rendererFactory.begin) {
rendererFactory.begin(); rendererFactory.begin();
@ -757,14 +767,15 @@ export function elementContainerEnd(): void {
isParent = false; isParent = false;
} else { } else {
ngDevMode && assertHasParent(); ngDevMode && assertHasParent();
previousOrParentNode = getParentLNode(previousOrParentNode) as LElementContainerNode; previousOrParentTNode = previousOrParentTNode.parent !;
} }
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.ElementContainer); // Can be read directly because ng-containers can't have style bindings
const previousNode = viewData[previousOrParentTNode.index];
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.ElementContainer);
currentQueries && (currentQueries = currentQueries.addNode(previousNode));
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentNode)); queueLifecycleHooks(previousOrParentTNode.flags, tView);
queueLifecycleHooks(previousOrParentNode.tNode.flags, tView);
} }
/** /**
@ -847,7 +858,7 @@ function createDirectivesAndLocals(
localRefExtractor: LocalRefExtractor = nativeNodeLocalRefExtractor) { localRefExtractor: LocalRefExtractor = nativeNodeLocalRefExtractor) {
if (firstTemplatePass) { if (firstTemplatePass) {
ngDevMode && ngDevMode.firstTemplatePass++; ngDevMode && ngDevMode.firstTemplatePass++;
cacheMatchingDirectivesForNode(lNode.tNode, tView, localRefs || null); cacheMatchingDirectivesForNode(previousOrParentTNode, tView, localRefs || null);
} else { } else {
instantiateDirectivesDirectly(); instantiateDirectivesDirectly();
} }
@ -913,7 +924,7 @@ export function resolveDirective(
/** Stores index of component's host element so it will be queued for view refresh during CD. */ /** Stores index of component's host element so it will be queued for view refresh during CD. */
function queueComponentIndexForCheck(): void { function queueComponentIndexForCheck(): void {
if (firstTemplatePass) { if (firstTemplatePass) {
(tView.components || (tView.components = [])).push(previousOrParentNode.tNode.index); (tView.components || (tView.components = [])).push(previousOrParentTNode.index);
} }
} }
@ -929,7 +940,7 @@ export function queueHostBindingForCheck(dirIndex: number, hostVars: number): vo
viewData.push(NO_CHANGE); viewData.push(NO_CHANGE);
} }
(tView.hostBindings || (tView.hostBindings = [ (tView.hostBindings || (tView.hostBindings = [
])).push(dirIndex, previousOrParentNode.tNode.index - HEADER_OFFSET); ])).push(dirIndex, previousOrParentTNode.index - HEADER_OFFSET);
} }
/** Sets the context for a ChangeDetectorRef to the given instance. */ /** Sets the context for a ChangeDetectorRef to the given instance. */
@ -955,15 +966,14 @@ function instantiateDirectivesDirectly() {
ngDevMode && assertEqual( ngDevMode && assertEqual(
firstTemplatePass, false, firstTemplatePass, false,
`Directives should only be instantiated directly after first template pass`); `Directives should only be instantiated directly after first template pass`);
const tNode = previousOrParentNode.tNode; const count = previousOrParentTNode.flags & TNodeFlags.DirectiveCountMask;
const count = tNode.flags & TNodeFlags.DirectiveCountMask;
if (isContentQueryHost(tNode) && currentQueries) { if (isContentQueryHost(previousOrParentTNode) && currentQueries) {
currentQueries = currentQueries.clone(); currentQueries = currentQueries.clone();
} }
if (count > 0) { if (count > 0) {
const start = tNode.flags >> TNodeFlags.DirectiveStartingIndexShift; const start = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count; const end = start + count;
const tDirectives = tView.directives !; const tDirectives = tView.directives !;
@ -1213,40 +1223,38 @@ export function hostElement(
*/ */
export function listener( export function listener(
eventName: string, listenerFn: (e?: any) => any, useCapture = false): void { eventName: string, listenerFn: (e?: any) => any, useCapture = false): void {
ngDevMode && const tNode = previousOrParentTNode;
assertNodeOfPossibleTypes( ngDevMode && assertNodeOfPossibleTypes(
previousOrParentNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer); tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer);
const node = previousOrParentNode;
// add native event listener - applicable to elements only // add native event listener - applicable to elements only
if (previousOrParentNode.tNode.type === TNodeType.Element) { if (tNode.type === TNodeType.Element) {
const native = node.native as RElement; const node = getPreviousOrParentNode() as LElementNode;
ngDevMode && ngDevMode.rendererAddEventListener++; ngDevMode && ngDevMode.rendererAddEventListener++;
// In order to match current behavior, native DOM event listeners must be added for all // In order to match current behavior, native DOM event listeners must be added for all
// events (including outputs). // events (including outputs).
if (isProceduralRenderer(renderer)) { if (isProceduralRenderer(renderer)) {
const wrappedListener = wrapListenerWithDirtyLogic(viewData, listenerFn); const wrappedListener = wrapListenerWithDirtyLogic(viewData, listenerFn);
const cleanupFn = renderer.listen(native, eventName, wrappedListener); const cleanupFn = renderer.listen(node.native, eventName, wrappedListener);
storeCleanupFn(viewData, cleanupFn); storeCleanupFn(viewData, cleanupFn);
} else { } else {
const wrappedListener = wrapListenerWithDirtyAndDefault(viewData, listenerFn); const wrappedListener = wrapListenerWithDirtyAndDefault(viewData, listenerFn);
native.addEventListener(eventName, wrappedListener, useCapture); node.native.addEventListener(eventName, wrappedListener, useCapture);
const cleanupInstances = getCleanup(viewData); const cleanupInstances = getCleanup(viewData);
cleanupInstances.push(wrappedListener); cleanupInstances.push(wrappedListener);
if (firstTemplatePass) { if (firstTemplatePass) {
getTViewCleanup(viewData).push( getTViewCleanup(viewData).push(
eventName, node.tNode.index, cleanupInstances !.length - 1, useCapture); eventName, tNode.index, cleanupInstances !.length - 1, useCapture);
} }
} }
} }
// subscribe to directive outputs // subscribe to directive outputs
let tNode: TNode|null = node.tNode;
if (tNode.outputs === undefined) { if (tNode.outputs === undefined) {
// if we create TNode here, inputs must be undefined so we know they still need to be // if we create TNode here, inputs must be undefined so we know they still need to be
// checked // checked
tNode.outputs = generatePropertyAliases(node.tNode.flags, BindingDirection.Output); tNode.outputs = generatePropertyAliases(tNode.flags, BindingDirection.Output);
} }
const outputs = tNode.outputs; const outputs = tNode.outputs;
@ -1307,11 +1315,12 @@ export function elementEnd(): void {
isParent = false; isParent = false;
} else { } else {
ngDevMode && assertHasParent(); ngDevMode && assertHasParent();
previousOrParentNode = getParentLNode(previousOrParentNode) as LElementNode; previousOrParentTNode = previousOrParentTNode.parent !;
} }
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Element); ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Element);
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentNode)); currentQueries && (currentQueries = currentQueries.addNode(getPreviousOrParentNode()));
queueLifecycleHooks(previousOrParentNode.tNode.flags, tView);
queueLifecycleHooks(previousOrParentTNode.flags, tView);
elementDepthCount--; elementDepthCount--;
} }
@ -1514,7 +1523,7 @@ export function elementStyling<T>(
classDeclarations?: (string | boolean | InitialStylingFlags)[] | null, classDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null, styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
styleSanitizer?: StyleSanitizeFn | null): void { styleSanitizer?: StyleSanitizeFn | null): void {
const tNode = previousOrParentNode.tNode; const tNode = previousOrParentTNode;
if (!tNode.stylingTemplate) { if (!tNode.stylingTemplate) {
// initialize the styling template. // initialize the styling template.
tNode.stylingTemplate = tNode.stylingTemplate =
@ -1693,14 +1702,13 @@ 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 instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef); const hostNode = getPreviousOrParentNode();
const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, hostNode);
ngDevMode && assertDefined(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
const tNode = previousOrParentNode.tNode;
const isComponent = (directiveDef as ComponentDefInternal<T>).template; const isComponent = (directiveDef as ComponentDefInternal<T>).template;
if (isComponent) { if (isComponent) {
addComponentLogic(directiveDefIdx, directive, directiveDef as ComponentDefInternal<T>); addComponentLogic(
directiveDefIdx, directive, directiveDef as ComponentDefInternal<T>, hostNode);
} }
if (firstTemplatePass) { if (firstTemplatePass) {
@ -1711,8 +1719,9 @@ export function directiveCreate<T>(
if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx, directiveDef.hostVars); if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx, directiveDef.hostVars);
} }
if (tNode && tNode.attrs) { ngDevMode && assertDefined(previousOrParentTNode, 'previousOrParentTNode');
setInputsFromAttrs(directiveDefIdx, instance, directiveDef.inputs, tNode); if (previousOrParentTNode && previousOrParentTNode.attrs) {
setInputsFromAttrs(directiveDefIdx, instance, directiveDef.inputs, previousOrParentTNode);
} }
if (directiveDef.contentQueries) { if (directiveDef.contentQueries) {
@ -1723,24 +1732,24 @@ export function directiveCreate<T>(
} }
function addComponentLogic<T>( function addComponentLogic<T>(
directiveIndex: number, instance: T, def: ComponentDefInternal<T>): void { directiveIndex: number, instance: T, def: ComponentDefInternal<T>, hostNode: LNode): void {
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);
// Only component views should be added to the view tree directly. Embedded views are // Only component views should be added to the view tree directly. Embedded views are
// accessed through their containers because they may be removed / re-added later. // accessed through their containers because they may be removed / re-added later.
const componentView = addToViewTree( const componentView = addToViewTree(
viewData, previousOrParentNode.tNode.index as number, viewData, previousOrParentTNode.index as number,
createLViewData( createLViewData(
rendererFactory.createRenderer(previousOrParentNode.native as RElement, def), tView, rendererFactory.createRenderer(hostNode.native as RElement, def), tView, instance,
instance, 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).
(previousOrParentNode as{data: LViewData}).data = componentView; (hostNode as{data: LViewData}).data = componentView;
(componentView as LViewData)[HOST_NODE] = previousOrParentNode as LElementNode; (componentView as LViewData)[HOST_NODE] = hostNode as LElementNode;
initChangeDetectorIfExisting(previousOrParentNode.nodeInjector, instance, componentView); initChangeDetectorIfExisting(hostNode.nodeInjector, instance, componentView);
if (firstTemplatePass) queueComponentIndexForCheck(); if (firstTemplatePass) queueComponentIndexForCheck();
} }
@ -1752,8 +1761,8 @@ function addComponentLogic<T>(
* current Angular. Example: local refs and inputs on root component. * current Angular. Example: local refs and inputs on root component.
*/ */
export function baseDirectiveCreate<T>( export function baseDirectiveCreate<T>(
index: number, directive: T, index: number, directive: T, directiveDef: DirectiveDefInternal<T>| ComponentDefInternal<T>,
directiveDef: DirectiveDefInternal<T>| ComponentDefInternal<T>): T { hostNode: LNode): T {
ngDevMode && assertEqual( ngDevMode && assertEqual(
viewData[BINDING_INDEX], tView.bindingStartIndex, viewData[BINDING_INDEX], tView.bindingStartIndex,
'directives should be created before any bindings'); 'directives should be created before any bindings');
@ -1767,28 +1776,27 @@ export function baseDirectiveCreate<T>(
directives[index] = directive; directives[index] = directive;
if (firstTemplatePass) { if (firstTemplatePass) {
const flags = previousOrParentNode.tNode.flags; const flags = previousOrParentTNode.flags;
if ((flags & TNodeFlags.DirectiveCountMask) === 0) { if ((flags & TNodeFlags.DirectiveCountMask) === 0) {
// When the first directive is created: // When the first directive is created:
// - save the index, // - save the index,
// - set the number of directives to 1 // - set the number of directives to 1
previousOrParentNode.tNode.flags = previousOrParentTNode.flags =
index << TNodeFlags.DirectiveStartingIndexShift | flags & TNodeFlags.isComponent | 1; index << TNodeFlags.DirectiveStartingIndexShift | flags & TNodeFlags.isComponent | 1;
} else { } else {
// Only need to bump the size when subsequent directives are created // Only need to bump the size when subsequent directives are created
ngDevMode && assertNotEqual( ngDevMode && assertNotEqual(
flags & TNodeFlags.DirectiveCountMask, TNodeFlags.DirectiveCountMask, flags & TNodeFlags.DirectiveCountMask, TNodeFlags.DirectiveCountMask,
'Reached the max number of directives'); 'Reached the max number of directives');
previousOrParentNode.tNode.flags++; previousOrParentTNode.flags++;
} }
} else { } else {
const diPublic = directiveDef !.diPublic; const diPublic = directiveDef !.diPublic;
if (diPublic) diPublic(directiveDef !); if (diPublic) diPublic(directiveDef !);
} }
if (directiveDef !.attributes != null && previousOrParentNode.tNode.type == TNodeType.Element) { if (directiveDef !.attributes != null && previousOrParentTNode.type == TNodeType.Element) {
setUpAttributes( setUpAttributes((hostNode as LElementNode).native, directiveDef !.attributes as string[]);
(previousOrParentNode as LElementNode).native, directiveDef !.attributes as string[]);
} }
return directive; return directive;
@ -1949,7 +1957,8 @@ function containerInternal(
viewData[BINDING_INDEX], tView.bindingStartIndex, viewData[BINDING_INDEX], tView.bindingStartIndex,
'container nodes should be created before any bindings'); 'container nodes should be created before any bindings');
const currentParent = isParent ? previousOrParentNode : getParentLNode(previousOrParentNode) !; const previousNode: LNode = getPreviousOrParentNode();
const currentParent = isParent ? previousNode : getParentLNode(previousNode) !;
const lContainer = createLContainer(currentParent, viewData); const lContainer = createLContainer(currentParent, viewData);
ngDevMode && ngDevMode.rendererCreateComment++; ngDevMode && ngDevMode.rendererCreateComment++;
@ -1966,7 +1975,7 @@ function containerInternal(
lContainer[QUERIES] = currentQueries.container(); lContainer[QUERIES] = currentQueries.container();
} }
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container); ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
return node; return node;
} }
@ -1976,10 +1985,12 @@ function containerInternal(
* @param index The index of the container in the data array * @param index The index of the container in the data array
*/ */
export function containerRefreshStart(index: number): void { export function containerRefreshStart(index: number): void {
previousOrParentNode = loadElement(index) as LNode; previousOrParentTNode = loadInternal(index, tView.data) as TNode;
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container);
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
isParent = true; isParent = true;
(previousOrParentNode as LContainerNode).data[ACTIVE_INDEX] = 0; // Inline containers cannot have style bindings, so we can read the value directly
(viewData[previousOrParentTNode.index] as LContainerNode).data[ACTIVE_INDEX] = 0;
if (!checkNoChangesMode) { if (!checkNoChangesMode) {
// We need to execute init hooks here so ngOnInit hooks are called in top level views // We need to execute init hooks here so ngOnInit hooks are called in top level views
@ -1997,13 +2008,14 @@ export function containerRefreshEnd(): void {
if (isParent) { if (isParent) {
isParent = false; isParent = false;
} else { } else {
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.View); ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.View);
ngDevMode && assertHasParent(); ngDevMode && assertHasParent();
previousOrParentNode = getParentLNode(previousOrParentNode) !; previousOrParentTNode = previousOrParentTNode.parent !;
} }
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container);
const container = previousOrParentNode as LContainerNode; // Inline containers cannot have style bindings, so we can read the value directly
ngDevMode && assertNodeType(container, TNodeType.Container); const container = viewData[previousOrParentTNode.index];
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
const nextIndex = container.data[ACTIVE_INDEX] !; const nextIndex = container.data[ACTIVE_INDEX] !;
// remove extra views at the end of the container // remove extra views at the end of the container
@ -2072,29 +2084,34 @@ function scanForView(
* @return boolean Whether or not this view is in creation mode * @return boolean Whether or not this view is in creation mode
*/ */
export function embeddedViewStart(viewBlockId: number, consts: number, vars: number): RenderFlags { export function embeddedViewStart(viewBlockId: number, consts: number, vars: number): RenderFlags {
const container = // The previous node can be a view node if we are processing an inline for loop
(isParent ? previousOrParentNode : getParentLNode(previousOrParentNode)) as LContainerNode; const containerTNode = previousOrParentTNode.type === TNodeType.View ?
ngDevMode && assertNodeType(container, TNodeType.Container); previousOrParentTNode.parent ! :
previousOrParentTNode;
// Inline containers cannot have style bindings, so we can read the value directly
const container = viewData[containerTNode.index] as LContainerNode;
ngDevMode && assertNodeType(containerTNode, TNodeType.Container);
const lContainer = container.data; const lContainer = container.data;
let viewNode: LViewNode|null = scanForView(container, lContainer[ACTIVE_INDEX] !, viewBlockId); let viewNode: LViewNode|null = scanForView(container, lContainer[ACTIVE_INDEX] !, viewBlockId);
if (viewNode) { if (viewNode) {
previousOrParentNode = viewNode; const embeddedView = viewNode.data;
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.View);
isParent = true; isParent = true;
enterView(viewNode.data, viewNode); enterView(embeddedView, embeddedView[TVIEW].node);
} else { } else {
// When we create a new LView, we always reset the state of the instructions. // When we create a new LView, we always reset the state of the instructions.
const newView = createLViewData( const newView = createLViewData(
renderer, getOrCreateEmbeddedTView(viewBlockId, consts, vars, container), null, renderer,
getOrCreateEmbeddedTView(viewBlockId, consts, vars, containerTNode as TContainerNode), null,
LViewFlags.CheckAlways, getCurrentSanitizer()); LViewFlags.CheckAlways, getCurrentSanitizer());
if (lContainer[QUERIES]) { if (lContainer[QUERIES]) {
newView[QUERIES] = lContainer[QUERIES] !.createView(); newView[QUERIES] = lContainer[QUERIES] !.createView();
} }
enterView( viewNode = createLNode(viewBlockId, TNodeType.View, null, null, null, newView);
newView, viewNode = createLNode(viewBlockId, TNodeType.View, null, null, null, newView)); enterView(newView, newView[TVIEW].node);
} }
if (container) { if (container) {
if (creationMode) { if (creationMode) {
@ -2116,13 +2133,13 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
* @param viewIndex The index of the TView in TNode.tViews * @param viewIndex The index of the TView in TNode.tViews
* @param consts The number of nodes, local refs, and pipes in this template * @param consts The number of nodes, local refs, and pipes in this template
* @param vars The number of bindings and pure function bindings in this template * @param vars The number of bindings and pure function bindings in this template
* @param parent The parent container in which to look for the view's static data * @param container The parent container in which to look for the view's static data
* @returns TView * @returns TView
*/ */
function getOrCreateEmbeddedTView( function getOrCreateEmbeddedTView(
viewIndex: number, consts: number, vars: number, parent: LContainerNode): TView { viewIndex: number, consts: number, vars: number, parent: TContainerNode): TView {
ngDevMode && assertNodeType(parent, TNodeType.Container); ngDevMode && assertNodeType(parent, TNodeType.Container);
const containerTViews = (parent !.tNode as TContainerNode).tViews as TView[]; const containerTViews = parent.tViews as TView[];
ngDevMode && assertDefined(containerTViews, 'TView expected'); ngDevMode && assertDefined(containerTViews, 'TView expected');
ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array'); ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array');
if (viewIndex >= containerTViews.length || containerTViews[viewIndex] == null) { if (viewIndex >= containerTViews.length || containerTViews[viewIndex] == null) {
@ -2134,12 +2151,11 @@ function getOrCreateEmbeddedTView(
/** Marks the end of an embedded view. */ /** Marks the end of an embedded view. */
export function embeddedViewEnd(): void { export function embeddedViewEnd(): void {
const viewHost = tView.node;
refreshDescendantViews(); refreshDescendantViews();
isParent = false;
previousOrParentNode = viewData[HOST_NODE] as LViewNode;
leaveView(viewData[PARENT] !); leaveView(viewData[PARENT] !);
ngDevMode && assertEqual(isParent, false, 'isParent'); previousOrParentTNode = viewHost !;
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.View); isParent = false;
} }
///////////// /////////////
@ -2151,8 +2167,8 @@ export function embeddedViewEnd(): void {
*/ */
export function componentRefresh<T>(adjustedElementIndex: number): void { export function componentRefresh<T>(adjustedElementIndex: number): void {
ngDevMode && assertDataInRange(adjustedElementIndex); ngDevMode && assertDataInRange(adjustedElementIndex);
const element = viewData[adjustedElementIndex] as LElementNode; const element = readElementValue(viewData[adjustedElementIndex]) as LElementNode;
ngDevMode && assertNodeType(element, TNodeType.Element); ngDevMode && assertNodeType(tView.data[adjustedElementIndex] as TNode, TNodeType.Element);
ngDevMode && ngDevMode &&
assertDefined(element.data, `Component's host node should have an LViewData attached.`); assertDefined(element.data, `Component's host node should have an LViewData attached.`);
const hostView = element.data !; const hostView = element.data !;
@ -2414,10 +2430,7 @@ export function tick<T>(component: T): void {
function tickRootContext(rootContext: RootContext) { function tickRootContext(rootContext: RootContext) {
for (let i = 0; i < rootContext.components.length; i++) { for (let i = 0; i < rootContext.components.length; i++) {
const rootComponent = rootContext.components[i]; const rootComponent = rootContext.components[i];
const hostNode = _getComponentHostLElementNode(rootComponent, true); renderComponentOrTemplate(getRootView(rootComponent), rootComponent);
ngDevMode && assertDefined(hostNode.data, 'Component host node should be attached to an LView');
renderComponentOrTemplate(hostNode, getRootView(rootComponent), rootComponent);
} }
} }
@ -2505,8 +2518,8 @@ export function checkNoChangesInRootView(lViewData: LViewData): void {
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */ /** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
export function detectChangesInternal<T>( export function detectChangesInternal<T>(
hostView: LViewData, hostNode: LElementNode, component: T) { hostView: LViewData, hostNode: LElementNode, component: T) {
const oldView = enterView(hostView, hostNode);
const hostTView = hostView[TVIEW]; const hostTView = hostView[TVIEW];
const oldView = enterView(hostView, null);
const templateFn = hostTView.template !; const templateFn = hostTView.template !;
const viewQuery = hostTView.viewQuery; const viewQuery = hostTView.viewQuery;
@ -2845,11 +2858,11 @@ export function registerContentQuery<Q>(queryList: QueryList<Q>): void {
} }
export function assertPreviousIsParent() { export function assertPreviousIsParent() {
assertEqual(isParent, true, 'previousOrParentNode should be a parent'); assertEqual(isParent, true, 'previousOrParentTNode should be a parent');
} }
function assertHasParent() { function assertHasParent() {
assertDefined(getParentLNode(previousOrParentNode), 'previousOrParentNode should have a parent'); assertDefined(previousOrParentTNode.parent, 'previousOrParentTNode should have a parent');
} }
function assertDataInRange(index: number, arr?: any[]) { function assertDataInRange(index: number, arr?: any[]) {

View File

@ -12,7 +12,7 @@ import {Sanitizer} from '../../sanitization/security';
import {LContainer} from './container'; import {LContainer} from './container';
import {ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefList, PipeDefInternal, PipeDefList} from './definition'; import {ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefList, PipeDefInternal, PipeDefList} from './definition';
import {LElementNode, LViewNode, TNode} from './node'; import {LElementNode, LViewNode, TElementNode, TNode, TViewNode} from './node';
import {LQueries} from './query'; import {LQueries} from './query';
import {Renderer3} from './renderer'; import {Renderer3} from './renderer';
@ -277,9 +277,15 @@ export interface TView {
* We need this pointer to be able to efficiently find this node when inserting the view * We need this pointer to be able to efficiently find this node when inserting the view
* into an anchor. * into an anchor.
* *
* If this is a `TNode` for an `LElementNode`, this is the TView of a component. * If this is a `TElementNode`, this is the view of a root component. It has exactly one
* root TNode.
*
* If this is null, this is the view of a component that is not at root. We do not store
* the host TNodes for child component views because they can potentially have several
* different host TNodes, depending on where the component is being used. These host
* TNodes cannot be shared (due to different indices, etc).
*/ */
node: TNode; node: TViewNode|TElementNode|null;
/** Whether or not this template has been processed. */ /** Whether or not this template has been processed. */
firstTemplatePass: boolean; firstTemplatePass: boolean;

View File

@ -7,19 +7,19 @@
*/ */
import {assertDefined, assertEqual} from './assert'; import {assertDefined, assertEqual} from './assert';
import {LNode, TNodeType} from './interfaces/node'; import {LNode, TNode, TNodeType} from './interfaces/node';
export function assertNodeType(node: LNode, type: TNodeType) { export function assertNodeType(tNode: TNode, type: TNodeType) {
assertDefined(node, 'should be called with a node'); assertDefined(tNode, 'should be called with a TNode');
assertEqual(node.tNode.type, type, `should be a ${typeName(type)}`); assertEqual(tNode.type, type, `should be a ${typeName(type)}`);
} }
export function assertNodeOfPossibleTypes(node: LNode, ...types: TNodeType[]) { export function assertNodeOfPossibleTypes(tNode: TNode, ...types: TNodeType[]) {
assertDefined(node, 'should be called with a node'); assertDefined(tNode, 'should be called with a TNode');
const found = types.some(type => node.tNode.type === type); const found = types.some(type => tNode.type === type);
assertEqual( assertEqual(
found, true, found, true,
`Should be one of ${types.map(typeName).join(', ')} but got ${typeName(node.tNode.type)}`); `Should be one of ${types.map(typeName).join(', ')} but got ${typeName(tNode.type)}`);
} }
function typeName(type: TNodeType): string { function typeName(type: TNodeType): string {

View File

@ -13,7 +13,7 @@ import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unuse
import {LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
import {CLEANUP, CONTAINER_INDEX, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {CLEANUP, CONTAINER_INDEX, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {readElementValue, stringify} from './util'; import {readElementValue, stringify} from './util';
@ -194,7 +194,7 @@ export function findComponentHost(lViewData: LViewData): LElementNode {
viewRootLNode = lViewData[HOST_NODE]; viewRootLNode = lViewData[HOST_NODE];
} }
ngDevMode && assertNodeType(viewRootLNode, TNodeType.Element); ngDevMode && assertNodeType(viewRootLNode.tNode, TNodeType.Element);
ngDevMode && assertDefined(viewRootLNode.data, 'node.data'); ngDevMode && assertDefined(viewRootLNode.data, 'node.data');
return viewRootLNode as LElementNode; return viewRootLNode as LElementNode;
@ -246,8 +246,8 @@ export function addRemoveViewFromContainer(
export function addRemoveViewFromContainer( export function addRemoveViewFromContainer(
container: LContainerNode, rootNode: LViewNode, insertMode: boolean, container: LContainerNode, rootNode: LViewNode, insertMode: boolean,
beforeNode?: RNode | null): void { beforeNode?: RNode | null): void {
ngDevMode && assertNodeType(container, TNodeType.Container); ngDevMode && assertNodeType(container.tNode, TNodeType.Container);
ngDevMode && assertNodeType(rootNode, TNodeType.View); ngDevMode && assertNodeType(rootNode.tNode, TNodeType.View);
const parentNode = container.data[RENDER_PARENT]; const parentNode = container.data[RENDER_PARENT];
const parent = parentNode ? parentNode.native : null; const parent = parentNode ? parentNode.native : null;
if (parent) { if (parent) {
@ -544,7 +544,7 @@ function canInsertNativeChildOfElement(parent: LElementNode, currentView: LViewD
* the container itself has it render parent determined. * the container itself has it render parent determined.
*/ */
function canInsertNativeChildOfView(parent: LViewNode): boolean { function canInsertNativeChildOfView(parent: LViewNode): boolean {
ngDevMode && assertNodeType(parent, TNodeType.View); ngDevMode && assertNodeType(parent.tNode, TNodeType.View);
// Because we are inserting into a `View` the `View` may be disconnected. // Because we are inserting into a `View` the `View` may be disconnected.
const grandParentContainer = getParentLNode(parent) as LContainerNode; const grandParentContainer = getParentLNode(parent) as LContainerNode;
@ -552,7 +552,7 @@ function canInsertNativeChildOfView(parent: LViewNode): boolean {
// The `View` is not inserted into a `Container` we have to delay insertion. // The `View` is not inserted into a `Container` we have to delay insertion.
return false; return false;
} }
ngDevMode && assertNodeType(grandParentContainer, TNodeType.Container); ngDevMode && assertNodeType(grandParentContainer.tNode, TNodeType.Container);
if (grandParentContainer.data[RENDER_PARENT] == null) { if (grandParentContainer.data[RENDER_PARENT] == null) {
// The parent `Container` itself is disconnected. So we have to delay. // The parent `Container` itself is disconnected. So we have to delay.
return false; return false;
@ -584,7 +584,7 @@ function canInsertNativeChildOfView(parent: LViewNode): boolean {
export function canInsertNativeNode(parent: LNode, currentView: LViewData): boolean { export function canInsertNativeNode(parent: LNode, currentView: LViewData): boolean {
// We can only insert into a Component or View. Any other type should be an Error. // We can only insert into a Component or View. Any other type should be an Error.
ngDevMode && assertNodeOfPossibleTypes( ngDevMode && assertNodeOfPossibleTypes(
parent, TNodeType.Element, TNodeType.ElementContainer, TNodeType.View); parent.tNode, TNodeType.Element, TNodeType.ElementContainer, TNodeType.View);
if (parent.tNode.type === TNodeType.Element) { if (parent.tNode.type === TNodeType.Element) {
// Parent is a regular element or a component // Parent is a regular element or a component

View File

@ -8,7 +8,7 @@
import {devModeEqual} from '../change_detection/change_detection_util'; import {devModeEqual} from '../change_detection/change_detection_util';
import {assertLessThan} from './assert'; import {assertLessThan} from './assert';
import {LElementNode} from './interfaces/node'; import {LElementNode} from './interfaces/node';
import {HEADER_OFFSET, LViewData} from './interfaces/view'; import {HEADER_OFFSET, LViewData, TData} from './interfaces/view';
/** /**
* Returns wether the values are different from a change detection stand point. * Returns wether the values are different from a change detection stand point.
@ -66,8 +66,8 @@ export function flatten(list: any[]): any[] {
return result; return result;
} }
/** Retrieves a value from any `LViewData`. */ /** Retrieves a value from any `LViewData` or `TData`. */
export function loadInternal<T>(index: number, arr: LViewData): T { export function loadInternal<T>(index: number, arr: LViewData | TData): T {
ngDevMode && assertDataInRangeInternal(index + HEADER_OFFSET, arr); ngDevMode && assertDataInRangeInternal(index + HEADER_OFFSET, arr);
return arr[index + HEADER_OFFSET]; return arr[index + HEADER_OFFSET];
} }

View File

@ -1721,6 +1721,9 @@
{ {
"name": "getPreviousOrParentNode" "name": "getPreviousOrParentNode"
}, },
{
"name": "getPreviousOrParentTNode"
},
{ {
"name": "getPromiseCtor" "name": "getPromiseCtor"
}, },

View File

@ -1501,7 +1501,7 @@ describe('di', () => {
it('should handle initial undefined state', () => { it('should handle initial undefined state', () => {
const contentView = createLViewData( const contentView = createLViewData(
null !, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways); null !, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways);
const oldView = enterView(contentView, null !); const oldView = enterView(contentView, null);
try { try {
const parent = createLNode(0, TNodeType.Element, null, null, null, null); const parent = createLNode(0, TNodeType.Element, null, null, null, null);