refactor(ivy): add tNodes for view nodes and hosts (#24113)

PR Close #24113
This commit is contained in:
Kara Erickson 2018-05-16 05:56:01 -07:00 committed by Matias Niemelä
parent 13cb75da8b
commit 8216657681
10 changed files with 92 additions and 51 deletions

View File

@ -187,7 +187,7 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
// Root component is always created at dir index 0 // Root component is always created at dir index 0
queueInitHooks(0, def.onInit, def.doCheck, elementNode.view.tView); queueInitHooks(0, def.onInit, def.doCheck, elementNode.view.tView);
queueLifecycleHooks(elementNode.tNode !.flags, elementNode.view); queueLifecycleHooks(elementNode.tNode.flags, elementNode.view);
} }
/** /**

View File

@ -19,7 +19,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie
import {Type} from '../type'; import {Type} from '../type';
import {assertGreaterThan, assertLessThan, assertNotNull} from './assert'; import {assertGreaterThan, assertLessThan, assertNotNull} from './assert';
import {addToViewTree, assertPreviousIsParent, createLContainer, createLNodeObject, createTView, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions'; import {addToViewTree, assertPreviousIsParent, createLContainer, createLNodeObject, createTNode, createTView, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList} from './interfaces/definition'; import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList} from './interfaces/definition';
import {LInjector} from './interfaces/injector'; import {LInjector} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, LNodeType, LViewNode, TNodeFlags} from './interfaces/node'; import {LContainerNode, LElementNode, LNode, LNodeType, LViewNode, TNodeFlags} from './interfaces/node';
@ -254,7 +254,7 @@ export function injectAttribute(attrName: string): string|undefined {
ngDevMode && assertPreviousIsParent(); ngDevMode && assertPreviousIsParent();
const lElement = getPreviousOrParentNode() as LElementNode; const lElement = getPreviousOrParentNode() as LElementNode;
ngDevMode && assertNodeType(lElement, LNodeType.Element); ngDevMode && assertNodeType(lElement, LNodeType.Element);
const tElement = lElement.tNode !; const tElement = lElement.tNode;
ngDevMode && assertNotNull(tElement, 'expecting tNode'); ngDevMode && assertNotNull(tElement, 'expecting tNode');
const attrs = tElement.attrs; const attrs = tElement.attrs;
if (attrs) { if (attrs) {
@ -278,7 +278,7 @@ export function getOrCreateChangeDetectorRef(
if (di.changeDetectorRef) return di.changeDetectorRef; if (di.changeDetectorRef) return di.changeDetectorRef;
const currentNode = di.node; const currentNode = di.node;
if (isComponent(currentNode.tNode !)) { if (isComponent(currentNode.tNode)) {
return di.changeDetectorRef = createViewRef(currentNode.data as LView, context); return di.changeDetectorRef = createViewRef(currentNode.data as LView, context);
} else if (currentNode.type === LNodeType.Element) { } else if (currentNode.type === LNodeType.Element) {
return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node); return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node);
@ -298,7 +298,7 @@ function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode):
createViewRef( createViewRef(
hostNode.data as LView, hostNode.data as LView,
hostNode.view hostNode.view
.directives ![hostNode.tNode !.flags >> TNodeFlags.DirectiveStartingIndexShift]); .directives ![hostNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift]);
} }
/** /**
@ -361,7 +361,7 @@ export function getOrCreateInjectable<T>(
// At this point, we have an injector which *may* contain the token, so we step through the // At this point, we have an injector which *may* contain the token, so we step through the
// directives associated with the injector's corresponding node to get the directive instance. // directives associated with the injector's corresponding node to get the directive instance.
const node = injector.node; const node = injector.node;
const nodeFlags = node.tNode !.flags; const nodeFlags = node.tNode.flags;
const count = nodeFlags & TNodeFlags.DirectiveCountMask; const count = nodeFlags & TNodeFlags.DirectiveCountMask;
if (count !== 0) { if (count !== 0) {
@ -572,8 +572,12 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainer
const lContainerNode: LContainerNode = createLNodeObject( const lContainerNode: LContainerNode = createLNodeObject(
LNodeType.Container, vcRefHost.view, vcRefHost.parent !, undefined, lContainer, null); LNodeType.Container, vcRefHost.view, vcRefHost.parent !, undefined, lContainer, null);
// TODO(kara): Separate into own TNode when moving parent/child properties const hostTNode = vcRefHost.tNode;
lContainerNode.tNode = vcRefHost.tNode; if (!hostTNode.dynamicContainerNode) {
hostTNode.dynamicContainerNode = createTNode(hostTNode.index, null, null, null);
}
lContainerNode.tNode = hostTNode.dynamicContainerNode;
vcRefHost.dynamicLContainerNode = lContainerNode; vcRefHost.dynamicLContainerNode = lContainerNode;
addToViewTree(vcRefHost.view, lContainer); addToViewTree(vcRefHost.view, lContainer);
@ -699,7 +703,7 @@ export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef
if (!di.templateRef) { if (!di.templateRef) {
ngDevMode && assertNodeType(di.node, LNodeType.Container); ngDevMode && assertNodeType(di.node, LNodeType.Container);
const hostNode = di.node as LContainerNode; const hostNode = di.node as LContainerNode;
const hostTNode = hostNode.tNode !; const hostTNode = hostNode.tNode;
const hostTView = hostNode.view.tView; const hostTView = hostNode.view.tView;
if (!hostTNode.tViews) { if (!hostTNode.tViews) {
hostTNode.tViews = createTView(hostTView.directiveRegistry, hostTView.pipeRegistry); hostTNode.tViews = createTView(hostTView.directiveRegistry, hostTView.pipeRegistry);

View File

@ -349,7 +349,7 @@ export function createLNodeObject(
nodeInjector: parent ? parent.nodeInjector : null, nodeInjector: parent ? parent.nodeInjector : null,
data: state, data: state,
queries: queries, queries: queries,
tNode: null, tNode: null !,
pNextOrParent: null, pNextOrParent: null,
dynamicLContainerNode: null dynamicLContainerNode: null
}; };
@ -371,7 +371,7 @@ export function createLNode(
index: number | null, type: LNodeType.Element, native: RElement | RText | null, index: number | null, type: LNodeType.Element, native: RElement | RText | null,
name: string | null, attrs: string[] | null, lView?: LView | null): LElementNode; name: string | null, attrs: string[] | null, lView?: LView | null): LElementNode;
export function createLNode( export function createLNode(
index: null, type: LNodeType.View, native: null, name: null, attrs: null, index: number | null, type: LNodeType.View, native: null, name: null, attrs: null,
lView: LView): LViewNode; lView: LView): LViewNode;
export function createLNode( export function createLNode(
index: number, type: LNodeType.Container, native: undefined, name: string | null, index: number, type: LNodeType.Container, native: undefined, name: string | null,
@ -392,14 +392,12 @@ export function createLNode(
const node = const node =
createLNodeObject(type, currentView, parent, native, isState ? state as any : null, queries); createLNodeObject(type, currentView, parent, native, isState ? state as any : null, queries);
if ((type & LNodeType.ViewOrElement) === LNodeType.ViewOrElement && isState) { if (index === null || type === LNodeType.View) {
// Bit of a hack to bust through the readonly because there is a circular dep between // View nodes are not stored in data because they can be added / removed at runtime (which
// LView and LNode. // would cause indices to change). Their TNodes are instead stored in TView.node.
ngDevMode && assertNull((state as LView).node, 'LView.node should not have been initialized'); node.tNode = (state as LView).tView.node || createTNode(index, null, null, null);
(state as LView as{node: LNode}).node = node; } else {
} // This is an element or container or projection node
if (index != null) {
// We are Element or Container
ngDevMode && assertDataNext(index); ngDevMode && assertDataNext(index);
data[index] = node; data[index] = node;
@ -407,7 +405,9 @@ export function createLNode(
if (index >= tData.length) { if (index >= tData.length) {
const tNode = tData[index] = createTNode(index, name, attrs, null); const tNode = tData[index] = createTNode(index, name, attrs, null);
if (!isParent && previousOrParentNode) { if (!isParent && previousOrParentNode) {
previousOrParentNode.tNode !.next = tNode; const previousTNode = previousOrParentNode.tNode;
previousTNode.next = tNode;
if (previousTNode.dynamicContainerNode) previousTNode.dynamicContainerNode.next = tNode;
} }
} }
node.tNode = tData[index] as TNode; node.tNode = tData[index] as TNode;
@ -427,6 +427,16 @@ export function createLNode(
} }
} }
} }
// View nodes and host elements need to set their host node (components set host nodes later)
if ((type & LNodeType.ViewOrElement) === LNodeType.ViewOrElement && isState) {
// Bit of a hack to bust through the readonly because there is a circular dep between
// LView and LNode.
ngDevMode && assertNull((state as LView).node, 'LView.node should not have been initialized');
(state as{node: LNode}).node = node;
if (firstTemplatePass) (state as LView).tView.node = node.tNode;
}
previousOrParentNode = node; previousOrParentNode = node;
isParent = true; isParent = true;
return node; return node;
@ -613,7 +623,7 @@ function createDirectivesAndLocals(
const node = previousOrParentNode; const node = previousOrParentNode;
if (firstTemplatePass) { if (firstTemplatePass) {
ngDevMode && ngDevMode.firstTemplatePass++; ngDevMode && ngDevMode.firstTemplatePass++;
cacheMatchingDirectivesForNode(node.tNode !, currentView.tView, localRefs || null); cacheMatchingDirectivesForNode(node.tNode, currentView.tView, localRefs || null);
} else { } else {
instantiateDirectivesDirectly(); instantiateDirectivesDirectly();
} }
@ -708,7 +718,7 @@ export function isComponent(tNode: TNode): boolean {
* This function instantiates the given directives. * This function instantiates the given directives.
*/ */
function instantiateDirectivesDirectly() { function instantiateDirectivesDirectly() {
const tNode = previousOrParentNode.tNode !; const tNode = previousOrParentNode.tNode;
const count = tNode.flags & TNodeFlags.DirectiveCountMask; const count = tNode.flags & TNodeFlags.DirectiveCountMask;
if (count > 0) { if (count > 0) {
@ -758,7 +768,7 @@ function saveNameToExportMap(
* to data[] in the same order as they are loaded in the template with load(). * to data[] in the same order as they are loaded in the template with load().
*/ */
function saveResolvedLocalsInData(): void { function saveResolvedLocalsInData(): void {
const localNames = previousOrParentNode.tNode !.localNames; const localNames = previousOrParentNode.tNode.localNames;
if (localNames) { if (localNames) {
for (let i = 0; i < localNames.length; i += 2) { for (let i = 0; i < localNames.length; i += 2) {
const index = localNames[i + 1] as number; const index = localNames[i + 1] as number;
@ -796,6 +806,7 @@ export function createTView(
defs: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null): TView { defs: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null): TView {
ngDevMode && ngDevMode.tView++; ngDevMode && ngDevMode.tView++;
return { return {
node: null !,
data: [], data: [],
directives: null, directives: null,
firstTemplatePass: true, firstTemplatePass: true,
@ -879,7 +890,7 @@ export function hostElement(
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer)); def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer));
if (firstTemplatePass) { if (firstTemplatePass) {
node.tNode !.flags = TNodeFlags.isComponent; node.tNode.flags = TNodeFlags.isComponent;
if (def.diPublic) def.diPublic(def); if (def.diPublic) def.diPublic(def);
currentView.tView.directives = [def]; currentView.tView.directives = [def];
} }
@ -918,11 +929,11 @@ export function listener(
cleanupFns.push(eventName, native, wrappedListener, useCapture); cleanupFns.push(eventName, native, wrappedListener, useCapture);
} }
let tNode: TNode|null = node.tNode !; 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(node.tNode.flags, BindingDirection.Output);
} }
const outputs = tNode.outputs; const outputs = tNode.outputs;
@ -955,7 +966,7 @@ export function elementEnd() {
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Element); ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Element);
const queries = previousOrParentNode.queries; const queries = previousOrParentNode.queries;
queries && queries.addNode(previousOrParentNode); queries && queries.addNode(previousOrParentNode);
queueLifecycleHooks(previousOrParentNode.tNode !.flags, currentView); queueLifecycleHooks(previousOrParentNode.tNode.flags, currentView);
} }
/** /**
@ -1002,12 +1013,12 @@ export function elementProperty<T>(
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void { index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void {
if (value === NO_CHANGE) return; if (value === NO_CHANGE) return;
const node = data[index] as LElementNode; const node = data[index] as LElementNode;
const tNode = node.tNode !; const tNode = node.tNode;
// if tNode.inputs is undefined, a listener has created outputs, but inputs haven't // if tNode.inputs is undefined, a listener has created outputs, but inputs haven't
// yet been checked // yet been checked
if (tNode && tNode.inputs === undefined) { if (tNode && tNode.inputs === undefined) {
// mark inputs as checked // mark inputs as checked
tNode.inputs = generatePropertyAliases(node.tNode !.flags, BindingDirection.Input); tNode.inputs = generatePropertyAliases(node.tNode.flags, BindingDirection.Input);
} }
const inputData = tNode && tNode.inputs; const inputData = tNode && tNode.inputs;
@ -1036,8 +1047,9 @@ export function elementProperty<T>(
* @param tViews Any TViews attached to this node * @param tViews Any TViews attached to this node
* @returns the TNode object * @returns the TNode object
*/ */
function createTNode( export function createTNode(
index: number, tagName: string | null, attrs: string[] | null, tViews: TView[] | null): TNode { index: number | null, tagName: string | null, attrs: string[] | null,
tViews: TView[] | null): TNode {
ngDevMode && ngDevMode.tNode++; ngDevMode && ngDevMode.tNode++;
return { return {
index: index, index: index,
@ -1049,7 +1061,8 @@ function createTNode(
inputs: undefined, inputs: undefined,
outputs: undefined, outputs: undefined,
tViews: tViews, tViews: tViews,
next: null next: null,
dynamicContainerNode: null
}; };
} }
@ -1321,8 +1334,11 @@ function addComponentLogic<T>(index: number, instance: T, def: ComponentDef<T>):
tView, null, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, tView, null, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways,
getCurrentSanitizer())); getCurrentSanitizer()));
(previousOrParentNode.data as any) = hostView; // We need to set the host node/data here because when the component LNode was created,
(hostView.node as any) = previousOrParentNode; // we didn't yet know it was a component (just an element).
(previousOrParentNode as{data: LView}).data = hostView;
(hostView as{node: LNode}).node = previousOrParentNode;
if (firstTemplatePass) tView.node = previousOrParentNode.tNode;
initChangeDetectorIfExisting(previousOrParentNode.nodeInjector, instance, hostView); initChangeDetectorIfExisting(previousOrParentNode.nodeInjector, instance, hostView);
@ -1351,19 +1367,19 @@ export function baseDirectiveCreate<T>(
directives[index] = directive; directives[index] = directive;
if (firstTemplatePass) { if (firstTemplatePass) {
const flags = previousOrParentNode.tNode !.flags; const flags = previousOrParentNode.tNode.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 = previousOrParentNode.tNode.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++; previousOrParentNode.tNode.flags++;
} }
} else { } else {
const diPublic = directiveDef !.diPublic; const diPublic = directiveDef !.diPublic;
@ -1481,7 +1497,7 @@ export function container(
const node = createLNode( const node = createLNode(
index, LNodeType.Container, undefined, tagName || null, attrs || null, lContainer); index, LNodeType.Container, undefined, tagName || null, attrs || null, lContainer);
if (firstTemplatePass && template == null) node.tNode !.tViews = []; if (firstTemplatePass && template == null) node.tNode.tViews = [];
// Containers are added to the current view tree instead of their embedded views // Containers are added to the current view tree instead of their embedded views
// because views can be removed and re-inserted. // because views can be removed and re-inserted.
@ -1618,7 +1634,8 @@ export function embeddedViewStart(viewBlockId: number): RenderFlags {
newView.queries = lContainer.queries.enterView(lContainer.nextIndex); newView.queries = lContainer.queries.enterView(lContainer.nextIndex);
} }
enterView(newView, viewNode = createLNode(null, LNodeType.View, null, null, null, newView)); enterView(
newView, viewNode = createLNode(viewBlockId, LNodeType.View, null, null, null, newView));
} }
return getRenderFlags(viewNode.data); return getRenderFlags(viewNode.data);
} }
@ -2027,7 +2044,7 @@ export function getRootView(component: any): LView {
export function detectChanges<T>(component: T): void { export function detectChanges<T>(component: T): void {
const hostNode = _getComponentHostLElementNode(component); const hostNode = _getComponentHostLElementNode(component);
ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView'); ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView');
const componentIndex = hostNode.tNode !.flags >> TNodeFlags.DirectiveStartingIndexShift; const componentIndex = hostNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
const def = hostNode.view.tView.directives ![componentIndex] as ComponentDef<T>; const def = hostNode.view.tView.directives ![componentIndex] as ComponentDef<T>;
detectChangesInternal(hostNode.data as LView, hostNode, def, component); detectChangesInternal(hostNode.data as LView, hostNode, def, component);
} }

