refactor(ivy): remove LNode.tNode (#25958)

PR Close #25958
This commit is contained in:
Kara Erickson 2018-09-13 16:07:23 -07:00 committed by Ben Lesh
parent 47f4412650
commit aedebaf025
21 changed files with 755 additions and 639 deletions

View File

@ -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?: (<T>(component: T, componentDef: ComponentDef<T, string>) => 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 = (<T>(component: T, componentDef: ComponentDef<T, string>) => 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<T>(
// 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<T>(
// 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<T>(
return component;
}
/**
* Creates a root component and sets it up with features and host bindings. Shared by
* renderComponent() and ViewContainerRef.createComponent().
*/
export function createRootComponent<T>(
elementNode: LElementNode, componentDef: ComponentDef<T, string>, 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);
}
/**

View File

@ -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<T> extends viewEngine_ComponentFactory<T> {
// 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<T> extends viewEngine_ComponentFactory<T> {
// 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<T> extends viewEngine_ComponentFactory<T> {
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<T> extends viewEngine_ComponentFactory<T> {
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<T> extends viewEngine_ComponentRef<T> {
super();
this.instance = instance;
this.hostView = this.changeDetectorRef = new RootViewRef<T>(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;

View File

@ -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;
}

View File

@ -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<any>): 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<T>(
// 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<T>(
}
}
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<T> {
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<any>((injector: LInjector, node: LNode, directiveIdx: number) => {
ngDevMode &&
assertNodeOfPossibleTypes(
node.tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
(new ReadFromInjectorFn<any>((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<any>);
@ -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<C>(templateRef: viewEngine_TemplateRef<C>, context?: C, index?: number):
viewEngine_EmbeddedViewRef<C> {
const adjustedIdx = this._adjustIndex(index);
const viewRef = (templateRef as TemplateRef<C>)
.createEmbeddedView(context || <any>{}, this._lContainerNode, adjustedIdx);
const viewRef =
(templateRef as TemplateRef<C>)
.createEmbeddedView(
context || <any>{}, this._lContainerNode, this._tContainerNode, adjustedIdx);
(viewRef as ViewRef<any>).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<any>)._lViewNode !;
const lView = (viewRef as ViewRef<any>)._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<any>).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<T>(di: LInjector): viewEngine_TemplateRef<T> {
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<any>(
hostNode.view, getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(),
hostNode.data[QUERIES]);
hostNode.data ![QUERIES]);
}
return di.templateRef;
}
@ -828,16 +817,17 @@ class TemplateRef<T> extends viewEngine_TemplateRef<T> {
super();
}
createEmbeddedView(context: T, containerNode?: LContainerNode, index?: number):
viewEngine_EmbeddedViewRef<T> {
const viewNode = createEmbeddedViewNode(
createEmbeddedView(
context: T, containerNode?: LContainerNode, tContainerNode?: TContainerNode,
index?: number): viewEngine_EmbeddedViewRef<T> {
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<T> extends viewEngine_TemplateRef<T> {
* Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the
* `<ng-template>` element.
*/
export function templateRefExtractor(lNode: LNodeWithLocalRefs) {
return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode));
export function templateRefExtractor(lNode: LNodeWithLocalRefs, tNode: TNode) {
return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode, tNode));
}

View File

@ -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;

View File

@ -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&LTextNode&
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<T>(
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<T>(
* either through ViewContainerRef.createEmbeddedView() or TemplateRef.createEmbeddedView().
* Such lViewNode will then be renderer with renderEmbeddedTemplate() (see below).
*/
export function createEmbeddedViewNode<T>(
export function createEmbeddedViewAndNode<T>(
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<T>(
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<T>(
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<T>(
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<T = any>(level: number = 1): T {
export function renderComponentOrTemplate<T>(
hostView: LViewData, componentOrContext: T, templateFn?: ComponentTemplate<T>) {
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 <ng-container>. */
@ -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<any>,
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<T>(
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<StylingContext>(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<T>(index: number, value: T | NO_CHANGE): void {
export function directiveCreate<T>(
directiveDefIdx: number, directive: T,
directiveDef: DirectiveDefInternal<T>| ComponentDefInternal<T>): T {
const hostNode = getPreviousOrParentNode();
const hostNode = getPreviousOrParentNode() !;
const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, hostNode);
const isComponent = (directiveDef as ComponentDefInternal<T>).template;
@ -1745,7 +1746,7 @@ function addComponentLogic<T>(
// 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;
// `<ng-content>` 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<T>(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]);

View File

@ -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';

View File

@ -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

View File

@ -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
* - `<div #nativeDivEl>` - `nativeDivEl` should point to the native `<div>` element;
* - `<ng-template #tplRef>` - `tplRef` should point to the `TemplateRef` instance;
*/
export type LocalRefExtractor = (lNode: LNodeWithLocalRefs) => any;
export type LocalRefExtractor = (lNode: LNodeWithLocalRefs, tNode: TNode) => any;

View File

@ -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

View File

@ -94,15 +94,16 @@ export interface LViewData extends Array<any> {
[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,
}
/**

View File

@ -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);
}
}

View File

@ -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<any>): 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<any>;
if (def.type === type && def.diPublic) {
return i;
function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: Type<any>): 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<any>;
if (def.type === type && def.diPublic) {
return i;
}
}
}
return null;
}
function readFromNodeInjector(
nodeInjector: LInjector, node: LNode, read: QueryReadType<any>| Type<any>,
directiveIdx: number): any {
nodeInjector: LInjector, tNode: TNode, currentView: LViewData,
read: QueryReadType<any>| Type<any>, 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<any>);
const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type<any>);
if (matchingIdx !== null) {
return node.view[DIRECTIVES] ![matchingIdx];
return currentView[DIRECTIVES] ![matchingIdx];
}
}
return null;
}
function add(query: LQuery<any>| null, node: LNode) {
const nodeInjector = getOrCreateNodeInjectorForNode(node as LElementNode | LContainerNode);
function add(query: LQuery<any>| 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<any>| 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);
}

View File

@ -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;
}

View File

@ -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<T> implements viewEngine_EmbeddedViewRef<T>, 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<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
/** @internal */
export class RootViewRef<T> extends ViewRef<T> {
constructor(protected _view: LViewData) { super(_view, null); }
constructor(public _view: LViewData) { super(_view, null); }
detectChanges(): void { detectChangesInRootView(this._view); }

View File

@ -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"
},

View File

@ -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"
},

View File

@ -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"
},

View File

@ -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);

View File

@ -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
});

View File

@ -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,
});
});