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

PR Close #25958
This commit is contained in:
Kara Erickson
2018-09-14 16:16:28 -07:00
committed by Ben Lesh
parent 47f4412650
commit aedebaf025
21 changed files with 755 additions and 639 deletions
+33 -17
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);
}
/**
+20 -30
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;
+10 -10
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;
}
File diff suppressed because it is too large Load Diff
+52 -44
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;
File diff suppressed because it is too large Load Diff
@@ -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';
@@ -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
+19 -15
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;
@@ -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
+15 -11
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,
}
/**
File diff suppressed because it is too large Load Diff
+45 -34
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);
}
+11 -2
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;
}
+12 -4
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); }
@@ -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"
},
@@ -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"
},
@@ -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"
},
+3 -4
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);
@@ -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
});
@@ -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,
});
});