View File

@ -118,11 +118,12 @@ export interface LNode {
* Pointer to the corresponding TNode object, which stores static * Pointer to the corresponding TNode object, which stores static
* data about this node. * data about this node.
*/ */
tNode: TNode|null; tNode: TNode;
/** /**
* A pointer to a LContainerNode created by directives requesting ViewContainerRef * A pointer to a LContainerNode created by directives requesting ViewContainerRef
*/ */
// TODO(kara): Remove when removing LNodes
dynamicLContainerNode: LContainerNode|null; dynamicLContainerNode: LContainerNode|null;
} }
@ -210,8 +211,10 @@ export interface TNode {
* *
* This is necessary to get from any TNode to its corresponding LNode when * This is necessary to get from any TNode to its corresponding LNode when
* traversing the node tree. * traversing the node tree.
*
* If null, this is a view node created from a dynamically created view.
*/ */
index: number; index: number|null;
/** /**
* This number stores two values using its bits: * This number stores two values using its bits:
@ -306,6 +309,11 @@ export interface TNode {
* to insert them or remove them from the DOM. * to insert them or remove them from the DOM.
*/ */
next: TNode|null; next: TNode|null;
/**
* A pointer to a LContainerNode created by directives requesting ViewContainerRef
*/
dynamicContainerNode: TNode|null;
} }
/** Static data for an LElementNode */ /** Static data for an LElementNode */

