From aedebaf025a9aebaca6cc7828169ac2ef2643ffd Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Thu, 13 Sep 2018 16:07:23 -0700 Subject: [PATCH] refactor(ivy): remove LNode.tNode (#25958) PR Close #25958 --- packages/core/src/render3/component.ts | 50 ++- packages/core/src/render3/component_ref.ts | 50 +-- .../core/src/render3/context_discovery.ts | 20 +- packages/core/src/render3/di.ts | 170 ++++---- packages/core/src/render3/i18n.ts | 96 ++--- packages/core/src/render3/instructions.ts | 320 ++++++++------- .../core/src/render3/interfaces/container.ts | 2 +- .../core/src/render3/interfaces/injector.ts | 4 +- packages/core/src/render3/interfaces/node.ts | 34 +- packages/core/src/render3/interfaces/query.ts | 6 +- packages/core/src/render3/interfaces/view.ts | 26 +- .../core/src/render3/node_manipulation.ts | 369 ++++++++++-------- packages/core/src/render3/query.ts | 79 ++-- packages/core/src/render3/util.ts | 13 +- packages/core/src/render3/view_ref.ts | 16 +- .../hello_world/bundle.golden_symbols.json | 29 +- .../bundling/todo/bundle.golden_symbols.json | 42 +- .../todo_r2/bundle.golden_symbols.json | 39 +- packages/core/test/render3/di_spec.ts | 7 +- .../core/test/render3/instructions_spec.ts | 14 +- .../core/test/render3/integration_spec.ts | 8 +- 21 files changed, 755 insertions(+), 639 deletions(-) diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 2c67a9232d..1420d85ca3 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -18,7 +18,7 @@ import {CLEAN_PROMISE, _getComponentHostLElementNode, baseDirectiveCreate, creat import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition'; import {LElementNode} from './interfaces/node'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; -import {LViewData, LViewFlags, RootContext, BINDING_INDEX, INJECTOR, CONTEXT, TVIEW} from './interfaces/view'; +import {LViewData, LViewFlags, RootContext, INJECTOR, CONTEXT, TVIEW} from './interfaces/view'; import {stringify} from './util'; import {getComponentDef} from './definition'; @@ -53,7 +53,7 @@ export interface CreateComponentOptions { * features list because there's no way of knowing when the component will be used as * a root component. */ - hostFeatures?: ((component: T, componentDef: ComponentDef) => void)[]; + hostFeatures?: HostFeature[]; /** * A function which is used to schedule change detection work in the future. @@ -69,6 +69,9 @@ export interface CreateComponentOptions { scheduler?: (work: () => void) => void; } +/** See CreateComponentOptions.hostFeatures */ +type HostFeature = ((component: T, componentDef: ComponentDef) => void); + // TODO: A hack to not pull in the NullInjector from @angular/core. export const NULL_INJECTOR: Injector = { get: (token: any, notFoundValue?: any) => { @@ -103,12 +106,13 @@ export function renderComponent( // The first index of the first selector is the tag name. const componentTag = componentDef.selectors ![0] ![0] as string; const hostNode = locateHostElement(rendererFactory, opts.host || componentTag); + const rootFlags = componentDef.onPush ? LViewFlags.Dirty | LViewFlags.IsRoot : + LViewFlags.CheckAlways | LViewFlags.IsRoot; const rootContext = createRootContext(opts.scheduler || requestAnimationFrame.bind(window)); const rootView: LViewData = createLViewData( rendererFactory.createRenderer(hostNode, componentDef), - createTView(-1, null, 1, 0, null, null, null), rootContext, - componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); + createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags); rootView[INJECTOR] = opts.injector || null; const oldView = enterView(rootView, null); @@ -119,20 +123,10 @@ export function renderComponent( // Create element node at index 0 in data array elementNode = hostElement(componentTag, hostNode, componentDef, sanitizer); - - // Create directive instance with factory() and store at index 0 in directives array - component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef, elementNode); - if (componentDef.hostBindings) { - queueHostBindingForCheck(0, componentDef.hostVars); - } - rootContext.components.push(component); - (elementNode.data as LViewData)[CONTEXT] = component; - initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !); - - opts.hostFeatures && opts.hostFeatures.forEach((feature) => feature(component, componentDef)); + component = createRootComponent( + elementNode, componentDef, rootView, rootContext, opts.hostFeatures || null); executeInitAndContentHooks(); - setHostBindings(rootView[TVIEW].hostBindings); detectChangesInternal(elementNode.data as LViewData, component); } finally { leaveView(oldView); @@ -142,6 +136,27 @@ export function renderComponent( return component; } +/** + * Creates a root component and sets it up with features and host bindings. Shared by + * renderComponent() and ViewContainerRef.createComponent(). + */ +export function createRootComponent( + elementNode: LElementNode, componentDef: ComponentDef, rootView: LViewData, + rootContext: RootContext, hostFeatures: HostFeature[] | null): any { + // Create directive instance with factory() and store at index 0 in directives array + const component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef, elementNode); + + if (componentDef.hostBindings) queueHostBindingForCheck(0, componentDef.hostVars); + rootContext.components.push(component); + (elementNode.data as LViewData)[CONTEXT] = component; + initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data as LViewData); + + hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef)); + setHostBindings(rootView[TVIEW].hostBindings); + return component; +} + + export function createRootContext(scheduler: (workFn: () => void) => void): RootContext { return { components: [], @@ -169,7 +184,8 @@ export function LifecycleHooksFeature(component: any, def: ComponentDefInternal< // Root component is always created at dir index 0 const tView = elementNode.view[TVIEW]; queueInitHooks(0, def.onInit, def.doCheck, tView); - queueLifecycleHooks(elementNode.tNode.flags, tView); + // Directive starting index 0, directive count 1 -> directive flags: 1 + queueLifecycleHooks(1, tView); } /** diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 9cbd5bf871..6338487732 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -17,13 +17,13 @@ import {RendererFactory2} from '../render/api'; import {Type} from '../type'; import {assertComponentType, assertDefined} from './assert'; -import {LifecycleHooksFeature, createRootContext} from './component'; +import {LifecycleHooksFeature, createRootComponent, createRootContext} from './component'; import {getComponentDef} from './definition'; -import {adjustBlueprintForNewNode, baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, queueHostBindingForCheck, renderEmbeddedTemplate, setHostBindings} from './instructions'; -import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition'; -import {LElementNode, TNode, TNodeType} from './interfaces/node'; +import {adjustBlueprintForNewNode, createLViewData, createNodeAtIndex, createTView, elementCreate, enterView, getTNode, hostElement, locateHostElement, renderEmbeddedTemplate} from './instructions'; +import {ComponentDefInternal, RenderFlags} from './interfaces/definition'; +import {LElementNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; -import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view'; +import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view'; import {RootViewRef, ViewRef} from './view_ref'; export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver { @@ -115,6 +115,8 @@ export class ComponentFactory extends viewEngine_ComponentFactory { // The first index of the first selector is the tag name. const componentTag = this.componentDef.selectors ![0] ![0] as string; + const rootFlags = this.componentDef.onPush ? LViewFlags.Dirty | LViewFlags.IsRoot : + LViewFlags.CheckAlways | LViewFlags.IsRoot; const rootContext: RootContext = ngModule && !isInternalRootView ? ngModule.injector.get(ROOT_CONTEXT) : createRootContext(requestAnimationFrame.bind(window)); @@ -122,8 +124,7 @@ export class ComponentFactory extends viewEngine_ComponentFactory { // Create the root view. Uses empty TView and ContentTemplate. const rootView: LViewData = createLViewData( rendererFactory.createRenderer(hostNode, this.componentDef), - createTView(-1, null, 1, 0, null, null, null), rootContext, - this.componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); + createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags); rootView[INJECTOR] = ngModule && ngModule.injector || null; // rootView is the parent when bootstrapping @@ -131,54 +132,43 @@ export class ComponentFactory extends viewEngine_ComponentFactory { let component: T; let elementNode: LElementNode; + let tElementNode: TElementNode; try { if (rendererFactory.begin) rendererFactory.begin(); // Create element node at index 0 in data array elementNode = hostElement(componentTag, hostNode, this.componentDef); - const componentView = elementNode.data as LViewData; - // Create directive instance with factory() and store at index 0 in directives array - component = - baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef, elementNode); - if (this.componentDef.hostBindings) { - queueHostBindingForCheck(0, this.componentDef.hostVars); - } - rootContext.components.push(component); - initChangeDetectorIfExisting(elementNode.nodeInjector, component, componentView); - componentView[CONTEXT] = component; // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and // executed here? // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref - LifecycleHooksFeature(component, this.componentDef); + component = createRootComponent( + elementNode, this.componentDef, rootView, rootContext, [LifecycleHooksFeature]); - setHostBindings(rootView[TVIEW].hostBindings); + tElementNode = getTNode(0) as TElementNode; // Transform the arrays of native nodes into a LNode structure that can be consumed by the // projection instruction. This is needed to support the reprojection of these nodes. if (projectableNodes) { let index = 0; - const projection: TNode[] = elementNode.tNode.projection = []; + const projection: TNode[] = tElementNode.projection = []; for (let i = 0; i < projectableNodes.length; i++) { const nodeList = projectableNodes[i]; let firstTNode: TNode|null = null; let previousTNode: TNode|null = null; for (let j = 0; j < nodeList.length; j++) { adjustBlueprintForNewNode(rootView); - const lNode = - createLNode(++index, TNodeType.Element, nodeList[j] as RElement, null, null); - if (previousTNode) { - previousTNode.next = lNode.tNode; - } else { - firstTNode = lNode.tNode; - } - previousTNode = lNode.tNode; + const tNode = + createNodeAtIndex(++index, TNodeType.Element, nodeList[j] as RElement, null, null); + previousTNode ? (previousTNode.next = tNode) : (firstTNode = tNode); + previousTNode = tNode; } projection.push(firstTNode !); } } // Execute the template in creation mode only, and then turn off the CreationMode flag + const componentView = elementNode.data as LViewData; renderEmbeddedTemplate(componentView, componentView[TVIEW], component, RenderFlags.Create); componentView[FLAGS] &= ~LViewFlags.CreationMode; } finally { @@ -190,7 +180,7 @@ export class ComponentFactory extends viewEngine_ComponentFactory { new ComponentRef(this.componentType, component, rootView, injector, hostNode !); if (isInternalRootView) { // The host element of the internal root view is attached to the component's host view node - componentRef.hostView._lViewNode !.tNode.child = elementNode.tNode; + componentRef.hostView._tViewNode !.child = tElementNode; } return componentRef; } @@ -219,7 +209,7 @@ export class ComponentRef extends viewEngine_ComponentRef { super(); this.instance = instance; this.hostView = this.changeDetectorRef = new RootViewRef(rootView); - this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView); + this.hostView._tViewNode = createNodeAtIndex(-1, TNodeType.View, null, null, null, rootView); this.injector = injector; this.location = new viewEngine_ElementRef(hostNode); this.componentType = componentType; diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index ca7c6fa40c..88d218ba46 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -321,9 +321,8 @@ function findViaDirective(lViewData: LViewData, directiveInstance: {}): number { if (directiveIndex >= 0) { let tNode = lViewData[TVIEW].firstChild; while (tNode) { - const lNode = getLNodeFromViewData(lViewData, tNode.index) !; - const directiveIndexStart = getDirectiveStartIndex(lNode); - const directiveIndexEnd = getDirectiveEndIndex(lNode, directiveIndexStart); + const directiveIndexStart = getDirectiveStartIndex(tNode); + const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart); if (directiveIndex >= directiveIndexStart && directiveIndex < directiveIndexEnd) { return tNode.index; } @@ -357,10 +356,11 @@ function getLNodeFromViewData(lViewData: LViewData, lElementIndex: number): LEle function discoverDirectiveIndices(lViewData: LViewData, lNodeIndex: number): number[]|null { const directivesAcrossView = lViewData[DIRECTIVES]; const lNode = getLNodeFromViewData(lViewData, lNodeIndex); + const tNode = lViewData[TVIEW].data[lNodeIndex] as TNode; if (lNode && directivesAcrossView && directivesAcrossView.length) { // this check for tNode is to determine if the calue is a LEmementNode instance - const directiveIndexStart = getDirectiveStartIndex(lNode); - const directiveIndexEnd = getDirectiveEndIndex(lNode, directiveIndexStart); + const directiveIndexStart = getDirectiveStartIndex(tNode); + const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart); const directiveIndices: number[] = []; for (let i = directiveIndexStart; i < directiveIndexEnd; i++) { // special case since the instance of the component (if it exists) @@ -388,17 +388,17 @@ function discoverDirectives(lViewData: LViewData, directiveIndices: number[]): n return directives; } -function getDirectiveStartIndex(lNode: LElementNode): number { +function getDirectiveStartIndex(tNode: TNode): number { // the tNode instances store a flag value which then has a // pointer which tells the starting index of where all the // active directives are in the master directive array - return lNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift; + return tNode.flags >> TNodeFlags.DirectiveStartingIndexShift; } -function getDirectiveEndIndex(lNode: LElementNode, startIndex: number): number { - // The end value is also apart of the same flag +function getDirectiveEndIndex(tNode: TNode, startIndex: number): number { + // The end value is also a part of the same flag // (see `TNodeFlags` to see how the flag bit shifting // values are used). - const count = lNode.tNode.flags & TNodeFlags.DirectiveCountMask; + const count = tNode.flags & TNodeFlags.DirectiveCountMask; return count ? (startIndex + count) : -1; } diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 17cf5e5c8f..9e490e940d 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -26,16 +26,17 @@ import {Type} from '../type'; import {assertDefined, assertGreaterThan, assertLessThan} from './assert'; import {ComponentFactoryResolver} from './component_ref'; import {getComponentDef, getDirectiveDef, getPipeDef} from './definition'; -import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getPreviousOrParentTNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions'; -import {VIEWS} from './interfaces/container'; +import {_getViewData, addToViewTree, assertPreviousIsParent, createEmbeddedViewAndNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate, resolveDirective} from './instructions'; +import {RENDER_PARENT, VIEWS} from './interfaces/container'; import {DirectiveDefInternal, RenderFlags} from './interfaces/definition'; import {LInjector} from './interfaces/injector'; -import {AttributeMarker, LContainerNode, LElementContainerNode, LElementNode, LNode, LNodeWithLocalRefs, LViewNode, TContainerNode, TElementNode, TNodeFlags, TNodeType} from './interfaces/node'; +import {AttributeMarker, LContainerNode, LElementContainerNode, LElementNode, LNode, LNodeWithLocalRefs, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node'; import {LQueries, QueryReadType} from './interfaces/query'; -import {Renderer3, isProceduralRenderer} from './interfaces/renderer'; -import {DIRECTIVES, HOST_NODE, INJECTOR, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view'; +import {RElement, Renderer3, isProceduralRenderer} from './interfaces/renderer'; +import {CONTEXT, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, PARENT, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; -import {addRemoveViewFromContainer, appendChild, detachView, getChildLNode, getParentLNode, insertView, removeView} from './node_manipulation'; +import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getHostElementNode, getParentLNode, getParentOrContainerNode, getRenderParent, insertView, removeView} from './node_manipulation'; +import {isComponent, loadElementInternal} from './util'; import {ViewRef} from './view_ref'; @@ -101,7 +102,8 @@ export function bloomAdd(injector: LInjector, type: Type): void { export function getOrCreateNodeInjector(): LInjector { ngDevMode && assertPreviousIsParent(); return getOrCreateNodeInjectorForNode( - getPreviousOrParentNode() as LElementNode | LElementContainerNode | LContainerNode); + getPreviousOrParentNode() as LElementNode | LElementContainerNode | LContainerNode, + getPreviousOrParentTNode()); } /** @@ -111,16 +113,18 @@ export function getOrCreateNodeInjector(): LInjector { * @returns Node injector */ export function getOrCreateNodeInjectorForNode( - node: LElementNode | LElementContainerNode | LContainerNode): LInjector { + node: LElementNode | LElementContainerNode | LContainerNode, tNode: TNode): LInjector { + // TODO: remove LNode arg when nodeInjector refactor is done const nodeInjector = node.nodeInjector; - const parent = getParentLNode(node); - const parentInjector = parent && parent.nodeInjector; + const parentLNode = getParentOrContainerNode(tNode, node.view); + const parentInjector = parentLNode && parentLNode.nodeInjector; if (nodeInjector != parentInjector) { return nodeInjector !; } return node.nodeInjector = { parent: parentInjector, node: node, + tNode: tNode, bf0: 0, bf1: 0, bf2: 0, @@ -302,32 +306,25 @@ export function getOrCreateChangeDetectorRef( di: LInjector, context: any): viewEngine_ChangeDetectorRef { if (di.changeDetectorRef) return di.changeDetectorRef; - const currentNode = di.node; - if (isComponent(currentNode.tNode)) { - return di.changeDetectorRef = new ViewRef(currentNode.data as LViewData, context); - } else if (currentNode.tNode.type === TNodeType.Element) { - return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view[HOST_NODE]); + const currentTNode = getPreviousOrParentTNode(); + if (isComponent(currentTNode)) { + return di.changeDetectorRef = new ViewRef(di.node.data as LViewData, context); + } else if (currentTNode.type === TNodeType.Element) { + return di.changeDetectorRef = getOrCreateHostChangeDetector(_getViewData()); } return null !; } /** Gets or creates ChangeDetectorRef for the closest host component */ -function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode): - viewEngine_ChangeDetectorRef { - const hostNode = getClosestComponentAncestor(currentNode); +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( - hostNode.data as LViewData, - hostNode - .view[DIRECTIVES] ![hostNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift]); + return existingRef ? existingRef : new ViewRef(hostComponentView, hostComponentView[CONTEXT]); } - - function getOrCreateRenderer2(di: LInjector): Renderer2 { const renderer = di.node.view[RENDERER]; if (isProceduralRenderer(renderer)) { @@ -337,18 +334,6 @@ function getOrCreateRenderer2(di: LInjector): Renderer2 { } } -/** - * If the node is an embedded view, traverses up the view tree to return the closest - * ancestor view that is attached to a component. If it's already a component node, - * returns itself. - */ -function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNode { - while (node.tNode.type === TNodeType.View) { - node = node.view[HOST_NODE]; - } - return node as LElementNode; -} - /** * Returns the value associated to the given token from the injectors. * @@ -384,7 +369,8 @@ export function getOrCreateInjectable( // 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. const node = injector.node; - const nodeFlags = node.tNode.flags; + const tNode = injector.tNode; + const nodeFlags = tNode.flags; const count = nodeFlags & TNodeFlags.DirectiveCountMask; if (count !== 0) { @@ -420,7 +406,7 @@ export function getOrCreateInjectable( } } - const moduleInjector = getPreviousOrParentNode().view[INJECTOR]; + const moduleInjector = getPreviousOrParentNode() !.view[INJECTOR]; const formerInjector = setCurrentInjector(moduleInjector); try { return inject(token, flags); @@ -546,7 +532,7 @@ function sameHostView(injector: LInjector): boolean { } export class ReadFromInjectorFn { - constructor(readonly read: (injector: LInjector, node: LNode, directiveIndex?: number) => T) {} + constructor(readonly read: (injector: LInjector, tNode: TNode, directiveIndex?: number) => T) {} } /** @@ -573,22 +559,21 @@ export const QUERY_READ_ELEMENT_REF = (injector: LInjector) => getOrCreateElementRef(injector)) as any); export const QUERY_READ_FROM_NODE = - (new ReadFromInjectorFn((injector: LInjector, node: LNode, directiveIdx: number) => { - ngDevMode && - assertNodeOfPossibleTypes( - node.tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); + (new ReadFromInjectorFn((injector: LInjector, tNode: TNode, directiveIdx: number) => { + ngDevMode && assertNodeOfPossibleTypes( + tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); if (directiveIdx > -1) { - return node.view[DIRECTIVES] ![directiveIdx]; + return _getViewData()[DIRECTIVES] ![directiveIdx]; } - if (node.tNode.type === TNodeType.Element || node.tNode.type === TNodeType.ElementContainer) { + if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) { return getOrCreateElementRef(injector); } - if (node.tNode.type === TNodeType.Container) { + if (tNode.type === TNodeType.Container) { return getOrCreateTemplateRef(injector); } if (ngDevMode) { // should never happen - throw new Error(`Unexpected node type: ${node.tNode.type}`); + throw new Error(`Unexpected node type: ${tNode.type}`); } }) as any as QueryReadType); @@ -603,29 +588,32 @@ class ElementRef extends viewEngine_ElementRef {} */ export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainerRef { if (!di.viewContainerRef) { - const vcRefHost = di.node; - const hostTNode = vcRefHost.tNode as TElementNode | TContainerNode; - + const hostLNode = + getPreviousOrParentNode() as LElementNode | LContainerNode | LElementContainerNode; + const hostTNode = getPreviousOrParentTNode() as TElementNode | TContainerNode; ngDevMode && assertNodeOfPossibleTypes( hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); - const hostParent = getParentLNode(vcRefHost) !; - const lContainer = createLContainer(hostParent, vcRefHost.view, true); - const comment = vcRefHost.view[RENDERER].createComment(ngDevMode ? 'container' : ''); + + const hostView = hostLNode.view; + const lContainer = createLContainer(hostView, true); + const comment = hostView[RENDERER].createComment(ngDevMode ? 'container' : ''); const lContainerNode: LContainerNode = createLNodeObject( - TNodeType.Container, vcRefHost.view, vcRefHost.nodeInjector, comment, lContainer); - appendChild(hostParent, comment, vcRefHost.view); + TNodeType.Container, hostView, hostLNode.nodeInjector, comment, lContainer); + + lContainer[RENDER_PARENT] = getRenderParent(hostTNode, hostView); + + appendChild(comment, hostTNode, hostView); if (!hostTNode.dynamicContainerNode) { hostTNode.dynamicContainerNode = createTNode(TNodeType.Container, -1, null, null, hostTNode, null); } - lContainerNode.tNode = hostTNode.dynamicContainerNode; - vcRefHost.dynamicLContainerNode = lContainerNode; + hostLNode.dynamicLContainerNode = lContainerNode; + addToViewTree(hostView, hostTNode.index as number, lContainer); - addToViewTree(vcRefHost.view, hostTNode.index as number, lContainer); - - di.viewContainerRef = new ViewContainerRef(lContainerNode, vcRefHost); + di.viewContainerRef = new ViewContainerRef( + lContainerNode, hostTNode.dynamicContainerNode as TContainerNode, hostLNode, hostTNode); } return di.viewContainerRef; @@ -663,24 +651,25 @@ class ViewContainerRef extends viewEngine_ViewContainerRef { private _viewRefs: viewEngine_ViewRef[] = []; constructor( - private _lContainerNode: LContainerNode, - private _hostNode: LElementNode|LElementContainerNode|LContainerNode) { + private _lContainerNode: LContainerNode, private _tContainerNode: TContainerNode, + private _hostNode: LElementNode|LElementContainerNode|LContainerNode, + private _hostTNode: TNode) { super(); } get element(): ElementRef { - const injector = getOrCreateNodeInjectorForNode(this._hostNode); + const injector = getOrCreateNodeInjectorForNode(this._hostNode, this._hostTNode); return getOrCreateElementRef(injector); } get injector(): Injector { - const injector = getOrCreateNodeInjectorForNode(this._hostNode); + const injector = getOrCreateNodeInjectorForNode(this._hostNode, this._hostTNode); return new NodeInjector(injector); } /** @deprecated No replacement */ get parentInjector(): Injector { - const parentLInjector = getParentLNode(this._hostNode).nodeInjector; + const parentLInjector = getParentLNode(this._hostTNode, this._hostNode.view) !.nodeInjector; return parentLInjector ? new NodeInjector(parentLInjector) : new NullInjector(); } @@ -701,8 +690,10 @@ class ViewContainerRef extends viewEngine_ViewContainerRef { createEmbeddedView(templateRef: viewEngine_TemplateRef, context?: C, index?: number): viewEngine_EmbeddedViewRef { const adjustedIdx = this._adjustIndex(index); - const viewRef = (templateRef as TemplateRef) - .createEmbeddedView(context || {}, this._lContainerNode, adjustedIdx); + const viewRef = + (templateRef as TemplateRef) + .createEmbeddedView( + context || {}, this._lContainerNode, this._tContainerNode, adjustedIdx); (viewRef as ViewRef).attachToViewContainerRef(this); this._viewRefs.splice(adjustedIdx, 0, viewRef); return viewRef; @@ -727,15 +718,13 @@ class ViewContainerRef extends viewEngine_ViewContainerRef { if (viewRef.destroyed) { throw new Error('Cannot insert a destroyed View in a ViewContainer!'); } - const lViewNode = (viewRef as ViewRef)._lViewNode !; + const lView = (viewRef as ViewRef)._view !; const adjustedIdx = this._adjustIndex(index); - insertView(this._lContainerNode, lViewNode, adjustedIdx); - const views = this._lContainerNode.data[VIEWS]; - const beforeNode = adjustedIdx + 1 < views.length ? - (getChildLNode(views[adjustedIdx + 1][HOST_NODE]) !).native : - this._lContainerNode.native; - addRemoveViewFromContainer(this._lContainerNode, lViewNode.data, true, beforeNode); + insertView(this._lContainerNode, lView, adjustedIdx, this._tContainerNode.parent !.index); + const beforeNode = + getBeforeNodeForView(adjustedIdx, this._lContainerNode.data[VIEWS], this._lContainerNode); + addRemoveViewFromContainer(lView, true, beforeNode); (viewRef as ViewRef).attachToViewContainerRef(this); this._viewRefs.splice(adjustedIdx, 0, viewRef); @@ -754,13 +743,13 @@ class ViewContainerRef extends viewEngine_ViewContainerRef { remove(index?: number): void { const adjustedIdx = this._adjustIndex(index, -1); - removeView(this._lContainerNode, adjustedIdx); + removeView(this._lContainerNode, this._tContainerNode as TContainerNode, adjustedIdx); this._viewRefs.splice(adjustedIdx, 1); } detach(index?: number): viewEngine_ViewRef|null { const adjustedIdx = this._adjustIndex(index, -1); - detachView(this._lContainerNode, adjustedIdx); + detachView(this._lContainerNode, adjustedIdx, !!this._tContainerNode.detached); return this._viewRefs.splice(adjustedIdx, 1)[0] || null; } @@ -786,13 +775,13 @@ class ViewContainerRef extends viewEngine_ViewContainerRef { */ export function getOrCreateTemplateRef(di: LInjector): viewEngine_TemplateRef { if (!di.templateRef) { - const hostNode = di.node as LContainerNode; - const hostTNode = hostNode.tNode; + const hostNode = getPreviousOrParentNode() as LContainerNode; + const hostTNode = getPreviousOrParentTNode() as TContainerNode; ngDevMode && assertNodeType(hostTNode, TNodeType.Container); ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated'); di.templateRef = new TemplateRef( hostNode.view, getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(), - hostNode.data[QUERIES]); + hostNode.data ![QUERIES]); } return di.templateRef; } @@ -828,16 +817,17 @@ class TemplateRef extends viewEngine_TemplateRef { super(); } - createEmbeddedView(context: T, containerNode?: LContainerNode, index?: number): - viewEngine_EmbeddedViewRef { - const viewNode = createEmbeddedViewNode( + createEmbeddedView( + context: T, containerNode?: LContainerNode, tContainerNode?: TContainerNode, + index?: number): viewEngine_EmbeddedViewRef { + const lView = createEmbeddedViewAndNode( this._tView, context, this._declarationParentView, this._renderer, this._queries); if (containerNode) { - insertView(containerNode, viewNode, index !); + insertView(containerNode, lView, index !, tContainerNode !.parent !.index); } - renderEmbeddedTemplate(viewNode.data, this._tView, context, RenderFlags.Create); - const viewRef = new ViewRef(viewNode.data, context); - viewRef._lViewNode = viewNode; + renderEmbeddedTemplate(lView, this._tView, context, RenderFlags.Create); + const viewRef = new ViewRef(lView, context); + viewRef._tViewNode = lView[HOST_NODE] as TViewNode; return viewRef; } } @@ -846,6 +836,6 @@ class TemplateRef extends viewEngine_TemplateRef { * Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the * `` element. */ -export function templateRefExtractor(lNode: LNodeWithLocalRefs) { - return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode)); +export function templateRefExtractor(lNode: LNodeWithLocalRefs, tNode: TNode) { + return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode, tNode)); } diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index 32ac12d240..0aa642d771 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -7,11 +7,12 @@ */ import {assertEqual, assertLessThan} from './assert'; -import {NO_CHANGE, _getViewData, adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createLNode, getPreviousOrParentNode, getRenderer, load, resetComponentState} from './instructions'; +import {NO_CHANGE, _getViewData, adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createNodeAtIndex, getRenderer, getTNode, load, loadElement, resetComponentState} from './instructions'; import {RENDER_PARENT} from './interfaces/container'; -import {LContainerNode, LNode, TContainerNode, TElementNode, TNodeType} from './interfaces/node'; -import {BINDING_INDEX, HEADER_OFFSET, TVIEW} from './interfaces/view'; -import {appendChild, createTextNode, getParentLNode, removeChild} from './node_manipulation'; +import {LContainerNode, LElementNode, LNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node'; +import {RElement} from './interfaces/renderer'; +import {BINDING_INDEX, HEADER_OFFSET, HOST_NODE, TVIEW} from './interfaces/view'; +import {appendChild, createTextNode, getContainerRenderParent, getParentLNode, getParentOrContainerNode, removeChild} from './node_manipulation'; import {stringify} from './util'; /** @@ -245,42 +246,42 @@ function generateMappingInstructions( return partIndex; } -function appendI18nNode(node: LNode, parentNode: LNode, previousNode: LNode) { +// TODO: Remove LNode arg when we remove dynamicContainerNode +function appendI18nNode( + node: LNode, tNode: TNode, parentTNode: TNode, previousTNode: TNode): TNode { if (ngDevMode) { ngDevMode.rendererMoveNode++; } const viewData = _getViewData(); - appendChild(parentNode, node.native || null, viewData); - // On first pass, re-organize node tree to put this node in the correct position. const firstTemplatePass = node.view[TVIEW].firstTemplatePass; if (firstTemplatePass) { - if (previousNode === parentNode && node.tNode !== parentNode.tNode.child) { - node.tNode.next = parentNode.tNode.child; - parentNode.tNode.child = node.tNode; - } else if (previousNode !== parentNode && node.tNode !== previousNode.tNode.next) { - node.tNode.next = previousNode.tNode.next; - previousNode.tNode.next = node.tNode; + if (previousTNode === parentTNode && tNode !== parentTNode.child) { + tNode.next = parentTNode.child; + parentTNode.child = tNode; + } else if (previousTNode !== parentTNode && tNode !== previousTNode.next) { + tNode.next = previousTNode.next; + previousTNode.next = tNode; } else { - node.tNode.next = null; + tNode.next = null; } - if (parentNode.view === node.view) node.tNode.parent = parentNode.tNode as TElementNode; + if (parentTNode !== viewData[HOST_NODE]) { + tNode.parent = parentTNode as TElementNode; + } } + appendChild(node.native, tNode, viewData); + // Template containers also have a comment node for the `ViewContainerRef` that should be moved - if (node.tNode.type === TNodeType.Container && node.dynamicLContainerNode) { - appendChild(parentNode, node.dynamicLContainerNode.native || null, viewData); - if (firstTemplatePass) { - node.tNode.dynamicContainerNode = node.dynamicLContainerNode.tNode; - node.dynamicLContainerNode.tNode.parent = node.tNode as TContainerNode; - } - return node.dynamicLContainerNode; + if (tNode.type === TNodeType.Container && node.dynamicLContainerNode) { + appendChild(node.dynamicLContainerNode.native, tNode, viewData); + return tNode.dynamicContainerNode !; } - return node; + return tNode; } /** @@ -303,23 +304,29 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]): } const renderer = getRenderer(); - let localParentNode: LNode = getParentLNode(load(startIndex)) || getPreviousOrParentNode(); - let localPreviousNode: LNode = localParentNode; + const startTNode = getTNode(startIndex); + let localParentTNode: TNode = startTNode.parent || viewData[HOST_NODE] !; + let localPreviousTNode: TNode = localParentTNode; resetComponentState(); // We don't want to add to the tree with the wrong previous node for (let i = 0; i < instructions.length; i++) { const instruction = instructions[i] as number; switch (instruction & I18nInstructions.InstructionMask) { case I18nInstructions.Element: - const element: LNode = load(instruction & I18nInstructions.IndexMask); - localPreviousNode = appendI18nNode(element, localParentNode, localPreviousNode); - localParentNode = element; + const elementIndex = instruction & I18nInstructions.IndexMask; + const element: LNode = load(elementIndex); + const elementTNode = getTNode(elementIndex); + localPreviousTNode = + appendI18nNode(element, elementTNode, localParentTNode, localPreviousTNode); + localParentTNode = elementTNode; break; case I18nInstructions.Expression: case I18nInstructions.TemplateRoot: case I18nInstructions.Any: - const node: LNode = load(instruction & I18nInstructions.IndexMask); - localPreviousNode = appendI18nNode(node, localParentNode, localPreviousNode); + const nodeIndex = instruction & I18nInstructions.IndexMask; + const node: LNode = load(nodeIndex); + localPreviousTNode = + appendI18nNode(node, getTNode(nodeIndex), localParentTNode, localPreviousTNode); break; case I18nInstructions.Text: if (ngDevMode) { @@ -329,31 +336,32 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]): const textRNode = createTextNode(value, renderer); // If we were to only create a `RNode` then projections won't move the text. // Create text node at the current end of viewData. Must subtract header offset because - // createLNode takes a raw index (not adjusted by header offset). + // createNodeAtIndex takes a raw index (not adjusted by header offset). adjustBlueprintForNewNode(viewData); - const lastNodeIndex = viewData.length - 1; - const textLNode = - createLNode(lastNodeIndex - HEADER_OFFSET, TNodeType.Element, textRNode, null, null); - localPreviousNode = appendI18nNode(textLNode, localParentNode, localPreviousNode); + const lastNodeIndex = viewData.length - 1 - HEADER_OFFSET; + const textTNode = + createNodeAtIndex(lastNodeIndex, TNodeType.Element, textRNode, null, null); + localPreviousTNode = appendI18nNode( + loadElement(lastNodeIndex), textTNode, localParentTNode, localPreviousTNode); resetComponentState(); break; case I18nInstructions.CloseNode: - localPreviousNode = localParentNode; - localParentNode = getParentLNode(localParentNode) !; + localPreviousTNode = localParentTNode; + localParentTNode = localParentTNode.parent || viewData[HOST_NODE] !; break; case I18nInstructions.RemoveNode: if (ngDevMode) { ngDevMode.rendererRemoveNode++; } - const index = instruction & I18nInstructions.IndexMask; - const removedNode: LNode|LContainerNode = load(index); - const parentNode = getParentLNode(removedNode) !; - removeChild(parentNode, removedNode.native || null, viewData); + const removeIndex = instruction & I18nInstructions.IndexMask; + const removedNode: LNode|LContainerNode = load(removeIndex); + const removedTNode = getTNode(removeIndex); + removeChild(removedTNode, removedNode.native || null, viewData); // For template containers we also need to remove their `ViewContainerRef` from the DOM - if (removedNode.tNode.type === TNodeType.Container && removedNode.dynamicLContainerNode) { - removeChild(parentNode, removedNode.dynamicLContainerNode.native || null, viewData); - removedNode.dynamicLContainerNode.tNode.detached = true; + if (removedTNode.type === TNodeType.Container && removedNode.dynamicLContainerNode) { + removeChild(removedTNode, removedNode.dynamicLContainerNode.native || null, viewData); + removedTNode.dynamicContainerNode !.detached = true; removedNode.dynamicLContainerNode.data[RENDER_PARENT] = null; } break; diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index afdc77df45..3c6873a8fc 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -12,26 +12,27 @@ import {QueryList} from '../linker'; import {Sanitizer} from '../sanitization/security'; import {StyleSanitizeFn} from '../sanitization/style_sanitizer'; -import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotEqual} from './assert'; -import {attachPatchData, getLElementFromComponent, getLElementFromRootComponent, readPatchedData, readPatchedLViewData} from './context_discovery'; +import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './assert'; +import {attachPatchData, getLElementFromComponent, readPatchedLViewData} from './context_discovery'; import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors'; import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks'; import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container'; import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition'; 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, TViewNode} from './interfaces/node'; +import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementNode, LNode, LNodeWithLocalRefs, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node'; import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'; import {LQueries} from './interfaces/query'; -import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; -import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view'; +import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer'; +import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; -import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getLViewChild, getParentLNode, insertView, removeView} from './node_manipulation'; +import {appendChild, appendProjectedNode, createTextNode, findComponentView, getContainerNode, getHostElementNode, getLViewChild, getParentLNode, getParentOrContainerNode, getRenderParent, insertView, removeView} from './node_manipulation'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling'; -import {assertDataInRangeInternal, isDifferent, loadElementInternal, loadInternal, readElementValue, stringify} from './util'; +import {assertDataInRangeInternal, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, readElementValue, stringify} from './util'; import {ViewRef} from './view_ref'; + /** * A permanent marker promise which signifies that the current CD tree is * clean. @@ -128,9 +129,9 @@ export function restoreView(viewToRestore: OpaqueViewState) { /** Used to set the parent property when nodes are created and track query results. */ let previousOrParentTNode: TNode; -export function getPreviousOrParentNode(): LNode { - return previousOrParentTNode == null || previousOrParentTNode === tView.node ? - viewData[HOST_NODE] : +export function getPreviousOrParentNode(): LNode|null { + return previousOrParentTNode == null || previousOrParentTNode === viewData[HOST_NODE] ? + getHostElementNode(viewData) : readElementValue(viewData[previousOrParentTNode.index]); } @@ -162,7 +163,7 @@ export function getOrCreateCurrentQueries( QueryType: {new (parent: null, shallow: null, deep: null): LQueries}): LQueries { // 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. - if (previousOrParentTNode && previousOrParentTNode !== tView.node && + if (previousOrParentTNode && previousOrParentTNode !== viewData[HOST_NODE] && !isContentQueryHost(previousOrParentTNode)) { currentQueries && (currentQueries = currentQueries.clone()); previousOrParentTNode.flags |= TNodeFlags.hasContentQuery; @@ -400,7 +401,6 @@ export function createLNodeObject( view: currentView, nodeInjector: nodeInjector, data: state, - tNode: null !, dynamicLContainerNode: null }; } @@ -417,40 +417,41 @@ export function createLNodeObject( * @param attrs Any attrs for the native element, if applicable * @param data Any data that should be saved on the LNode */ -export function createLNode( +export function createNodeAtIndex( index: number, type: TNodeType.Element, native: RElement | RText | null, name: string | null, - attrs: TAttributes | null, lViewData?: LViewData | null): LElementNode; -export function createLNode( + attrs: TAttributes | null, lViewData?: LViewData | null): TElementNode; +export function createNodeAtIndex( index: number, type: TNodeType.View, native: null, name: null, attrs: null, - lViewData: LViewData): LViewNode; -export function createLNode( + lViewData: LViewData): TViewNode; +export function createNodeAtIndex( index: number, type: TNodeType.Container, native: RComment, name: string | null, - attrs: TAttributes | null, lContainer: LContainer): LContainerNode; -export function createLNode( + attrs: TAttributes | null, lContainer: LContainer): TContainerNode; +export function createNodeAtIndex( index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null, - lProjection: null): LProjectionNode; -export function createLNode( + lProjection: null): TProjectionNode; +export function createNodeAtIndex( index: number, type: TNodeType.ElementContainer, native: RComment, name: null, - attrs: TAttributes | null, data: null): LElementContainerNode; -export function createLNode( + attrs: TAttributes | null, data: null): TElementContainerNode; +export function createNodeAtIndex( index: number, type: TNodeType, native: RText | RElement | RComment | null, name: string | null, - attrs: TAttributes | null, state?: null | LViewData | LContainer): LElementNode<extNode& - LViewNode&LContainerNode&LProjectionNode { + attrs: TAttributes | null, state?: null | LViewData | LContainer): TElementNode&TViewNode& + TContainerNode&TElementContainerNode&TProjectionNode { const parent = isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent; // Parents cannot cross component boundaries because components will be used in multiple places, // so it's only set if the view is the same. - const parentInSameView = parent && tView && parent !== tView.node; + const parentInSameView = parent && viewData && parent !== viewData[HOST_NODE]; const tParent = parentInSameView ? parent as TElementNode | TContainerNode : null; const isState = state != null; const node = createLNodeObject(type, viewData, null, native, isState ? state as any : null); + let tNode: TNode; if (index === -1 || type === TNodeType.View) { // View nodes are not stored in data because they can be added / removed at runtime (which - // would cause indices to change). Their TNodes are instead stored in TView.node. - node.tNode = (state ? (state as LViewData)[TVIEW].node : null) || + // would cause indices to change). Their TNodes are instead stored in tView.node. + tNode = (state ? (state as LViewData)[TVIEW].node : null) || createTNode(type, index, null, null, tParent, null); } else { const adjustedIndex = index + HEADER_OFFSET; @@ -472,9 +473,9 @@ export function createLNode( } } - node.tNode = tData[adjustedIndex] as TNode; + tNode = tData[adjustedIndex] as TNode; if (!tView.firstChild && type === TNodeType.Element) { - tView.firstChild = node.tNode; + tView.firstChild = tNode; } // Now link ourselves into the tree. @@ -482,13 +483,17 @@ export function createLNode( if (previousOrParentTNode.child == null && parentInSameView || previousOrParentTNode.type === TNodeType.View) { // We are in the same view, which means we are adding content node to the parent View. - previousOrParentTNode.child = node.tNode; + previousOrParentTNode.child = tNode; } } } - // TODO: temporary, remove when removing LNode.nodeInjector - const parentLNode = index === -1 ? null : getParentLNode(node); - if (parentLNode) node.nodeInjector = parentLNode.nodeInjector; + // TODO: temporary, remove this when removing nodeInjector (bringing in fns to hello world) + if (index !== -1 && !(viewData[FLAGS] & LViewFlags.IsRoot)) { + const parentLNode: LNode|null = type === TNodeType.View ? + getContainerNode(tNode, state as LViewData) : + getParentOrContainerNode(tNode, viewData); + parentLNode && (node.nodeInjector = parentLNode.nodeInjector); + } // View nodes and host elements need to set their host node (components do not save host TNodes) if ((type & TNodeType.ViewOrElement) === TNodeType.ViewOrElement && isState) { @@ -496,15 +501,16 @@ export function createLNode( ngDevMode && assertEqual( lViewData[HOST_NODE], null, 'lViewData[HOST_NODE] should not have been initialized'); - lViewData[HOST_NODE] = node; + lViewData[HOST_NODE] = tNode as TElementNode | TViewNode; if (lViewData[TVIEW].firstTemplatePass) { - lViewData[TVIEW].node = node.tNode as TViewNode | TElementNode; + lViewData[TVIEW].node = tNode as TViewNode | TElementNode; } } - previousOrParentTNode = node.tNode; + previousOrParentTNode = tNode; isParent = true; - return node; + return tNode as TElementNode & TViewNode & TContainerNode & TElementContainerNode & + TProjectionNode; } /** @@ -554,17 +560,23 @@ export function renderTemplate( if (host == null) { resetComponentState(); rendererFactory = providedRendererFactory; - const tView = + renderer = providedRendererFactory.createRenderer(null, null); + + // We need to create a root view so it's possible to look up the host element through its index + tView = createTView(-1, null, 1, 0, null, null, null); + viewData = createLViewData(renderer, tView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot); + + const componentTView = getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null); - host = createLNode( - -1, TNodeType.Element, hostNode, null, null, - createLViewData( - providedRendererFactory.createRenderer(null, null), tView, {}, LViewFlags.CheckAlways, - sanitizer)); + const componentLView = + createLViewData(renderer, componentTView, context, LViewFlags.CheckAlways, sanitizer); + createNodeAtIndex(0, TNodeType.Element, hostNode, null, null, componentLView); + host = loadElement(0); } const hostView = host.data !; ngDevMode && assertDefined(hostView, 'Host node should have an LView defined in host.data.'); renderComponentOrTemplate(hostView, context, templateFn); + return host; } @@ -573,9 +585,9 @@ export function renderTemplate( * either through ViewContainerRef.createEmbeddedView() or TemplateRef.createEmbeddedView(). * Such lViewNode will then be renderer with renderEmbeddedTemplate() (see below). */ -export function createEmbeddedViewNode( +export function createEmbeddedViewAndNode( tView: TView, context: T, declarationView: LViewData, renderer: Renderer3, - queries?: LQueries | null): LViewNode { + queries?: LQueries | null): LViewData { const _isParent = isParent; const _previousOrParentTNode = previousOrParentTNode; isParent = true; @@ -588,11 +600,11 @@ export function createEmbeddedViewNode( if (queries) { lView[QUERIES] = queries.createView(); } - const viewNode = createLNode(-1, TNodeType.View, null, null, null, lView); + createNodeAtIndex(-1, TNodeType.View, null, null, null, lView); isParent = _isParent; previousOrParentTNode = _previousOrParentTNode; - return viewNode; + return lView; } /** @@ -610,7 +622,7 @@ export function renderEmbeddedTemplate( const _isParent = isParent; const _previousOrParentTNode = previousOrParentTNode; let oldView: LViewData; - if (viewToRender[PARENT] == null && viewToRender[CONTEXT] && !tView.template) { + if (viewToRender[FLAGS] & LViewFlags.IsRoot) { // This is a root view inside the view tree tickRootContext(viewToRender[CONTEXT] as RootContext); } else { @@ -618,7 +630,7 @@ export function renderEmbeddedTemplate( isParent = true; previousOrParentTNode = null !; - oldView = enterView(viewToRender, tView.node); + oldView = enterView(viewToRender, viewToRender[HOST_NODE]); namespaceHTML(); tView.template !(rf, context); if (rf & RenderFlags.Update) { @@ -654,7 +666,7 @@ export function nextContext(level: number = 1): T { export function renderComponentOrTemplate( hostView: LViewData, componentOrContext: T, templateFn?: ComponentTemplate) { - const oldView = enterView(hostView, null); + const oldView = enterView(hostView, hostView[HOST_NODE]); try { if (rendererFactory.begin) { rendererFactory.begin(); @@ -751,12 +763,11 @@ export function elementContainerStart( const native = renderer.createComment(ngDevMode ? 'ng-container' : ''); ngDevMode && assertDataInRange(index - 1); + const tNode = + createNodeAtIndex(index, TNodeType.ElementContainer, native, null, attrs || null, null); - const node: LElementContainerNode = - createLNode(index, TNodeType.ElementContainer, native, null, attrs || null, null); - - appendChild(getParentLNode(node), native, viewData); - createDirectivesAndLocals(node, localRefs); + appendChild(native, tNode, viewData); + createDirectivesAndLocals(localRefs); } /** Mark the end of the . */ @@ -768,10 +779,8 @@ export function elementContainerEnd(): void { previousOrParentTNode = previousOrParentTNode.parent !; } - // 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(previousOrParentTNode)); queueLifecycleHooks(previousOrParentTNode.flags, tView); } @@ -800,14 +809,13 @@ export function elementStart( ngDevMode && assertDataInRange(index - 1); - const node: LElementNode = - createLNode(index, TNodeType.Element, native !, name, attrs || null, null); + const tNode = createNodeAtIndex(index, TNodeType.Element, native !, name, attrs || null, null); if (attrs) { setUpAttributes(native, attrs); } - appendChild(getParentLNode(node), native, viewData); - createDirectivesAndLocals(node, localRefs); + appendChild(native, tNode, viewData); + createDirectivesAndLocals(localRefs); // any immediate children of a component or template container must be pre-emptively // monkey-patched with the component view data so that the element can be inspected @@ -840,7 +848,7 @@ export function elementCreate(name: string, overriddenRenderer?: Renderer3): REl return native; } -function nativeNodeLocalRefExtractor(lNode: LNodeWithLocalRefs): RNode { +function nativeNodeLocalRefExtractor(lNode: LNodeWithLocalRefs, tNode: TNode): RNode { return lNode.native; } @@ -852,7 +860,7 @@ function nativeNodeLocalRefExtractor(lNode: LNodeWithLocalRefs): RNode { * @param localRefExtractor mapping function that extracts local ref value from LNode */ function createDirectivesAndLocals( - lNode: LNodeWithLocalRefs, localRefs: string[] | null | undefined, + localRefs: string[] | null | undefined, localRefExtractor: LocalRefExtractor = nativeNodeLocalRefExtractor) { if (firstTemplatePass) { ngDevMode && ngDevMode.firstTemplatePass++; @@ -860,7 +868,7 @@ function createDirectivesAndLocals( } else { instantiateDirectivesDirectly(); } - saveResolvedLocalsInData(lNode, localRefExtractor); + saveResolvedLocalsInData(localRefExtractor); } /** @@ -949,14 +957,6 @@ export function initChangeDetectorIfExisting( } } -export function isContentQueryHost(tNode: TNode): boolean { - return (tNode.flags & TNodeFlags.hasContentQuery) !== 0; -} - -export function isComponent(tNode: TNode): boolean { - return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent; -} - /** * This function instantiates the given directives. */ @@ -1016,14 +1016,15 @@ function saveNameToExportMap( * Takes a list of local names and indices and pushes the resolved local variable values * to LViewData in the same order as they are loaded in the template with load(). */ -function saveResolvedLocalsInData( - lNode: LNodeWithLocalRefs, localRefExtractor: LocalRefExtractor): void { - const localNames = lNode.tNode.localNames; +function saveResolvedLocalsInData(localRefExtractor: LocalRefExtractor): void { + const localNames = previousOrParentTNode.localNames; + const node = getPreviousOrParentNode() as LElementNode; if (localNames) { - let localIndex = lNode.tNode.index + 1; + let localIndex = previousOrParentTNode.index + 1; for (let i = 0; i < localNames.length; i += 2) { const index = localNames[i + 1] as number; - const value = index === -1 ? localRefExtractor(lNode) : directives ![index]; + const value = + index === -1 ? localRefExtractor(node, previousOrParentTNode) : directives ![index]; viewData[localIndex++] = value; } } @@ -1192,7 +1193,7 @@ export function hostElement( tag: string, rNode: RElement | null, def: ComponentDefInternal, sanitizer?: Sanitizer | null): LElementNode { resetComponentState(); - const node = createLNode( + const tNode = createNodeAtIndex( 0, TNodeType.Element, rNode, null, null, createLViewData( renderer, @@ -1201,12 +1202,12 @@ export function hostElement( null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer)); if (firstTemplatePass) { - node.tNode.flags = TNodeFlags.isComponent; + tNode.flags = TNodeFlags.isComponent; if (def.diPublic) def.diPublic(def); tView.directives = [def]; } - return node; + return viewData[HEADER_OFFSET]; } /** @@ -1316,7 +1317,7 @@ export function elementEnd(): void { previousOrParentTNode = previousOrParentTNode.parent !; } ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Element); - currentQueries && (currentQueries = currentQueries.addNode(getPreviousOrParentNode())); + currentQueries && (currentQueries = currentQueries.addNode(previousOrParentTNode)); queueLifecycleHooks(previousOrParentTNode.flags, tView); elementDepthCount--; @@ -1366,12 +1367,12 @@ export function elementProperty( index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void { if (value === NO_CHANGE) return; const node = loadElement(index) as LElementNode; - const tNode = node.tNode; + const tNode = getTNode(index); // if tNode.inputs is undefined, a listener has created outputs, but inputs haven't // yet been checked if (tNode && tNode.inputs === undefined) { // mark inputs as checked - tNode.inputs = generatePropertyAliases(node.tNode.flags, BindingDirection.Input); + tNode.inputs = generatePropertyAliases(tNode.flags, BindingDirection.Input); } const inputData = tNode && tNode.inputs; @@ -1547,7 +1548,7 @@ function getStylingContext(index: number): StylingContext { let stylingContext = load(index); if (!Array.isArray(stylingContext)) { const lElement = stylingContext as any as LElementNode; - const tNode = lElement.tNode; + const tNode = getTNode(index); ngDevMode && assertDefined(tNode.stylingTemplate, 'getStylingContext() called before elementStyling()'); stylingContext = viewData[index + HEADER_OFFSET] = @@ -1657,12 +1658,12 @@ export function text(index: number, value?: any): void { viewData[BINDING_INDEX], tView.bindingStartIndex, 'text nodes should be created before any bindings'); ngDevMode && ngDevMode.rendererCreateTextNode++; - const textNode = createTextNode(value, renderer); - const node = createLNode(index, TNodeType.Element, textNode, null, null); + const textNative = createTextNode(value, renderer); + const tNode = createNodeAtIndex(index, TNodeType.Element, textNative, null, null); // Text nodes are self closing. isParent = false; - appendChild(getParentLNode(node), textNode, viewData); + appendChild(textNative, tNode, viewData); } /** @@ -1700,7 +1701,7 @@ export function textBinding(index: number, value: T | NO_CHANGE): void { export function directiveCreate( directiveDefIdx: number, directive: T, directiveDef: DirectiveDefInternal| ComponentDefInternal): T { - const hostNode = getPreviousOrParentNode(); + const hostNode = getPreviousOrParentNode() !; const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, hostNode); const isComponent = (directiveDef as ComponentDefInternal).template; @@ -1745,7 +1746,7 @@ function addComponentLogic( // 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). (hostNode as{data: LViewData}).data = componentView; - (componentView as LViewData)[HOST_NODE] = hostNode as LElementNode; + (componentView as LViewData)[HOST_NODE] = getPreviousOrParentTNode() as TElementNode; initChangeDetectorIfExisting(hostNode.nodeInjector, instance, componentView); @@ -1877,27 +1878,19 @@ function generateInitialInputs( /** * Creates a LContainer, either from a container instruction, or for a ViewContainerRef. * - * @param parentLNode the LNode in which the container's content will be rendered * @param currentView The parent view of the LContainer * @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case * @returns LContainer */ export function createLContainer( - parentLNode: LNode, currentView: LViewData, isForViewContainerRef?: boolean): LContainer { - ngDevMode && assertDefined(parentLNode, 'containers should have a parent'); - let renderParent = canInsertNativeNode(parentLNode, currentView) ? - parentLNode as LElementNode | LViewNode : - null; - if (renderParent && renderParent.tNode.type === TNodeType.View) { - renderParent = getParentLNode(renderParent as LViewNode) !.data[RENDER_PARENT]; - } + currentView: LViewData, isForViewContainerRef?: boolean): LContainer { return [ isForViewContainerRef ? null : 0, // active index currentView, // parent null, // next null, // queries [], // views - renderParent as LElementNode + null // renderParent, set after node creation ]; } @@ -1923,16 +1916,16 @@ export function template( tagName?: string | null, attrs?: TAttributes | null, localRefs?: string[] | null, localRefExtractor?: LocalRefExtractor) { // TODO: consider a separate node type for templates - const node = containerInternal(index, tagName || null, attrs || null, localRefs || null); + const tNode = containerInternal(index, tagName || null, attrs || null); if (firstTemplatePass) { - node.tNode.tViews = createTView( + tNode.tViews = createTView( -1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null); } - createDirectivesAndLocals(node, localRefs, localRefExtractor); - currentQueries && (currentQueries = currentQueries.addNode(node)); - queueLifecycleHooks(node.tNode.flags, tView); + createDirectivesAndLocals(localRefs, localRefExtractor); + currentQueries && (currentQueries = currentQueries.addNode(previousOrParentTNode)); + queueLifecycleHooks(tNode.flags, tView); isParent = false; } @@ -1946,30 +1939,28 @@ export function template( * @param index The index of the container in the data array */ export function container(index: number): void { - const node = containerInternal(index, null, null, null); - firstTemplatePass && (node.tNode.tViews = []); + const tNode = containerInternal(index, null, null); + firstTemplatePass && (tNode.tViews = []); isParent = false; } function containerInternal( - index: number, tagName: string | null, attrs: TAttributes | null, - localRefs: string[] | null): LContainerNode { + index: number, tagName: string | null, attrs: TAttributes | null): TNode { ngDevMode && assertEqual( viewData[BINDING_INDEX], tView.bindingStartIndex, 'container nodes should be created before any bindings'); - const previousNode: LNode = getPreviousOrParentNode(); - const currentParent = isParent ? previousNode : getParentLNode(previousNode) !; - const lContainer = createLContainer(currentParent, viewData); - + const lContainer = createLContainer(viewData); ngDevMode && ngDevMode.rendererCreateComment++; const comment = renderer.createComment(ngDevMode ? 'container' : ''); - const node = createLNode(index, TNodeType.Container, comment, tagName, attrs, lContainer); - appendChild(getParentLNode(node), comment, viewData); + const tNode = createNodeAtIndex(index, TNodeType.Container, comment, tagName, attrs, lContainer); + + lContainer[RENDER_PARENT] = getRenderParent(tNode, viewData); + appendChild(comment, tNode, viewData); // Containers are added to the current view tree instead of their embedded views // because views can be removed and re-inserted. - addToViewTree(viewData, index + HEADER_OFFSET, node.data); + addToViewTree(viewData, index + HEADER_OFFSET, lContainer); if (currentQueries) { // prepare place for matching nodes from views inserted into a given container @@ -1977,7 +1968,7 @@ function containerInternal( } ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container); - return node; + return tNode; } /** @@ -2021,7 +2012,7 @@ export function containerRefreshEnd(): void { // remove extra views at the end of the container while (nextIndex < container.data[VIEWS].length) { - removeView(container, nextIndex); + removeView(container, previousOrParentTNode as TContainerNode, nextIndex); } } @@ -2059,7 +2050,8 @@ function refreshDynamicEmbeddedViews(lViewData: LViewData) { * @returns index of a found view or -1 if not found */ function scanForView( - containerNode: LContainerNode, startIdx: number, viewBlockId: number): LViewData|null { + containerNode: LContainerNode, tContainerNode: TContainerNode, startIdx: number, + viewBlockId: number): LViewData|null { const views = containerNode.data[VIEWS]; for (let i = startIdx; i < views.length; i++) { const viewAtPositionId = views[i][TVIEW].id; @@ -2067,7 +2059,7 @@ function scanForView( return views[i]; } else if (viewAtPositionId < viewBlockId) { // found a view that should not be at this position - remove - removeView(containerNode, i); + removeView(containerNode, tContainerNode, i); } else { // found a view with id greater than the one we are searching for // which means that required view doesn't exist and can't be found at @@ -2094,12 +2086,11 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num ngDevMode && assertNodeType(containerTNode, TNodeType.Container); const lContainer = container.data; - let viewNode: LViewNode|null; - let viewToRender = scanForView(container, lContainer[ACTIVE_INDEX] !, viewBlockId); + let viewToRender = scanForView( + container, containerTNode as TContainerNode, lContainer[ACTIVE_INDEX] !, viewBlockId); if (viewToRender) { isParent = true; - viewNode = viewToRender[HOST_NODE] as LViewNode; enterView(viewToRender, viewToRender[TVIEW].node); } else { // When we create a new LView, we always reset the state of the instructions. @@ -2112,13 +2103,13 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num viewToRender[QUERIES] = lContainer[QUERIES] !.createView(); } - viewNode = createLNode(viewBlockId, TNodeType.View, null, null, null, viewToRender); + createNodeAtIndex(viewBlockId, TNodeType.View, null, null, null, viewToRender); enterView(viewToRender, viewToRender[TVIEW].node); } if (container) { if (creationMode) { // it is a new view, insert it into collection of views for a given container - insertView(container, viewNode, lContainer[ACTIVE_INDEX] !); + insertView(container, viewToRender, lContainer[ACTIVE_INDEX] !, -1); } lContainer[ACTIVE_INDEX] !++; } @@ -2153,7 +2144,7 @@ function getOrCreateEmbeddedTView( /** Marks the end of an embedded view. */ export function embeddedViewEnd(): void { - const viewHost = tView.node; + const viewHost = viewData[HOST_NODE]; refreshDescendantViews(); leaveView(viewData[PARENT] !); previousOrParentTNode = viewHost !; @@ -2208,15 +2199,15 @@ export function viewAttached(view: LViewData): boolean { * @param rawSelectors A collection of CSS selectors in the raw, un-parsed form */ export function projectionDef(selectors?: CssSelectorList[], textSelectors?: string[]): void { - const componentNode: LElementNode = findComponentHost(viewData); + const componentNode = findComponentView(viewData)[HOST_NODE] as TElementNode; - if (!componentNode.tNode.projection) { + if (!componentNode.projection) { const noOfNodeBuckets = selectors ? selectors.length + 1 : 1; - const pData: (TNode | null)[] = componentNode.tNode.projection = + const pData: (TNode | null)[] = componentNode.projection = new Array(noOfNodeBuckets).fill(null); const tails: (TNode | null)[] = pData.slice(); - let componentChild = componentNode.tNode.child; + let componentChild: TNode|null = componentNode.child; while (componentChild !== null) { const bucketIndex = @@ -2243,7 +2234,7 @@ export function projectionDef(selectors?: CssSelectorList[], textSelectors?: str * a new array each time the function is called. Instead the array will be * re-used by each invocation. This works because the function is not reentrant. */ -const projectionNodeStack: LProjectionNode[] = []; +const projectionNodeStack: (LViewData | TNode)[] = []; /** * Inserts previously re-distributed projected nodes. This instruction must be preceded by a call @@ -2255,63 +2246,52 @@ const projectionNodeStack: LProjectionNode[] = []; * - 1 based index of the selector from the {@link projectionDef} */ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?: string[]): void { - const node = createLNode(nodeIndex, TNodeType.Projection, null, null, attrs || null, null); + const tProjectionNode = + createNodeAtIndex(nodeIndex, TNodeType.Projection, null, null, attrs || null, null); // We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views. - if (node.tNode.projection === null) node.tNode.projection = selectorIndex; + if (tProjectionNode.projection === null) tProjectionNode.projection = selectorIndex; // `` has no content isParent = false; // re-distribution of projectable nodes is stored on a component's view level - const parent = getParentLNode(node); - - const componentNode = findComponentHost(viewData); - let nodeToProject = (componentNode.tNode.projection as(TNode | null)[])[selectorIndex]; - let projectedView = componentNode.view; + const componentView = findComponentView(viewData); + const componentNode = componentView[HOST_NODE] as TElementNode; + let nodeToProject = (componentNode.projection as(TNode | null)[])[selectorIndex]; + let projectedView = componentView[PARENT] !; let projectionNodeIndex = -1; - let renderParent: LElementNode|null = null; while (nodeToProject) { if (nodeToProject.type === TNodeType.Projection) { // This node is re-projected, so we must go up the tree to get its projected nodes. - const currentComponentHost = findComponentHost(projectedView); - const firstProjectedNode = (currentComponentHost.tNode.projection as( - TNode | null)[])[nodeToProject.projection as number]; + const currentComponentView = findComponentView(projectedView); + const currentComponentHost = currentComponentView[HOST_NODE] as TElementNode; + const firstProjectedNode = + (currentComponentHost.projection as(TNode | null)[])[nodeToProject.projection as number]; if (firstProjectedNode) { - projectionNodeStack[++projectionNodeIndex] = projectedView[nodeToProject.index]; + projectionNodeStack[++projectionNodeIndex] = nodeToProject; + projectionNodeStack[++projectionNodeIndex] = projectedView; + nodeToProject = firstProjectedNode; - projectedView = currentComponentHost.view; + projectedView = currentComponentView[PARENT] !; continue; } } else { - const lNode = projectedView[nodeToProject.index]; + const lNode = projectedView[nodeToProject.index] as LTextNode | LElementNode | LContainerNode; // This flag must be set now or we won't know that this node is projected // if the nodes are inserted into a container later. - lNode.tNode.flags |= TNodeFlags.isProjected; - if (canInsertNativeNode(parent, viewData)) { - let grandparent: LContainerNode; - if (renderParent == null) { - renderParent = parent.tNode.type === TNodeType.View ? - (grandparent = getParentLNode(parent) as LContainerNode) && - grandparent.data[RENDER_PARENT] ! : - parent as LElementNode; - } + nodeToProject.flags |= TNodeFlags.isProjected; - appendProjectedNode( - lNode as LTextNode | LElementNode | LContainerNode, parent, viewData, renderParent, - projectedView); - } + appendProjectedNode(lNode, nodeToProject, tProjectionNode, viewData, projectedView); } // If we are finished with a list of re-projected nodes, we need to get // back to the root projection node that was re-projected. - if (nodeToProject.next === null && projectedView !== componentNode.view) { - // move down into the view of the component we're projecting right now - const lNode = projectionNodeStack[projectionNodeIndex--]; - nodeToProject = lNode.tNode; - projectedView = lNode.view; + if (nodeToProject.next === null && projectedView !== componentView[PARENT] !) { + projectedView = projectionNodeStack[projectionNodeIndex--] as LViewData; + nodeToProject = projectionNodeStack[projectionNodeIndex--] as TNode; } nodeToProject = nodeToProject.next; } @@ -2383,12 +2363,12 @@ export function wrapListenerWithDirtyAndDefault( export function markViewDirty(view: LViewData): void { let currentView: LViewData = view; - while (currentView[PARENT] != null) { + while (currentView && !(currentView[FLAGS] & LViewFlags.IsRoot)) { currentView[FLAGS] |= LViewFlags.Dirty; currentView = currentView[PARENT] !; } currentView[FLAGS] |= LViewFlags.Dirty; - ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext'); + ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext should be defined'); scheduleTick(currentView[CONTEXT] as RootContext); } @@ -2451,7 +2431,7 @@ function tickRootContext(rootContext: RootContext) { export function getRootView(component: any): LViewData { ngDevMode && assertDefined(component, 'component'); let lViewData = readPatchedLViewData(component) !; - while (lViewData[PARENT]) { + while (lViewData && !(lViewData[FLAGS] & LViewFlags.IsRoot)) { lViewData = lViewData[PARENT] !; } return lViewData; @@ -2523,7 +2503,7 @@ export function checkNoChangesInRootView(lViewData: LViewData): void { /** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */ export function detectChangesInternal(hostView: LViewData, component: T) { const hostTView = hostView[TVIEW]; - const oldView = enterView(hostView, null); + const oldView = enterView(hostView, hostView[HOST_NODE]); const templateFn = hostTView.template !; const viewQuery = hostTView.viewQuery; @@ -2790,6 +2770,10 @@ export function loadElement(index: number): LElementNode { return loadElementInternal(index, viewData); } +export function getTNode(index: number): TNode { + return tView.data[index + HEADER_OFFSET] as TNode; +} + /** Gets the current binding value. */ export function getBinding(bindingIndex: number): any { ngDevMode && assertDataInRange(viewData[bindingIndex]); diff --git a/packages/core/src/render3/interfaces/container.ts b/packages/core/src/render3/interfaces/container.ts index c8cfd9e6ca..e4559bb9a0 100644 --- a/packages/core/src/render3/interfaces/container.ts +++ b/packages/core/src/render3/interfaces/container.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {LElementNode, LViewNode} from './node'; +import {LElementNode} from './node'; import {LQueries} from './query'; import {LViewData, NEXT, PARENT, QUERIES} from './view'; diff --git a/packages/core/src/render3/interfaces/injector.ts b/packages/core/src/render3/interfaces/injector.ts index 7d76cac0c1..9363ff92e3 100644 --- a/packages/core/src/render3/interfaces/injector.ts +++ b/packages/core/src/render3/interfaces/injector.ts @@ -11,7 +11,7 @@ import {ElementRef} from '../../linker/element_ref'; import {TemplateRef} from '../../linker/template_ref'; import {ViewContainerRef} from '../../linker/view_container_ref'; -import {LContainerNode, LElementContainerNode, LElementNode} from './node'; +import {LContainerNode, LElementContainerNode, LElementNode, TContainerNode, TElementNode, TNode} from './node'; export interface LInjector { /** @@ -28,6 +28,8 @@ export interface LInjector { */ readonly node: LElementNode|LElementContainerNode|LContainerNode; + readonly tNode: TNode; + /** * The following bloom filter determines whether a directive is available * on the associated node or not. This prevents us from searching the directives diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts index bea7adca1e..af47f0efd3 100644 --- a/packages/core/src/render3/interfaces/node.ts +++ b/packages/core/src/render3/interfaces/node.ts @@ -81,7 +81,6 @@ export interface LNode { */ readonly data: LViewData|LContainer|null; - /** * Each node belongs to a view. * @@ -92,12 +91,6 @@ export interface LNode { /** The injector associated with this node. Necessary for DI. */ nodeInjector: LInjector|null; - /** - * Pointer to the corresponding TNode object, which stores static - * data about this node. - */ - tNode: TNode; - /** * A pointer to an LContainerNode created by directives requesting ViewContainerRef */ @@ -385,13 +378,13 @@ export interface TNode { export interface TElementNode extends TNode { /** Index in the data[] array */ index: number; - child: TElementNode|TTextNode|TContainerNode|TProjectionNode|null; + child: TElementNode|TTextNode|TElementContainerNode|TContainerNode|TProjectionNode|null; /** * Element nodes will have parents unless they are the first node of a component or * embedded view (which means their parent is in a different view and must be - * retrieved using LView.node). + * retrieved using viewData[HOST_NODE]). */ - parent: TElementNode|null; + parent: TElementNode|TElementContainerNode|null; tViews: null; /** @@ -412,7 +405,7 @@ export interface TTextNode extends TNode { * embedded view (which means their parent is in a different view and must be * retrieved using LView.node). */ - parent: TElementNode|null; + parent: TElementNode|TElementContainerNode|null; tViews: null; projection: null; } @@ -434,16 +427,27 @@ export interface TContainerNode extends TNode { * - They are the first node of a component or embedded view * - They are dynamically created */ - parent: TElementNode|null; + parent: TElementNode|TElementContainerNode|null; tViews: TView|TView[]|null; projection: null; } + +/** Static data for an LElementContainerNode */ +export interface TElementContainerNode extends TNode { + /** Index in the LViewData[] array. */ + index: number; + child: TElementNode|TTextNode|TContainerNode|TElementContainerNode|TProjectionNode|null; + parent: TElementNode|TElementContainerNode|null; + tViews: null; + projection: null; +} + /** Static data for an LViewNode */ export interface TViewNode extends TNode { /** If -1, it's a dynamically created view. Otherwise, it is the view block ID. */ index: number; - child: TElementNode|TTextNode|TContainerNode|TProjectionNode|null; + child: TElementNode|TTextNode|TElementContainerNode|TContainerNode|TProjectionNode|null; parent: TContainerNode|null; tViews: null; projection: null; @@ -458,7 +462,7 @@ export interface TProjectionNode extends TNode { * or embedded view (which means their parent is in a different view and must be * retrieved using LView.node). */ - parent: TElementNode|null; + parent: TElementNode|TElementContainerNode|null; tViews: null; /** Index of the projection node. (See TNode.projection for more info.) */ @@ -536,4 +540,4 @@ export type LNodeWithLocalRefs = LContainerNode | LElementNode | LElementContain * - `
` - `nativeDivEl` should point to the native `
` element; * - `` - `tplRef` should point to the `TemplateRef` instance; */ -export type LocalRefExtractor = (lNode: LNodeWithLocalRefs) => any; \ No newline at end of file +export type LocalRefExtractor = (lNode: LNodeWithLocalRefs, tNode: TNode) => any; diff --git a/packages/core/src/render3/interfaces/query.ts b/packages/core/src/render3/interfaces/query.ts index 21950de575..0d9e6c262a 100644 --- a/packages/core/src/render3/interfaces/query.ts +++ b/packages/core/src/render3/interfaces/query.ts @@ -8,7 +8,7 @@ import {QueryList} from '../../linker'; import {Type} from '../../type'; -import {LNode} from './node'; +import {TNode} from './node'; /** Used for tracking queries (e.g. ViewChild, ContentChild). */ export interface LQueries { @@ -30,10 +30,10 @@ export interface LQueries { clone(): LQueries; /** - * Notify `LQueries` that a new `LNode` has been created and needs to be added to query results + * Notify `LQueries` that a new `TNode` has been created and needs to be added to query results * if matching query predicate. */ - addNode(node: LNode): LQueries|null; + addNode(tNode: TNode): LQueries|null; /** * Notify `LQueries` that a new LContainer was added to ivy data structures. As a result we need diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 4855f2aaa7..479c9d6f38 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -94,15 +94,16 @@ export interface LViewData extends Array { [FLAGS]: LViewFlags; /** - * Pointer to the `LViewNode` or `LElementNode` which represents the root of the view. + * Pointer to the `TViewNode` or `TElementNode` which represents the root of the view. * - * If `LViewNode`, this is an embedded view of a container. We need this to be able to + * If `TViewNode`, this is an embedded view of a container. We need this to be able to * efficiently find the `LViewNode` when inserting the view into an anchor. * - * If `LElementNode`, this is the LView of a component. + * If `TElementNode`, this is the LView of a component. + * + * If null, this is the root view of an application (root component is in this view). */ - // TODO(kara): Replace with index - [HOST_NODE]: LViewNode|LElementNode; + [HOST_NODE]: TViewNode|TElementNode|null; /** * The binding index we should access next. @@ -213,16 +214,16 @@ export const enum LViewFlags { * back into the parent view, `data` will be defined and `creationMode` will be * improperly reported as false. */ - CreationMode = 0b000001, + CreationMode = 0b0000001, /** Whether this view has default change detection strategy (checks always) or onPush */ - CheckAlways = 0b000010, + CheckAlways = 0b0000010, /** Whether or not this view is currently dirty (needing check) */ - Dirty = 0b000100, + Dirty = 0b0000100, /** Whether or not this view is currently attached to change detection tree. */ - Attached = 0b001000, + Attached = 0b0001000, /** * Whether or not the init hooks have run. @@ -231,10 +232,13 @@ export const enum LViewFlags { * runs OR the first cR() instruction that runs (so inits are run for the top level view before * any embedded views). */ - RunInit = 0b010000, + RunInit = 0b0010000, /** Whether or not this view is destroyed. */ - Destroyed = 0b100000, + Destroyed = 0b0100000, + + /** Whether or not this view is the root view */ + IsRoot = 0b1000000, } /** diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index e66464182b..31ebdc03e4 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -10,51 +10,66 @@ import {assertDefined} from './assert'; import {attachPatchData} from './context_discovery'; import {callHooks} from './hooks'; import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container'; -import {LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; +import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TTextNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; -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 {readElementValue, stringify} from './util'; +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 {assertNodeType} from './node_assert'; +import {isComponent, readElementValue, stringify} from './util'; const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5; -/** Retrieves the first child of a given node */ -export function getChildLNode(node: LNode): LNode|null { - if (node.tNode.child) { - const viewData = node.tNode.type === TNodeType.View ? node.data as LViewData : node.view; - return readElementValue(viewData[node.tNode.child.index]); - } - return null; -} - /** Retrieves the parent LNode of a given node. */ -export function getParentLNode( - node: LContainerNode | LElementNode | LElementContainerNode | LTextNode | - LProjectionNode): LElementNode|LElementContainerNode|LViewNode; -export function getParentLNode(node: LViewNode): LContainerNode|null; -export function getParentLNode(node: LElementContainerNode): LElementNode|LElementContainerNode| - LViewNode; -export function getParentLNode(node: LNode): LElementNode|LElementContainerNode|LContainerNode| - LViewNode|null; -export function getParentLNode(node: LNode): LElementNode|LElementContainerNode|LContainerNode| - LViewNode|null { - if (node.tNode.index === -1 && node.tNode.type === TNodeType.View) { - // This is a dynamically created view inside a dynamic container. - // If the host index is -1, the view has not yet been inserted, so it has no parent. - const containerHostIndex = (node.data as LViewData)[CONTAINER_INDEX]; - return containerHostIndex === -1 ? null : node.view[containerHostIndex].dynamicLContainerNode; - } - const parent = node.tNode.parent; - return readElementValue(parent ? node.view[parent.index] : node.view[HOST_NODE]); +export function getParentLNode(tNode: TNode, currentView: LViewData): LElementNode| + LElementContainerNode|LContainerNode|null { + return tNode.parent == null ? getHostElementNode(currentView) : + readElementValue(currentView[tNode.parent.index]); } /** - * Retrieves render parent LElementNode for a given view. - * Might be null if a view is not yet attatched to any container. + * Gets the host LElementNode given a view. Will return null if the host element is an + * LViewNode, since they are being phased out. */ -function getRenderParent(viewNode: LViewNode): LElementNode|null { - const container = getParentLNode(viewNode); +export function getHostElementNode(currentView: LViewData): LElementNode|null { + const hostTNode = currentView[HOST_NODE] as TElementNode; + return hostTNode && hostTNode.type !== TNodeType.View ? + readElementValue(currentView[PARENT] ![hostTNode.index]) : + null; +} + +/** + * Gets the parent LNode if it's not a view. If it's a view, it will instead return the view's + * parent container node. + */ +export function getParentOrContainerNode(tNode: TNode, currentView: LViewData): LElementNode| + LElementContainerNode|LContainerNode|null { + const parentTNode = tNode.parent || currentView[HOST_NODE]; + return parentTNode && parentTNode.type === TNodeType.View ? + getContainerNode(parentTNode, currentView) : + getParentLNode(tNode, currentView); +} + +export function getContainerNode(tNode: TNode, embeddedView: LViewData): LContainerNode|null { + if (tNode.index === -1) { + // This is a dynamically created view inside a dynamic container. + // If the host index is -1, the view has not yet been inserted, so it has no parent. + const containerHostIndex = embeddedView[CONTAINER_INDEX]; + return containerHostIndex > -1 ? + embeddedView[PARENT] ![containerHostIndex].dynamicLContainerNode : + null; + } else { + // This is a inline view node (e.g. embeddedViewStart) + return getParentLNode(tNode, embeddedView[PARENT] !) as LContainerNode; + } +} + + +/** + * Retrieves render parent LElementNode for a given view. + * Might be null if a view is not yet attached to any container. + */ +export function getContainerRenderParent(tViewNode: TViewNode, view: LViewData): LElementNode|null { + const container = getContainerNode(tViewNode, view); return container ? container.data[RENDER_PARENT] : null; } @@ -129,16 +144,17 @@ function walkTNodeTree( lContainerNode.native; } } else if (tNode.type === TNodeType.Projection) { - const componentHost = findComponentHost(currentView !); + const componentView = findComponentView(currentView !); + const componentHost = componentView[HOST_NODE] as TElementNode; const head: TNode|null = - (componentHost.tNode.projection as(TNode | null)[])[tNode.projection as number]; + (componentHost.projection as(TNode | null)[])[tNode.projection as number]; // Must store both the TNode and the view because this projection node could be nested // deeply inside embedded views, and we need to get back down to this particular nested view. projectionNodeStack[++projectionNodeIndex] = tNode; projectionNodeStack[++projectionNodeIndex] = currentView !; if (head) { - currentView = (componentHost.data as LViewData)[PARENT] !; + currentView = componentView[PARENT] !; nextTNode = currentView[TVIEW].data[head.index] as TNode; } } else { @@ -191,19 +207,16 @@ function walkTNodeTree( * @param lViewData LViewData for which we want a host element node * @returns The host node */ -export function findComponentHost(lViewData: LViewData): LElementNode { - let viewRootLNode = lViewData[HOST_NODE]; +export function findComponentView(lViewData: LViewData): LViewData { + let rootTNode = lViewData[HOST_NODE]; - while (viewRootLNode.tNode.type === TNodeType.View) { - ngDevMode && assertDefined(lViewData[PARENT], 'lViewData.parent'); + while (rootTNode && rootTNode.type === TNodeType.View) { + ngDevMode && assertDefined(lViewData[PARENT], 'viewData.parent'); lViewData = lViewData[PARENT] !; - viewRootLNode = lViewData[HOST_NODE]; + rootTNode = lViewData[HOST_NODE]; } - ngDevMode && assertNodeType(viewRootLNode.tNode, TNodeType.Element); - ngDevMode && assertDefined(viewRootLNode.data, 'node.data'); - - return viewRootLNode as LElementNode; + return lViewData; } /** @@ -239,24 +252,20 @@ export function createTextNode(value: any, renderer: Renderer3): RText { * to propagate deeply into the nested containers to remove all elements in the * views beneath it. * - * @param container The container to which the root view belongs * @param viewToWalk The view from which elements should be added or removed * @param insertMode Whether or not elements should be added (if false, removing) * @param beforeNode The node before which elements should be added, if insert mode */ export function addRemoveViewFromContainer( - container: LContainerNode, viewToWalk: LViewData, insertMode: true, - beforeNode: RNode | null): void; + viewToWalk: LViewData, insertMode: true, beforeNode: RNode | null): void; +export function addRemoveViewFromContainer(viewToWalk: LViewData, insertMode: false): void; export function addRemoveViewFromContainer( - container: LContainerNode, viewToWalk: LViewData, insertMode: false): void; -export function addRemoveViewFromContainer( - container: LContainerNode, viewToWalk: LViewData, insertMode: boolean, - beforeNode?: RNode | null): void { - const parentNode = container.data[RENDER_PARENT]; + viewToWalk: LViewData, insertMode: boolean, beforeNode?: RNode | null): void { + const parentNode = getContainerRenderParent(viewToWalk[TVIEW].node as TViewNode, viewToWalk); const parent = parentNode ? parentNode.native : null; ngDevMode && assertNodeType(viewToWalk[TVIEW].node as TNode, TNodeType.View); if (parent) { - const renderer = container.view[RENDERER]; + const renderer = viewToWalk[RENDERER]; walkTNodeTree( viewToWalk, insertMode ? WalkTNodeTreeAction.Insert : WalkTNodeTreeAction.Detach, renderer, parentNode, beforeNode); @@ -323,10 +332,10 @@ export function destroyViewTree(rootView: LViewData): void { * @param index The index at which to insert the view * @returns The inserted view */ -export function insertView(container: LContainerNode, viewNode: LViewNode, index: number) { +export function insertView( + container: LContainerNode, lView: LViewData, index: number, containerIndex: number) { const state = container.data; const views = state[VIEWS]; - const lView = viewNode.data as LViewData; if (index > 0) { // This is a new view, we need to add it to the children. @@ -341,11 +350,11 @@ export function insertView(container: LContainerNode, viewNode: LViewNode, index lView[NEXT] = null; } - // Dynamically inserted views need a reference to their parent container'S host so it's + // Dynamically inserted views need a reference to their parent container's host so it's // possible to jump from a view to its container's next when walking the node tree. - if (viewNode.tNode.index === -1) { - lView[CONTAINER_INDEX] = container.tNode.parent !.index; - (viewNode as{view: LViewData}).view = container.view; + if (containerIndex > -1) { + lView[CONTAINER_INDEX] = containerIndex; + lView[PARENT] = container.view; } // Notify query that a new view has been added @@ -367,23 +376,22 @@ export function insertView(container: LContainerNode, viewNode: LViewNode, index * @param removeIndex The index of the view to detach * @returns The detached view */ -export function detachView(container: LContainerNode, removeIndex: number) { +export function detachView(container: LContainerNode, removeIndex: number, detached: boolean) { const views = container.data[VIEWS]; const viewToDetach = views[removeIndex]; - const viewNode = viewToDetach[HOST_NODE] as LViewNode; if (removeIndex > 0) { views[removeIndex - 1][NEXT] = viewToDetach[NEXT] as LViewData; } views.splice(removeIndex, 1); - if (!container.tNode.detached) { - addRemoveViewFromContainer(container, viewToDetach, false); + if (!detached) { + addRemoveViewFromContainer(viewToDetach, false); } if (viewToDetach[QUERIES]) { viewToDetach[QUERIES] !.removeView(); } viewToDetach[CONTAINER_INDEX] = -1; - (viewNode as{view: LViewData | null}).view = null; + viewToDetach[PARENT] = null; // Unsets the attached flag viewToDetach[FLAGS] &= ~LViewFlags.Attached; } @@ -395,10 +403,11 @@ export function detachView(container: LContainerNode, removeIndex: number) { * @param removeIndex The index of the view to remove * @returns The removed view */ -export function removeView(container: LContainerNode, removeIndex: number) { - const viewToRemove = container.data[VIEWS][removeIndex]; - destroyLView(viewToRemove); - detachView(container, removeIndex); +export function removeView( + container: LContainerNode, tContainer: TContainerNode, removeIndex: number) { + const view = container.data[VIEWS][removeIndex]; + destroyLView(view); + detachView(container, removeIndex, !!tContainer.detached); } /** Gets the child of the given LViewData */ @@ -440,11 +449,12 @@ export function destroyLView(view: LViewData) { */ export function getParentState(state: LViewData | LContainer, rootView: LViewData): LViewData| LContainer|null { - let node; - if ((node = (state as LViewData) ![HOST_NODE]) && node.tNode.type === TNodeType.View) { + let tNode; + if (state.length >= HEADER_OFFSET && (tNode = (state as LViewData) ![HOST_NODE]) && + tNode.type === TNodeType.View) { // if it's an embedded view, the state needs to go up to the container, in case the // container has a next - return getParentLNode(node) !.data as any; + return getContainerNode(tNode, state as LViewData) !.data as any; } else { // otherwise, use parent view for containers or component views return state[PARENT] === rootView ? null : state[PARENT]; @@ -512,17 +522,24 @@ function executePipeOnDestroys(viewData: LViewData): void { } } -function canInsertNativeChildOfElement(parent: LElementNode, currentView: LViewData): boolean { - if (parent.view !== currentView) { - // If the Parent view is not the same as current view than we are inserting across - // Views. This happens when we insert a root element of the component view into - // the component host element and it should always be eager. - return true; +export function getRenderParent(tNode: TNode, currentView: LViewData): LElementNode|null { + if (canInsertNativeNode(tNode, currentView)) { + const hostTNode = currentView[HOST_NODE]; + return tNode.parent == null && hostTNode !.type === TNodeType.View ? + getContainerRenderParent(hostTNode as TViewNode, currentView) : + getParentLNode(tNode, currentView) as LElementNode; } - // Parent elements can be a component which may have projection. - if (parent.data === null) { - // Parent is a regular non-component element. We should eagerly insert into it - // since we know that this relationship will never be broken. + return null; +} + +function canInsertNativeChildOfElement(tNode: TNode): boolean { + // If the parent is null, then we are inserting across views. This happens when we + // insert a root element of the component view into the component host element and it + // should always be eager. + if (tNode.parent == null || + // We should also eagerly insert if the parent is a regular, non-component element + // since we know that this relationship will never be broken. + tNode.parent.type === TNodeType.Element && !(tNode.parent.flags & TNodeFlags.isComponent)) { return true; } @@ -534,26 +551,21 @@ function canInsertNativeChildOfElement(parent: LElementNode, currentView: LViewD /** * We might delay insertion of children for a given view if it is disconnected. - * This might happen for 2 main reason: - * - view is not inserted into any container (view was created but not iserted yet) + * This might happen for 2 main reasons: + * - view is not inserted into any container (view was created but not inserted yet) * - view is inserted into a container but the container itself is not inserted into the DOM * (container might be part of projection or child of a view that is not inserted yet). * - * In other words we can insert children of a given view this view was inserted into a container and - * the container itself has it render parent determined. + * In other words we can insert children of a given view if this view was inserted into a container + * and + * the container itself has its render parent determined. */ -function canInsertNativeChildOfView(parent: LViewNode): boolean { - ngDevMode && assertNodeType(parent.tNode, TNodeType.View); - +function canInsertNativeChildOfView(viewTNode: TViewNode, view: LViewData): boolean { // Because we are inserting into a `View` the `View` may be disconnected. - const grandParentContainer = getParentLNode(parent) as LContainerNode; - if (grandParentContainer == null) { - // The `View` is not inserted into a `Container` we have to delay insertion. - return false; - } - ngDevMode && assertNodeType(grandParentContainer.tNode, TNodeType.Container); - if (grandParentContainer.data[RENDER_PARENT] == null) { - // The parent `Container` itself is disconnected. So we have to delay. + const container = getContainerNode(viewTNode, view) !; + if (container == null || container.data[RENDER_PARENT] == null) { + // The `View` is not inserted into a `Container` or the parent `Container` + // itself is disconnected. So we have to delay. return false; } @@ -580,31 +592,21 @@ function canInsertNativeChildOfView(parent: LViewNode): boolean { * @param currentView Current LView being processed. * @return boolean Whether the child should be inserted now (or delayed until later). */ -export function canInsertNativeNode(parent: LNode, currentView: LViewData): boolean { - // We can only insert into a Component or View. Any other type should be an Error. - ngDevMode && assertNodeOfPossibleTypes( - parent.tNode, TNodeType.Element, TNodeType.ElementContainer, TNodeType.View); +export function canInsertNativeNode(tNode: TNode, currentView: LViewData): boolean { + let currentNode = tNode; + let parent: TNode|null = tNode.parent; - if (parent.tNode.type === TNodeType.Element) { - // Parent is a regular element or a component - return canInsertNativeChildOfElement(parent as LElementNode, currentView); - } else if (parent.tNode.type === TNodeType.ElementContainer) { - // Parent is an element container (ng-container). - // Its grand-parent might be an element, view or a sequence of ng-container parents. - let grandParent = getParentLNode(parent); - while (grandParent !== null && grandParent.tNode.type === TNodeType.ElementContainer) { - grandParent = getParentLNode(grandParent); - } - if (grandParent === null) { - return false; - } else if (grandParent.tNode.type === TNodeType.Element) { - return canInsertNativeChildOfElement(grandParent as LElementNode, currentView); - } else { - return canInsertNativeChildOfView(grandParent as LViewNode); - } + if (tNode.parent && tNode.parent.type === TNodeType.ElementContainer) { + currentNode = getHighestElementContainer(tNode); + parent = currentNode.parent; + } + if (parent === null) parent = currentView[HOST_NODE]; + + if (parent && parent.type === TNodeType.View) { + return canInsertNativeChildOfView(parent as TViewNode, currentView); } else { - // Parent is a View. - return canInsertNativeChildOfView(parent as LViewNode); + // Parent is a regular element or a component + return canInsertNativeChildOfElement(currentNode); } } @@ -627,58 +629,79 @@ function nativeInsertBefore( * * The element insertion might be delayed {@link canInsertNativeNode}. * - * @param parent The parent to which to append the child - * @param child The child that should be appended + * @param childEl The child that should be appended + * @param childTNode The TNode of the child element * @param currentView The current LView * @returns Whether or not the child was appended */ -export function appendChild(parent: LNode, child: RNode | null, currentView: LViewData): boolean { - if (child !== null && canInsertNativeNode(parent, currentView)) { +export function appendChild( + childEl: RNode | null, childTNode: TNode, currentView: LViewData): boolean { + const parentLNode = getParentLNode(childTNode, currentView); + const parentEl = parentLNode ? parentLNode.native : null; + + if (childEl !== null && canInsertNativeNode(childTNode, currentView)) { const renderer = currentView[RENDERER]; - if (parent.tNode.type === TNodeType.View) { - const container = getParentLNode(parent) as LContainerNode; + const parentTNode: TNode = childTNode.parent || currentView[HOST_NODE] !; + + if (parentTNode.type === TNodeType.View) { + const container = getContainerNode(parentTNode, currentView) as LContainerNode; const renderParent = container.data[RENDER_PARENT]; const views = container.data[VIEWS]; const index = views.indexOf(currentView); - const beforeNode = index + 1 < views.length ? - (getChildLNode(views[index + 1][HOST_NODE]) !).native : - container.native; - nativeInsertBefore(renderer, renderParent !.native, child, beforeNode); - } else if (parent.tNode.type === TNodeType.ElementContainer) { - const beforeNode = parent.native; - let grandParent = getParentLNode(parent as LElementContainerNode); - while (grandParent.tNode.type === TNodeType.ElementContainer) { - grandParent = getParentLNode(grandParent as LElementContainerNode); - } - if (grandParent.tNode.type === TNodeType.View) { - const renderParent = getRenderParent(grandParent as LViewNode); - nativeInsertBefore(renderer, renderParent !.native, child, beforeNode); - } else { - nativeInsertBefore(renderer, (grandParent as LElementNode).native, child, beforeNode); - } + nativeInsertBefore( + renderer, renderParent !.native, childEl, getBeforeNodeForView(index, views, container)); + } else if (parentTNode.type === TNodeType.ElementContainer) { + let elementContainer = getHighestElementContainer(childTNode); + let node: LElementNode = getRenderParent(elementContainer, currentView) !; + nativeInsertBefore(renderer, node.native, childEl, parentEl); } else { - isProceduralRenderer(renderer) ? renderer.appendChild(parent.native !as RElement, child) : - parent.native !.appendChild(child); + isProceduralRenderer(renderer) ? renderer.appendChild(parentEl !as RElement, childEl) : + parentEl !.appendChild(childEl); } return true; } return false; } +/** + * Gets the top-level ng-container if ng-containers are nested. + * + * @param ngContainer The TNode of the starting ng-container + * @returns tNode The TNode of the highest level ng-container + */ +function getHighestElementContainer(ngContainer: TNode): TNode { + while (ngContainer.parent != null && ngContainer.parent.type === TNodeType.ElementContainer) { + ngContainer = ngContainer.parent; + } + return ngContainer; +} + +export function getBeforeNodeForView(index: number, views: LViewData[], container: LContainerNode) { + if (index + 1 < views.length) { + const view = views[index + 1] as LViewData; + const viewTNode = view[HOST_NODE] as TViewNode; + return viewTNode.child ? readElementValue(view[viewTNode.child.index]).native : + container.native; + } else { + return container.native; + } +} + /** * Removes the `child` element of the `parent` from the DOM. * - * @param parent The parent from which to remove the child + * @param parentEl The parent element from which to remove the child * @param child The child that should be removed * @param currentView The current LView * @returns Whether or not the child was removed */ -export function removeChild(parent: LNode, child: RNode | null, currentView: LViewData): boolean { - if (child !== null && canInsertNativeNode(parent, currentView)) { +export function removeChild(tNode: TNode, child: RNode | null, currentView: LViewData): boolean { + const parentNative = getParentLNode(tNode, currentView) !.native as RElement; + if (child !== null && canInsertNativeNode(tNode, currentView)) { // We only remove the element if not in View or not projected. const renderer = currentView[RENDERER]; - isProceduralRenderer(renderer) ? renderer.removeChild(parent.native as RElement, child) : - parent.native !.removeChild(child); + isProceduralRenderer(renderer) ? renderer.removeChild(parentNative as RElement, child) : + parentNative !.removeChild(child); return true; } return false; @@ -688,47 +711,49 @@ export function removeChild(parent: LNode, child: RNode | null, currentView: LVi * Appends a projected node to the DOM, or in the case of a projected container, * appends the nodes from all of the container's active views to the DOM. * - * @param node The node to process - * @param currentParent The last parent element to be processed + * @param projectedLNode The node to process + * @param parentNode The last parent element to be processed + * @param tProjectionNode * @param currentView Current LView + * @param projectionView Projection view */ export function appendProjectedNode( - node: LElementNode | LElementContainerNode | LTextNode | LContainerNode, - currentParent: LElementNode | LElementContainerNode | LViewNode, currentView: LViewData, - renderParent: LElementNode, parentView: LViewData): void { - appendChild(currentParent, node.native, currentView); + projectedLNode: LElementNode | LElementContainerNode | LTextNode | LContainerNode, + projectedTNode: TNode, tProjectionNode: TNode, currentView: LViewData, + projectionView: LViewData): void { + appendChild(projectedLNode.native, tProjectionNode, currentView); // the projected contents are processed while in the shadow view (which is the currentView) // therefore we need to extract the view where the host element lives since it's the // logical container of the content projected views - attachPatchData(node.native, parentView); + attachPatchData(projectedLNode.native, projectionView); - if (node.tNode.type === TNodeType.Container) { + const renderParent = getRenderParent(tProjectionNode, currentView); + + if (projectedTNode.type === TNodeType.Container) { // The node we are adding is a container and we are adding it to an element which // is not a component (no more re-projection). // Alternatively a container is projected at the root of a component's template // and can't be re-projected (as not content of any component). // Assign the final projection location in those cases. - const lContainer = (node as LContainerNode).data; + const lContainer = (projectedLNode as LContainerNode).data; lContainer[RENDER_PARENT] = renderParent; const views = lContainer[VIEWS]; for (let i = 0; i < views.length; i++) { - addRemoveViewFromContainer(node as LContainerNode, views[i], true, node.native); + addRemoveViewFromContainer(views[i], true, projectedLNode.native); } - } else if (node.tNode.type === TNodeType.ElementContainer) { - let ngContainerChild = getChildLNode(node as LElementContainerNode); - const parentView = currentView[PARENT] !; - while (ngContainerChild) { + } else if (projectedTNode.type === TNodeType.ElementContainer) { + let ngContainerChildTNode: TNode|null = projectedTNode.child as TNode; + while (ngContainerChildTNode) { + let ngContainerChild = readElementValue(projectionView[ngContainerChildTNode.index]); appendProjectedNode( ngContainerChild as LElementNode | LElementContainerNode | LTextNode | LContainerNode, - currentParent, currentView, renderParent, parentView); - ngContainerChild = ngContainerChild.tNode.next ? - readElementValue(ngContainerChild.view[ngContainerChild.tNode.next.index]) : - null; + ngContainerChildTNode, tProjectionNode, currentView, projectionView); + ngContainerChildTNode = ngContainerChildTNode.next; } } - if (node.dynamicLContainerNode) { - node.dynamicLContainerNode.data[RENDER_PARENT] = renderParent; - appendChild(currentParent, node.dynamicLContainerNode.native, currentView); + if (projectedLNode.dynamicLContainerNode) { + projectedLNode.dynamicLContainerNode.data[RENDER_PARENT] = renderParent; + appendChild(projectedLNode.dynamicLContainerNode.native, tProjectionNode, currentView); } } diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index 18e2ebff74..d73a237342 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -17,13 +17,13 @@ import {getSymbolIterator} from '../util'; import {assertDefined, assertEqual} from './assert'; import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di'; -import {assertPreviousIsParent, getOrCreateCurrentQueries, isContentQueryHost, store, storeCleanupWithContext} from './instructions'; +import {_getViewData, assertPreviousIsParent, getOrCreateCurrentQueries, store, storeCleanupWithContext} from './instructions'; import {DirectiveDefInternal, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition'; import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector'; -import {LContainerNode, LElementNode, LNode, TNode, TNodeFlags, unusedValueExportToPlacateAjd as unused3} from './interfaces/node'; +import {LContainerNode, LElementNode, TNode, TNodeFlags, unusedValueExportToPlacateAjd as unused3} from './interfaces/node'; import {LQueries, QueryReadType, unusedValueExportToPlacateAjd as unused4} from './interfaces/query'; -import {DIRECTIVES, TVIEW} from './interfaces/view'; -import {flatten} from './util'; +import {DIRECTIVES, LViewData, TVIEW} from './interfaces/view'; +import {flatten, isContentQueryHost, readElementValue} from './util'; const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4; @@ -121,21 +121,21 @@ export class LQueries_ implements LQueries { insertView(index, this.deep); } - addNode(node: LNode): LQueries|null { - add(this.deep, node); + addNode(tNode: TNode): LQueries|null { + add(this.deep, tNode); - if (isContentQueryHost(node.tNode)) { - add(this.shallow, node); + if (isContentQueryHost(tNode)) { + add(this.shallow, tNode); - if (node.tNode.parent && isContentQueryHost(node.tNode.parent)) { + if (tNode.parent && isContentQueryHost(tNode.parent)) { // if node has a content query and parent also has a content query // both queries need to check this node for shallow matches - add(this.parent !.shallow, node); + add(this.parent !.shallow, tNode); } return this.parent; } - isRootNodeOfQuery(node.tNode) && add(this.shallow, node); + isRootNodeOfQuery(tNode) && add(this.shallow, tNode); return this; } @@ -241,51 +241,61 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null { /** * Iterates over all the directives for a node and returns index of a directive for a given type. * - * @param node Node on which directives are present. + * @param tNode TNode on which directives are present. + * @param currentView The view we are currently processing * @param type Type of a directive to look for. * @returns Index of a found directive or null when none found. */ -function getIdxOfMatchingDirective(node: LNode, type: Type): number|null { - const defs = node.view[TVIEW].directives !; - const flags = node.tNode.flags; - const count = flags & TNodeFlags.DirectiveCountMask; - const start = flags >> TNodeFlags.DirectiveStartingIndexShift; - const end = start + count; - for (let i = start; i < end; i++) { - const def = defs[i] as DirectiveDefInternal; - if (def.type === type && def.diPublic) { - return i; +function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: Type): number| + null { + const defs = currentView[TVIEW].directives; + if (defs) { + const flags = tNode.flags; + const count = flags & TNodeFlags.DirectiveCountMask; + const start = flags >> TNodeFlags.DirectiveStartingIndexShift; + const end = start + count; + for (let i = start; i < end; i++) { + const def = defs[i] as DirectiveDefInternal; + if (def.type === type && def.diPublic) { + return i; + } } } return null; } function readFromNodeInjector( - nodeInjector: LInjector, node: LNode, read: QueryReadType| Type, - directiveIdx: number): any { + nodeInjector: LInjector, tNode: TNode, currentView: LViewData, + read: QueryReadType| Type, directiveIdx: number): any { if (read instanceof ReadFromInjectorFn) { - return read.read(nodeInjector, node, directiveIdx); + return read.read(nodeInjector, tNode, directiveIdx); } else { - const matchingIdx = getIdxOfMatchingDirective(node, read as Type); + const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type); if (matchingIdx !== null) { - return node.view[DIRECTIVES] ![matchingIdx]; + return currentView[DIRECTIVES] ![matchingIdx]; } } return null; } -function add(query: LQuery| null, node: LNode) { - const nodeInjector = getOrCreateNodeInjectorForNode(node as LElementNode | LContainerNode); +function add(query: LQuery| null, tNode: TNode) { + const currentView = _getViewData(); + + // TODO: remove this lookup when nodeInjector is removed from LNode + const currentNode = readElementValue(currentView[tNode.index]); + const nodeInjector = + getOrCreateNodeInjectorForNode(currentNode as LElementNode | LContainerNode, tNode); + while (query) { const predicate = query.predicate; const type = predicate.type; if (type) { - const directiveIdx = getIdxOfMatchingDirective(node, type); + const directiveIdx = getIdxOfMatchingDirective(tNode, currentView, type); if (directiveIdx !== null) { // a node is matching a predicate - determine what to read // if read token and / or strategy is not specified, use type as read token - const result = - readFromNodeInjector(nodeInjector, node, predicate.read || type, directiveIdx); + const result = readFromNodeInjector( + nodeInjector, tNode, currentView, predicate.read || type, directiveIdx); if (result !== null) { addMatch(query, result); } @@ -293,12 +303,13 @@ function add(query: LQuery| null, node: LNode) { } else { const selector = predicate.selector !; for (let i = 0; i < selector.length; i++) { - const directiveIdx = getIdxOfMatchingSelector(node.tNode, selector[i]); + const directiveIdx = getIdxOfMatchingSelector(tNode, selector[i]); if (directiveIdx !== null) { // a node is matching a predicate - determine what to read // note that queries using name selector must specify read strategy ngDevMode && assertDefined(predicate.read, 'the node should have a predicate'); - const result = readFromNodeInjector(nodeInjector, node, predicate.read !, directiveIdx); + const result = readFromNodeInjector( + nodeInjector, tNode, currentView, predicate.read !, directiveIdx); if (result !== null) { addMatch(query, result); } diff --git a/packages/core/src/render3/util.ts b/packages/core/src/render3/util.ts index adb5fae2da..6fa50a67b0 100644 --- a/packages/core/src/render3/util.ts +++ b/packages/core/src/render3/util.ts @@ -5,13 +5,14 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ + import {devModeEqual} from '../change_detection/change_detection_util'; import {assertLessThan} from './assert'; -import {LElementNode} from './interfaces/node'; +import {LElementNode, TNode, TNodeFlags} from './interfaces/node'; import {HEADER_OFFSET, LViewData, TData} from './interfaces/view'; /** - * Returns wether the values are different from a change detection stand point. + * Returns whether the values are different from a change detection stand point. * * Constraints are relaxed in checkNoChanges mode. See `devModeEqual` for details. */ @@ -89,3 +90,11 @@ export function loadElementInternal(index: number, arr: LViewData): LElementNode export function readElementValue(value: LElementNode | any[]): LElementNode { return (Array.isArray(value) ? (value as any as any[])[0] : value) as LElementNode; } + +export function isContentQueryHost(tNode: TNode): boolean { + return (tNode.flags & TNodeFlags.hasContentQuery) !== 0; +} + +export function isComponent(tNode: TNode): boolean { + return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent; +} diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index 8b57d6a546..f86c462ded 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -12,7 +12,7 @@ import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_co import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref'; import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions'; -import {LViewNode} from './interfaces/node'; +import {TViewNode} from './interfaces/node'; import {FLAGS, LViewData, LViewFlags} from './interfaces/view'; import {destroyLView} from './node_manipulation'; @@ -30,13 +30,21 @@ export class ViewRef implements viewEngine_EmbeddedViewRef, viewEngine_Int /** * @internal */ - _lViewNode: LViewNode|null = null; + _view: LViewData; + + /** + * @internal + */ + _tViewNode: TViewNode|null = null; context: T; // TODO(issue/24571): remove '!'. rootNodes !: any[]; - constructor(protected _view: LViewData, context: T|null) { this.context = context !; } + constructor(_view: LViewData, context: T|null) { + this.context = context !; + this._view = _view; + } /** @internal */ _setComponentContext(view: LViewData, context: T) { @@ -256,7 +264,7 @@ export class ViewRef implements viewEngine_EmbeddedViewRef, viewEngine_Int /** @internal */ export class RootViewRef extends ViewRef { - constructor(protected _view: LViewData) { super(_view, null); } + constructor(public _view: LViewData) { super(_view, null); } detectChanges(): void { detectChangesInRootView(this._view); } diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 2bec21b858..a73f427ada 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -134,15 +134,18 @@ { "name": "componentRefresh" }, - { - "name": "createLNode" - }, { "name": "createLNodeObject" }, { "name": "createLViewData" }, + { + "name": "createNodeAtIndex" + }, + { + "name": "createRootComponent" + }, { "name": "createRootContext" }, @@ -198,7 +201,7 @@ "name": "firstTemplatePass" }, { - "name": "getChildLNode" + "name": "getBeforeNodeForView" }, { "name": "getClosureSafeProperty" @@ -206,9 +209,21 @@ { "name": "getComponentDef" }, + { + "name": "getContainerNode" + }, + { + "name": "getContainerRenderParent" + }, { "name": "getDirectiveDef" }, + { + "name": "getHighestElementContainer" + }, + { + "name": "getHostElementNode" + }, { "name": "getLViewChild" }, @@ -224,12 +239,18 @@ { "name": "getParentLNode" }, + { + "name": "getParentOrContainerNode" + }, { "name": "getPipeDef" }, { "name": "getPreviousOrParentNode" }, + { + "name": "getPreviousOrParentTNode" + }, { "name": "getRenderFlags" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index a6792814fb..4ade6663ef 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -314,6 +314,9 @@ { "name": "_devMode" }, + { + "name": "_getViewData" + }, { "name": "_global" }, @@ -405,7 +408,7 @@ "name": "createDirectivesAndLocals" }, { - "name": "createEmbeddedViewNode" + "name": "createEmbeddedViewAndNode" }, { "name": "createLContainer" @@ -413,18 +416,21 @@ { "name": "createLContext" }, - { - "name": "createLNode" - }, { "name": "createLNodeObject" }, { "name": "createLViewData" }, + { + "name": "createNodeAtIndex" + }, { "name": "createOutput$1" }, + { + "name": "createRootComponent" + }, { "name": "createRootContext" }, @@ -540,7 +546,7 @@ "name": "findAttrIndexInNode" }, { - "name": "findComponentHost" + "name": "findComponentView" }, { "name": "findDirectiveMatches" @@ -558,20 +564,23 @@ "name": "generatePropertyAliases" }, { - "name": "getChildLNode" + "name": "getBeforeNodeForView" }, { "name": "getCleanup" }, - { - "name": "getClosestComponentAncestor" - }, { "name": "getClosureSafeProperty" }, { "name": "getComponentDef" }, + { + "name": "getContainerNode" + }, + { + "name": "getContainerRenderParent" + }, { "name": "getCurrentSanitizer" }, @@ -581,6 +590,12 @@ { "name": "getDirectiveDef" }, + { + "name": "getHighestElementContainer" + }, + { + "name": "getHostElementNode" + }, { "name": "getInitialIndex" }, @@ -635,6 +650,9 @@ { "name": "getParentLNode" }, + { + "name": "getParentOrContainerNode" + }, { "name": "getParentState" }, @@ -650,6 +668,9 @@ { "name": "getPreviousOrParentNode" }, + { + "name": "getPreviousOrParentTNode" + }, { "name": "getProp" }, @@ -674,6 +695,9 @@ { "name": "getSymbolIterator" }, + { + "name": "getTNode" + }, { "name": "getTViewCleanup" }, diff --git a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json index 7b2bc1abc1..ccaa3c9c33 100644 --- a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json @@ -1091,6 +1091,9 @@ { "name": "_getComponentHostLElementNode" }, + { + "name": "_getViewData" + }, { "name": "_global" }, @@ -1278,7 +1281,7 @@ "name": "createDirectivesAndLocals" }, { - "name": "createEmbeddedViewNode" + "name": "createEmbeddedViewAndNode" }, { "name": "createInjector" @@ -1289,15 +1292,15 @@ { "name": "createLContext" }, - { - "name": "createLNode" - }, { "name": "createLNodeObject" }, { "name": "createLViewData" }, + { + "name": "createNodeAtIndex" + }, { "name": "createOutput$1" }, @@ -1307,6 +1310,9 @@ { "name": "createPlatformFactory" }, + { + "name": "createRootComponent" + }, { "name": "createRootContext" }, @@ -1482,7 +1488,7 @@ "name": "findAttrIndexInNode" }, { - "name": "findComponentHost" + "name": "findComponentView" }, { "name": "findDirectiveMatches" @@ -1557,20 +1563,23 @@ "name": "getBaseElementHref" }, { - "name": "getChildLNode" + "name": "getBeforeNodeForView" }, { "name": "getCleanup" }, - { - "name": "getClosestComponentAncestor" - }, { "name": "getClosureSafeProperty" }, { "name": "getComponentDef" }, + { + "name": "getContainerNode" + }, + { + "name": "getContainerRenderParent" + }, { "name": "getCurrencySymbol" }, @@ -1607,6 +1616,12 @@ { "name": "getFirstThursdayOfYear" }, + { + "name": "getHighestElementContainer" + }, + { + "name": "getHostElementNode" + }, { "name": "getInitialIndex" }, @@ -1727,6 +1742,9 @@ { "name": "getParentLNode" }, + { + "name": "getParentOrContainerNode" + }, { "name": "getParentState" }, @@ -1781,6 +1799,9 @@ { "name": "getSymbolIterator$1" }, + { + "name": "getTNode" + }, { "name": "getTViewCleanup" }, diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 06a2b7abb0..51dc33b2c4 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -12,7 +12,7 @@ import {RenderFlags} from '@angular/core/src/render3/interfaces/definition'; import {defineComponent} from '../../src/render3/definition'; import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di'; import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectRenderer2, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; -import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, 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 {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node'; @@ -1503,12 +1503,11 @@ describe('di', () => { null !, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways); const oldView = enterView(contentView, null); try { - const parent = createLNode(0, TNodeType.Element, null, null, null, null); - + const parentTNode = createNodeAtIndex(0, TNodeType.Element, null, null, null, null); // Simulate the situation where the previous parent is not initialized. // This happens on first bootstrap because we don't init existing values // so that we have smaller HelloWorld. - (parent.tNode as{parent: any}).parent = undefined; + (parentTNode as{parent: any}).parent = undefined; const injector: any = getOrCreateNodeInjector(); // TODO: Review use of `any` here (#19904) expect(injector).not.toBe(null); diff --git a/packages/core/test/render3/instructions_spec.ts b/packages/core/test/render3/instructions_spec.ts index f1ce2a83bc..817b9126c6 100644 --- a/packages/core/test/render3/instructions_spec.ts +++ b/packages/core/test/render3/instructions_spec.ts @@ -50,7 +50,7 @@ describe('instructions', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for hostElement + 1 for the template under test - tView: 1, + tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2 }); @@ -68,7 +68,7 @@ describe('instructions', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for hostElement + 1 for the template under test - tView: 1, + tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 1 }); @@ -87,7 +87,7 @@ describe('instructions', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element - tView: 1, + tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, }); }); @@ -127,7 +127,7 @@ describe('instructions', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element - tView: 1, + tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetAttribute: 3 }); @@ -148,7 +148,7 @@ describe('instructions', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element - tView: 1, + tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetAttribute: 2 }); @@ -169,7 +169,7 @@ describe('instructions', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element - tView: 1, + tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, }); }); @@ -183,7 +183,7 @@ describe('instructions', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element - tView: 1, + tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 1 }); diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 73eb7d2b73..5cfa83cb68 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -42,7 +42,7 @@ describe('render3 integration test', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 3, // 1 for div, 1 for text, 1 for host element - tView: 1, + tView: 2, // 1 for root view, 1 for template rendererCreateElement: 1, }); }); @@ -86,7 +86,7 @@ describe('render3 integration test', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 0, tNode: 2, - tView: 1, + tView: 2, // 1 for root view, 1 for template rendererSetText: 2, }); }); @@ -106,7 +106,7 @@ describe('render3 integration test', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 0, tNode: 2, - tView: 1, + tView: 2, // 1 for root view, 1 for template rendererSetText: 2, }); }); @@ -125,7 +125,7 @@ describe('render3 integration test', () => { expect(ngDevMode).toHaveProperties({ firstTemplatePass: 0, tNode: 2, - tView: 1, + tView: 2, // 1 for root view, 1 for template rendererSetText: 1, }); });