View File

@ -46,6 +46,7 @@ export interface LView {
* *
* If `LElementNode`, this is the LView of a component. * If `LElementNode`, this is the LView of a component.
*/ */
// TODO(kara): Remove when we have parent/child on TNodes
readonly node: LViewNode|LElementNode; readonly node: LViewNode|LElementNode;
/** /**
@ -241,6 +242,17 @@ export interface LViewOrLContainer {
* Stored on the template function as ngPrivateData. * Stored on the template function as ngPrivateData.
*/ */
export interface TView { export interface TView {
/**
* Pointer to the `TNode` that represents the root of the view.
*
* If this is a `TNode` for an `LViewNode`, this is an embedded view of a container.
* We need this pointer to be able to efficiently find this node when inserting the view
* into an anchor.
*
* If this is a `TNode` for an `LElementNode`, this is the TView of a component.
*/
node: TNode;
/** Whether or not this template has been processed. */ /** Whether or not this template has been processed. */
firstTemplatePass: boolean; firstTemplatePass: boolean;

View File

@ -72,7 +72,7 @@ export function getNextLNode(node: LNode): LNode|null {
const lView = node.data as LView; const lView = node.data as LView;
return lView.next ? (lView.next as LView).node : null; return lView.next ? (lView.next as LView).node : null;
} }
return node.tNode !.next ? node.view.data[node.tNode !.next !.index] : null; return node.tNode.next ? node.view.data[node.tNode.next !.index as number] : null;
} }
/** /**

View File

@ -195,7 +195,7 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
*/ */
function getIdxOfMatchingDirective(node: LNode, type: Type<any>): number|null { function getIdxOfMatchingDirective(node: LNode, type: Type<any>): number|null {
const defs = node.view.tView.directives !; const defs = node.view.tView.directives !;
const flags = node.tNode !.flags; const flags = node.tNode.flags;
const count = flags & TNodeFlags.DirectiveCountMask; const count = flags & TNodeFlags.DirectiveCountMask;
const start = flags >> TNodeFlags.DirectiveStartingIndexShift; const start = flags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count; const end = start + count;
@ -241,8 +241,7 @@ function add(query: LQuery<any>| null, node: LNode) {
} else { } else {
const selector = predicate.selector !; const selector = predicate.selector !;
for (let i = 0; i < selector.length; i++) { for (let i = 0; i < selector.length; i++) {
ngDevMode && assertNotNull(node.tNode, 'node.tNode'); const directiveIdx = getIdxOfMatchingSelector(node.tNode, selector[i]);
const directiveIdx = getIdxOfMatchingSelector(node.tNode !, selector[i]);
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
// note that queries using name selector must specify read strategy // note that queries using name selector must specify read strategy

View File

@ -48,7 +48,7 @@ describe('instructions', () => {
expect(t.html).toEqual('<div title="javascript:true"></div>'); expect(t.html).toEqual('<div title="javascript:true"></div>');
expect(ngDevMode).toHaveProperties({ expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1, firstTemplatePass: 1,
tNode: 1, tNode: 2, // 1 for div, 1 for host element
tView: 1, tView: 1,
rendererCreateElement: 1, rendererCreateElement: 1,
rendererSetAttribute: 2 rendererSetAttribute: 2
@ -69,7 +69,7 @@ describe('instructions', () => {
expect(t.html).toEqual('<div title="javascript:false"></div>'); expect(t.html).toEqual('<div title="javascript:false"></div>');
expect(ngDevMode).toHaveProperties({ expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1, firstTemplatePass: 1,
tNode: 1, tNode: 2, // 1 for div, 1 for host element
tView: 1, tView: 1,
rendererCreateElement: 1, rendererCreateElement: 1,
}); });
@ -83,7 +83,7 @@ describe('instructions', () => {
expect((t.hostNode.native as HTMLElement).querySelector('div') !.hidden).toEqual(false); expect((t.hostNode.native as HTMLElement).querySelector('div') !.hidden).toEqual(false);
expect(ngDevMode).toHaveProperties({ expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1, firstTemplatePass: 1,
tNode: 1, tNode: 2, // 1 for div, 1 for host element
tView: 1, tView: 1,
rendererCreateElement: 1, rendererCreateElement: 1,
rendererSetProperty: 1 rendererSetProperty: 1

View File

@ -32,7 +32,7 @@ describe('render3 integration test', () => {
} }
expect(ngDevMode).toHaveProperties({ expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1, firstTemplatePass: 1,
tNode: 2, tNode: 3, // 1 for div, 1 for text, 1 for host element
tView: 1, tView: 1,
rendererCreateElement: 1, rendererCreateElement: 1,
}); });

View File

@ -19,7 +19,8 @@ function testLStaticData(tagName: string, attrs: string[] | null): TNode {
inputs: undefined, inputs: undefined,
outputs: undefined, outputs: undefined,
tViews: null, tViews: null,
next: null next: null,
dynamicContainerNode: null
}; };
} }