refactor(ivy): combine LView with data (#24382)

PR Close #24382
This commit is contained in:
Kara Erickson 2018-06-07 22:42:32 -07:00 committed by Misko Hevery
parent dc4a3d00d0
commit 29eb24b142
17 changed files with 702 additions and 588 deletions

View File

@ -10,19 +10,16 @@
// correctly implementing its interfaces for backwards compatibility.
import {Type} from '../core';
import {Injector} from '../di/injector';
import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {Sanitizer} from '../sanitization/security';
import {assertComponentType, assertDefined} from './assert';
import {queueInitHooks, queueLifecycleHooks} from './hooks';
import {CLEAN_PROMISE, ROOT_DIRECTIVE_INDICES, _getComponentHostLElementNode, baseDirectiveCreate, createLView, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings} from './instructions';
import {CLEAN_PROMISE, ROOT_DIRECTIVE_INDICES, _getComponentHostLElementNode, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings,} from './instructions';
import {ComponentDef, ComponentType} from './interfaces/definition';
import {LElementNode, TNodeFlags} from './interfaces/node';
import {LElementNode} from './interfaces/node';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {LView, LViewFlags, RootContext} from './interfaces/view';
import {LViewData, LViewFlags, RootContext, INJECTOR, CONTEXT, TVIEW} from './interfaces/view';
import {stringify} from './util';
import {ViewRef} from './view_ref';
/** Options that control how the component should be bootstrapped. */
@ -71,7 +68,6 @@ export interface CreateComponentOptions {
scheduler?: (work: () => void) => void;
}
// TODO: A hack to not pull in the NullInjector from @angular/core.
export const NULL_INJECTOR: Injector = {
get: (token: any, notFoundValue?: any) => {
@ -108,11 +104,11 @@ export function renderComponent<T>(
const hostNode = locateHostElement(rendererFactory, opts.host || componentTag);
const rootContext = createRootContext(opts.scheduler || requestAnimationFrame.bind(window));
const rootView: LView = createLView(
const rootView: LViewData = createLViewData(
rendererFactory.createRenderer(hostNode, componentDef.rendererType),
createTView(-1, null, null, null), rootContext,
componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
rootView.injector = opts.injector || null;
rootView[INJECTOR] = opts.injector || null;
const oldView = enterView(rootView, null !);
let elementNode: LElementNode;
@ -131,7 +127,7 @@ export function renderComponent<T>(
executeInitAndContentHooks();
setHostBindings(ROOT_DIRECTIVE_INDICES);
detectChangesInternal(elementNode.data as LView, elementNode, component);
detectChangesInternal(elementNode.data as LViewData, elementNode, component);
} finally {
leaveView(oldView);
if (rendererFactory.end) rendererFactory.end();
@ -165,8 +161,9 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
const elementNode = _getComponentHostLElementNode(component);
// Root component is always created at dir index 0
queueInitHooks(0, def.onInit, def.doCheck, elementNode.view.tView);
queueLifecycleHooks(elementNode.tNode.flags, elementNode.view);
const tView = elementNode.view[TVIEW];
queueInitHooks(0, def.onInit, def.doCheck, tView);
queueLifecycleHooks(elementNode.tNode.flags, tView);
}
/**
@ -176,7 +173,7 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
* @param component any component
*/
function getRootContext(component: any): RootContext {
const rootContext = getRootView(component).context as RootContext;
const rootContext = getRootView(component)[CONTEXT] as RootContext;
ngDevMode && assertDefined(rootContext, 'rootContext');
return rootContext;
}

View File

@ -18,11 +18,11 @@ import {Type} from '../type';
import {assertComponentType, assertDefined} from './assert';
import {createRootContext} from './component';
import {baseDirectiveCreate, createLView, createTView, enterView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement} from './instructions';
import {baseDirectiveCreate, createLViewData, createTView, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement} from './instructions';
import {ComponentDef, ComponentType} from './interfaces/definition';
import {LElementNode} from './interfaces/node';
import {RElement} from './interfaces/renderer';
import {LView, LViewFlags, RootContext} from './interfaces/view';
import {INJECTOR, LViewData, LViewFlags, RootContext} from './interfaces/view';
import {ViewRef} from './view_ref';
export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver {
@ -94,11 +94,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
const rootContext: RootContext = ngModule !.injector.get(ROOT_CONTEXT);
// Create the root view. Uses empty TView and ContentTemplate.
const rootView: LView = createLView(
const rootView: LViewData = createLViewData(
rendererFactory.createRenderer(hostNode, this.componentDef.rendererType),
createTView(-1, null, null, null), null,
this.componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
rootView.injector = ngModule && ngModule.injector || null;
rootView[INJECTOR] = ngModule && ngModule.injector || null;
// rootView is the parent when bootstrapping
const oldView = enterView(rootView, null !);
@ -145,7 +145,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
componentType: Type<T>;
constructor(
componentType: Type<T>, instance: T, rootView: LView, injector: Injector,
componentType: Type<T>, instance: T, rootView: LViewData, injector: Injector,
hostNode: RElement) {
super();
this.instance = instance;
@ -173,4 +173,4 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
this.destroyCbs !.push(callback);
}
}
}

View File

@ -19,13 +19,14 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie
import {Type} from '../type';
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
import {addToViewTree, assertPreviousIsParent, createLContainer, createLNodeObject, createTNode, createTView, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList} from './interfaces/definition';
import {addToViewTree, assertPreviousIsParent, createLContainer, createLNodeObject, createTNode, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
import {VIEWS} from './interfaces/container';
import {ComponentTemplate, DirectiveDef} from './interfaces/definition';
import {LInjector} from './interfaces/injector';
import {AttributeMarker, LContainerNode, LElementNode, LNode, LViewNode, TNodeFlags, TNodeType} from './interfaces/node';
import {LQueries, QueryReadType} from './interfaces/query';
import {Renderer3} from './interfaces/renderer';
import {LView, TView} from './interfaces/view';
import {DIRECTIVES, HOST_NODE, INJECTOR, LViewData, QUERIES, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {detachView, getParentLNode, insertView, removeView} from './node_manipulation';
import {notImplemented, stringify} from './util';
@ -282,9 +283,9 @@ export function getOrCreateChangeDetectorRef(
const currentNode = di.node;
if (isComponent(currentNode.tNode)) {
return di.changeDetectorRef = new ViewRef(currentNode.data as LView, context);
return di.changeDetectorRef = new ViewRef(currentNode.data as LViewData, context);
} else if (currentNode.tNode.type === TNodeType.Element) {
return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node);
return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view[HOST_NODE]);
}
return null !;
}
@ -299,9 +300,9 @@ function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode):
return existingRef ?
existingRef :
new ViewRef(
hostNode.data as LView,
hostNode.view
.directives ![hostNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift]);
hostNode.data as LViewData,
hostNode
.view[DIRECTIVES] ![hostNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift]);
}
/**
@ -311,7 +312,7 @@ function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode):
*/
function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNode {
while (node.tNode.type === TNodeType.View) {
node = node.view.node;
node = node.view[HOST_NODE];
}
return node as LElementNode;
}
@ -340,7 +341,7 @@ export function getOrCreateInjectable<T>(
// If the token has a bloom hash, then it is a directive that is public to the injection system
// (diPublic). If there is no hash, fall back to the module injector.
if (bloomHash === null) {
const moduleInjector = getPreviousOrParentNode().view.injector;
const moduleInjector = getPreviousOrParentNode().view[INJECTOR];
const formerInjector = setCurrentInjector(moduleInjector);
try {
return inject(token, flags);
@ -370,14 +371,14 @@ export function getOrCreateInjectable<T>(
if (count !== 0) {
const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count;
const defs = node.view.tView.directives !;
const defs = node.view[TVIEW].directives !;
for (let i = start; i < end; i++) {
// Get the definition for the directive at this index and, if it is injectable (diPublic),
// and matches the given token, return the directive instance.
const directiveDef = defs[i] as DirectiveDef<any>;
if (directiveDef.type === token && directiveDef.diPublic) {
return getDirectiveInstance(node.view.directives ![i]);
return getDirectiveInstance(node.view[DIRECTIVES] ![i]);
}
}
}
@ -405,12 +406,12 @@ export function getOrCreateInjectable<T>(
}
function searchMatchesQueuedForCreation<T>(node: LNode, token: any): T|null {
const matches = node.view.tView.currentMatches;
const matches = node.view[TVIEW].currentMatches;
if (matches) {
for (let i = 0; i < matches.length; i += 2) {
const def = matches[i] as DirectiveDef<any>;
if (def.type === token) {
return resolveDirective(def, i + 1, matches, node.view.tView);
return resolveDirective(def, i + 1, matches, node.view[TVIEW]);
}
}
}
@ -545,7 +546,7 @@ export const QUERY_READ_FROM_NODE =
(new ReadFromInjectorFn<any>((injector: LInjector, node: LNode, directiveIdx: number) => {
ngDevMode && assertNodeOfPossibleTypes(node, TNodeType.Container, TNodeType.Element);
if (directiveIdx > -1) {
return node.view.directives ![directiveIdx];
return node.view[DIRECTIVES] ![directiveIdx];
} else if (node.tNode.type === TNodeType.Element) {
return getOrCreateElementRef(injector);
} else if (node.tNode.type === TNodeType.Container) {
@ -611,7 +612,7 @@ class ViewContainerRef implements viewEngine_ViewContainerRef {
clear(): void {
const lContainer = this._lContainerNode.data;
while (lContainer.views.length) {
while (lContainer[VIEWS].length) {
this.remove(0);
}
}
@ -620,7 +621,7 @@ class ViewContainerRef implements viewEngine_ViewContainerRef {
get length(): number {
const lContainer = this._lContainerNode.data;
return lContainer.views.length;
return lContainer[VIEWS].length;
}
createEmbeddedView<C>(templateRef: viewEngine_TemplateRef<C>, context?: C, index?: number):
@ -679,12 +680,12 @@ class ViewContainerRef implements viewEngine_ViewContainerRef {
private _adjustIndex(index?: number, shift: number = 0) {
if (index == null) {
return this._lContainerNode.data.views.length + shift;
return this._lContainerNode.data[VIEWS].length + shift;
}
if (ngDevMode) {
assertGreaterThan(index, -1, 'index must be positive');
// +1 because it's legal to insert at the end.
assertLessThan(index, this._lContainerNode.data.views.length + 1 + shift, 'index');
assertLessThan(index, this._lContainerNode.data[VIEWS].length + 1 + shift, 'index');
}
return index;
}
@ -704,7 +705,8 @@ export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef
const hostTNode = hostNode.tNode;
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
di.templateRef = new TemplateRef<any>(
getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(), hostNode.data.queries);
getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(),
hostNode.data[QUERIES]);
}
return di.templateRef;
}

View File

@ -9,7 +9,7 @@
import {assertEqual} from './assert';
import {DirectiveDef} from './interfaces/definition';
import {TNodeFlags} from './interfaces/node';
import {HookData, LView, LViewFlags, TView} from './interfaces/view';
import {DIRECTIVES, FLAGS, HookData, LViewData, LViewFlags, TView} from './interfaces/view';
/**
@ -20,7 +20,7 @@ import {HookData, LView, LViewFlags, TView} from './interfaces/view';
* directive index), then saved in the even indices of the initHooks array. The odd indices
* hold the hook functions themselves.
*
* @param index The index of the directive in LView.data
* @param index The index of the directive in LViewData[DIRECTIVES]
* @param hooks The static hooks map on the directive def
* @param tView The current TView
*/
@ -42,9 +42,8 @@ export function queueInitHooks(
* Loops through the directives on a node and queues all their hooks except ngOnInit
* and ngDoCheck, which are queued separately in directiveCreate.
*/
export function queueLifecycleHooks(flags: number, currentView: LView): void {
const tView = currentView.tView;
if (tView.firstTemplatePass === true) {
export function queueLifecycleHooks(flags: number, tView: TView): void {
if (tView.firstTemplatePass) {
const start = flags >> TNodeFlags.DirectiveStartingIndexShift;
const count = flags & TNodeFlags.DirectiveCountMask;
const end = start + count;
@ -97,10 +96,11 @@ function queueDestroyHooks(def: DirectiveDef<any>, tView: TView, i: number): voi
*
* @param currentView The current view
*/
export function executeInitHooks(currentView: LView, tView: TView, creationMode: boolean): void {
if (currentView.flags & LViewFlags.RunInit) {
executeHooks(currentView.directives !, tView.initHooks, tView.checkHooks, creationMode);
currentView.flags &= ~LViewFlags.RunInit;
export function executeInitHooks(
currentView: LViewData, tView: TView, creationMode: boolean): void {
if (currentView[FLAGS] & LViewFlags.RunInit) {
executeHooks(currentView[DIRECTIVES] !, tView.initHooks, tView.checkHooks, creationMode);
currentView[FLAGS] &= ~LViewFlags.RunInit;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -6,14 +6,30 @@
* found in the LICENSE file at https://angular.io/license
*/
import {LContainerNode, LElementNode, LViewNode} from './node';
import {LElementNode, LViewNode} from './node';
import {LQueries} from './query';
import {LView, TView} from './view';
import {LViewData, NEXT, PARENT, QUERIES} from './view';
/**
* Below are constants for LContainer indices to help us look up LContainer members
* without having to remember the specific indices.
* Uglify will inline these when minifying so there shouldn't be a cost.
*/
export const ACTIVE_INDEX = 0;
// PARENT, NEXT, and QUERIES are indices 1, 2, and 3.
// As we already have these constants in LViewData, we don't need to re-create them.
export const VIEWS = 4;
export const RENDER_PARENT = 5;
/** The state associated with an LContainer */
export interface LContainer {
/**
* The state associated with an LContainerNode.
*
* This is an array so that its structure is closer to LViewData. This helps
* when traversing the view tree (which is a mix of containers and component
* views), so we can jump to viewOrContainer[NEXT] in the same way regardless
* of type.
*/
export interface LContainer extends Array<any> {
/**
* The next active index in the views array to read or write to. This helps us
* keep track of where we are in the views array.
@ -21,19 +37,25 @@ export interface LContainer {
* it is set to null to identify this scenario, as indices are "absolute" in that case,
* i.e. provided directly by the user of the ViewContainerRef API.
*/
nextIndex: number|null;
/**
* This allows us to jump from a container to a sibling container or
* component view with the same parent, so we can remove listeners efficiently.
*/
next: LView|LContainer|null;
[ACTIVE_INDEX]: number|null;
/**
* Access to the parent view is necessary so we can propagate back
* up from inside a container to parent.next.
* up from inside a container to parent[NEXT].
*/
parent: LView|null;
[PARENT]: LViewData|null;
/**
* This allows us to jump from a container to a sibling container or component
* view with the same parent, so we can remove listeners efficiently.
*/
[NEXT]: LViewData|LContainer|null;
/**
* Queries active for this container - all the views inserted to / removed from
* this container are reported to queries referenced here.
*/
[QUERIES]: LQueries|null;
/**
* A list of the container's currently active child views. Views will be inserted
@ -42,7 +64,7 @@ export interface LContainer {
* (and don't need to be re-added) and so we can remove views from the DOM when they
* are no longer required.
*/
readonly views: LViewNode[];
[VIEWS]: LViewNode[];
/**
* Parent Element which will contain the location where all of the Views will be
@ -64,13 +86,7 @@ export interface LContainer {
* - not `null`, then use the `projectedParent.native` as the `RElement` to insert
* `LViewNode`s into.
*/
renderParent: LElementNode|null;
/**
* Queries active for this container - all the views inserted to / removed from
* this container are reported to queries referenced here.
*/
queries: LQueries|null;
[RENDER_PARENT]: LElementNode|null;
}
// Note: This hack is necessary so we don't erroneously get a circular dependency

View File

@ -10,8 +10,8 @@ import {LContainer} from './container';
import {LInjector} from './injector';
import {LProjection} from './projection';
import {LQueries} from './query';
import {RElement, RNode, RText} from './renderer';
import {LView, TData, TView} from './view';
import {RElement, RText} from './renderer';
import {LViewData, TView} from './view';
@ -72,7 +72,7 @@ export interface LNode {
* If LContainerNode, then `data` contains LContainer.
* If LProjectionNode, then `data` contains LProjection.
*/
readonly data: LView|LContainer|LProjection|null;
readonly data: LViewData|LContainer|LProjection|null;
/**
@ -80,7 +80,7 @@ export interface LNode {
*
* When the injector is walking up a tree, it needs access to the `directives` (part of view).
*/
readonly view: LView;
readonly view: LViewData;
/** The injector associated with this node. Necessary for DI. */
nodeInjector: LInjector|null;
@ -120,7 +120,7 @@ export interface LElementNode extends LNode {
readonly native: RElement;
/** If Component then data has LView (light DOM) */
readonly data: LView|null;
readonly data: LViewData|null;
}
/** LNode representing a #text node. */
@ -134,7 +134,7 @@ export interface LTextNode extends LNode {
/** Abstract node which contains root nodes of a view. */
export interface LViewNode extends LNode {
readonly native: null;
readonly data: LView;
readonly data: LViewData;
dynamicLContainerNode: null;
}

View File

@ -13,7 +13,7 @@ import {LNode} from './node';
/** Used for tracking queries (e.g. ViewChild, ContentChild). */
export interface LQueries {
/**
* Used to ask queries if those should be cloned to the child element.
* Used to ask querieis if those should be cloned to the child element.
*
* For example in the case of deep queries the `child()` returns
* queries for the child node. In case of shallow queries it returns

View File

@ -15,28 +15,68 @@ import {LElementNode, LViewNode, TNode} from './node';
import {LQueries} from './query';
import {Renderer3} from './renderer';
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */
export const HEADER_OFFSET = 14;
// Below are constants for LViewData indices to help us look up LViewData members
// without having to remember the specific indices.
// Uglify will inline these when minifying so there shouldn't be a cost.
export const TVIEW = 0;
export const PARENT = 1;
export const NEXT = 2;
export const QUERIES = 3;
export const FLAGS = 4;
export const HOST_NODE = 5;
export const BINDING_INDEX = 6;
export const DIRECTIVES = 7;
export const CLEANUP = 8;
export const CONTEXT = 9;
export const INJECTOR = 10;
export const RENDERER = 11;
export const SANITIZER = 12;
export const TAIL = 13;
/**
* `LView` stores all of the information needed to process the instructions as
* `LViewData` stores all of the information needed to process the instructions as
* they are invoked from the template. Each embedded view and component view has its
* own `LView`. When processing a particular view, we set the `currentView` to that
* `LView`. When that view is done processing, the `currentView` is set back to
* whatever the original `currentView` was before (the parent `LView`).
* own `LViewData`. When processing a particular view, we set the `viewData` to that
* `LViewData`. When that view is done processing, the `viewData` is set back to
* whatever the original `viewData` was before (the parent `LViewData`).
*
* Keeping separate state for each view facilities view insertion / deletion, so we
* don't have to edit the data array based on which views are present.
*/
export interface LView {
/** Flags for this view (see LViewFlags for definition of each bit). */
flags: LViewFlags;
export interface LViewData extends Array<any> {
/**
* The static data for this view. We need a reference to this so we can easily walk up the
* node tree in DI and get the TView.data array associated with a node (where the
* directive defs are stored).
*/
[TVIEW]: TView;
/**
* The parent view is needed when we exit the view and must restore the previous
* `LView`. Without this, the render method would have to keep a stack of
* `LViewData`. Without this, the render method would have to keep a stack of
* views as it is recursively rendering templates.
*/
readonly parent: LView|null;
[PARENT]: LViewData|null;
/**
*
* The next sibling LViewData or LContainer.
*
* Allows us to propagate between sibling view states that aren't in the same
* container. Embedded views already have a node.next, but it is only set for
* views in the same container. We need a way to link component views and views
* across containers as well.
*/
[NEXT]: LViewData|LContainer|null;
/** Queries active for this view - nodes from a view are reported to those queries. */
[QUERIES]: LQueries|null;
/** Flags for this view. See LViewFlags for more info. */
[FLAGS]: LViewFlags;
/**
* Pointer to the `LViewNode` or `LElementNode` which represents the root of the view.
@ -46,11 +86,8 @@ export interface LView {
*
* If `LElementNode`, this is the LView of a component.
*/
// TODO(kara): Remove when we have parent/child on TNodes
readonly node: LViewNode|LElementNode;
/** Renderer to be used for this view. */
readonly renderer: Renderer3;
// TODO(kara): Replace with index
[HOST_NODE]: LViewNode|LElementNode;
/**
* The binding index we should access next.
@ -59,7 +96,16 @@ export interface LView {
* if a view is left midway through processing bindings (e.g. if there is
* a setter that creates an embedded view, like in ngIf).
*/
bindingIndex: number;
[BINDING_INDEX]: number;
/**
* An array of directive instances in the current view.
*
* These must be stored separately from LNodes because their presence is
* unknown at compile-time and thus space cannot be reserved in data[].
*/
// TODO: flatten into LViewData[]
[DIRECTIVES]: any[]|null;
/**
* When a view is destroyed, listeners need to be released and outputs need to be
@ -69,79 +115,36 @@ export interface LView {
* These change per LView instance, so they cannot be stored on TView. Instead,
* TView.cleanup saves an index to the necessary context in this array.
*/
// TODO: collapse into data[]
cleanupInstances: any[]|null;
/**
* The last LView or LContainer beneath this LView in the hierarchy.
*
* The tail allows us to quickly add a new state to the end of the view list
* without having to propagate starting from the first child.
*/
tail: LView|LContainer|null;
/**
* The next sibling LView or LContainer.
*
* Allows us to propagate between sibling view states that aren't in the same
* container. Embedded views already have a node.next, but it is only set for
* views in the same container. We need a way to link component views and views
* across containers as well.
*/
next: LView|LContainer|null;
/**
* This array stores all element/text/container nodes created inside this view
* and their bindings. Stored as an array rather than a linked list so we can
* look up nodes directly in the case of forward declaration or bindings
* (e.g. E(1)).
*
* All bindings for a given view are stored in the order in which they
* appear in the template, starting with `bindingStartIndex`.
* We use `bindingIndex` to internally keep track of which binding
* is currently active.
*/
readonly data: any[];
/**
* An array of directive instances in the current view.
*
* These must be stored separately from LNodes because their presence is
* unknown at compile-time and thus space cannot be reserved in data[].
*/
directives: any[]|null;
/**
* The static data for this view. We need a reference to this so we can easily walk up the
* node tree in DI and get the TView.data array associated with a node (where the
* directive defs are stored).
*/
tView: TView;
// TODO: flatten into LViewData[]
[CLEANUP]: any[]|null;
/**
* - For embedded views, the context with which to render the template.
* - For root view of the root component the context contains change detection data.
* - `null` otherwise.
*/
context: {}|RootContext|null;
*/
[CONTEXT]: {}|RootContext|null;
/** An optional Module Injector to be used as fall back after Element Injectors are consulted. */
[INJECTOR]: Injector|null;
/** Renderer to be used for this view. */
[RENDERER]: Renderer3;
/** An optional custom sanitizer. */
[SANITIZER]: Sanitizer|null;
/**
* Queries active for this view - nodes from a view are reported to those queries
* The last LViewData or LContainer beneath this LViewData in the hierarchy.
*
* The tail allows us to quickly add a new state to the end of the view list
* without having to propagate starting from the first child.
*/
queries: LQueries|null;
/**
* An optional Module Injector to be used as fall back after Element Injectors are consulted.
*/
injector: Injector|null;
/**
* An optional custom sanitizer
*/
sanitizer: Sanitizer|null;
// TODO: replace with global
[TAIL]: LViewData|LContainer|null;
}
/** Flags associated with an LView (saved in LView.flags) */
/** Flags associated with an LView (saved in LViewData[FLAGS]) */
export const enum LViewFlags {
/**
* Whether or not the view is in creationMode.
@ -175,14 +178,6 @@ export const enum LViewFlags {
Destroyed = 0b100000,
}
/** Interface necessary to work with view tree traversal */
export interface LViewOrLContainer {
next: LView|LContainer|null;
views?: LViewNode[];
tView?: TView;
parent: LView|null;
}
/**
* The static data for an LView (shared between all templates of a
* given type).
@ -388,7 +383,7 @@ export interface TView {
* refreshed when the current view has finished its check.
*
* Even indices: Directive indices
* Odd indices: Element indices
* Odd indices: Element indices (adjusted for LViewData header offset)
*/
components: number[]|null;
@ -397,6 +392,9 @@ export interface TView {
*
* Even indices: Directive indices
* Odd indices: Element indices
*
* Element indices are NOT adjusted for LViewData header offset because
* they will be fed into instructions that expect the raw index (e.g. elementProperty)
*/
hostBindings: number[]|null;
}

View File

@ -6,13 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {assertDefined} from './assert';
import {callHooks} from './hooks';
import {LContainer, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
import {LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
import {LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNodeType, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
import {HookData, LView, LViewFlags, LViewOrLContainer, TView, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
import {CLEANUP, 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 {stringify} from './util';
@ -69,17 +68,17 @@ function findNextRNodeSibling(node: LNode | null, stopNode: LNode | null): RElem
export function getNextLNode(node: LNode): LNode|null {
// View nodes don't have TNodes, so their next must be retrieved through their LView.
if (node.tNode.type === TNodeType.View) {
const lView = node.data as LView;
return lView.next ? (lView.next as LView).node : null;
const viewData = node.data as LViewData;
return viewData[NEXT] ? (viewData[NEXT] as LViewData)[HOST_NODE] : null;
}
return node.tNode.next ? node.view.data[node.tNode.next !.index] : null;
return node.tNode.next ? node.view[node.tNode.next !.index] : null;
}
/** Retrieves the first child of a given node */
export function getChildLNode(node: LNode): LNode|null {
if (node.tNode.child) {
const view = node.tNode.type === TNodeType.View ? node.data as LView : node.view;
return view.data[node.tNode.child.index];
const viewData = node.tNode.type === TNodeType.View ? node.data as LViewData : node.view;
return viewData[node.tNode.child.index];
}
return null;
}
@ -92,7 +91,7 @@ export function getParentLNode(node: LNode): LElementNode|LContainerNode|LViewNo
export function getParentLNode(node: LNode): LElementNode|LContainerNode|LViewNode|null {
if (node.tNode.index === -1) return null;
const parent = node.tNode.parent;
return parent ? node.view.data[parent.index] : node.view.node;
return parent ? node.view[parent.index] : node.view[HOST_NODE];
}
/**
@ -213,10 +212,10 @@ function walkLNodeTree(
lContainerNode.dynamicLContainerNode.data :
lContainerNode.data;
if (renderParentNode) {
childContainerData.renderParent = renderParentNode;
childContainerData[RENDER_PARENT] = renderParentNode;
}
nextNode =
childContainerData.views.length ? getChildLNode(childContainerData.views[0]) : null;
childContainerData[VIEWS].length ? getChildLNode(childContainerData[VIEWS][0]) : null;
} else if (node.tNode.type === TNodeType.Projection) {
// For Projection look at the first projected node
nextNode = (node as LProjectionNode).data.head;
@ -256,11 +255,11 @@ export function addRemoveViewFromContainer(
beforeNode?: RNode | null): void {
ngDevMode && assertNodeType(container, TNodeType.Container);
ngDevMode && assertNodeType(rootNode, TNodeType.View);
const parentNode = container.data.renderParent;
const parentNode = container.data[RENDER_PARENT];
const parent = parentNode ? parentNode.native : null;
if (parent) {
let node: LNode|null = getChildLNode(rootNode);
const renderer = container.view.renderer;
const renderer = container.view[RENDERER];
walkLNodeTree(
node, rootNode, insertMode ? WalkLNodeTreeAction.Insert : WalkLNodeTreeAction.Detach,
renderer, parentNode, beforeNode);
@ -280,38 +279,35 @@ export function addRemoveViewFromContainer(
*
* @param rootView The view to destroy
*/
export function destroyViewTree(rootView: LView): void {
export function destroyViewTree(rootView: LViewData): void {
// If the view has no children, we can clean it up and return early.
if (rootView.tView.childIndex === -1) {
if (rootView[TVIEW].childIndex === -1) {
return cleanUpView(rootView);
}
let viewOrContainer: LViewOrLContainer|null = getLViewChild(rootView);
let viewOrContainer: LViewData|LContainer|null = getLViewChild(rootView);
while (viewOrContainer) {
let next: LViewOrLContainer|null = null;
let next: LViewData|LContainer|null = null;
if (viewOrContainer.views && viewOrContainer.views.length) {
next = viewOrContainer.views[0].data;
} else if (viewOrContainer.tView && viewOrContainer.tView.childIndex > -1) {
next = getLViewChild(viewOrContainer as LView);
} else if (viewOrContainer.next) {
// Only move to the side and clean if operating below rootView -
// otherwise we would start cleaning up sibling views of the rootView.
cleanUpView(viewOrContainer);
next = viewOrContainer.next;
if (viewOrContainer.length >= HEADER_OFFSET) {
// If LViewData, traverse down to child.
const view = viewOrContainer as LViewData;
if (view[TVIEW].childIndex > -1) next = getLViewChild(view);
} else {
// If container, traverse down to its first LViewData.
const container = viewOrContainer as LContainer;
if (container[VIEWS].length) next = container[VIEWS][0].data;
}
if (next == null) {
// If the viewOrContainer is the rootView and next is null it means that we are dealing
// with a root view that doesn't have children. We didn't descend into child views
// so no need to go back up the views tree.
while (viewOrContainer && !viewOrContainer !.next && viewOrContainer !== rootView) {
// Only clean up view when moving to the side or up, as destroy hooks
// should be called in order from the bottom up.
while (viewOrContainer && !viewOrContainer ![NEXT] && viewOrContainer !== rootView) {
cleanUpView(viewOrContainer);
viewOrContainer = getParentState(viewOrContainer, rootView);
}
cleanUpView(viewOrContainer || rootView);
next = viewOrContainer && viewOrContainer.next;
next = viewOrContainer && viewOrContainer ![NEXT];
}
viewOrContainer = next;
}
@ -333,31 +329,31 @@ export function destroyViewTree(rootView: LView): void {
export function insertView(
container: LContainerNode, viewNode: LViewNode, index: number): LViewNode {
const state = container.data;
const views = state.views;
const views = state[VIEWS];
if (index > 0) {
// This is a new view, we need to add it to the children.
views[index - 1].data.next = viewNode.data as LView;
views[index - 1].data[NEXT] = viewNode.data as LViewData;
}
if (index < views.length) {
viewNode.data.next = views[index].data;
viewNode.data[NEXT] = views[index].data;
views.splice(index, 0, viewNode);
} else {
views.push(viewNode);
viewNode.data.next = null;
viewNode.data[NEXT] = null;
}
// Notify query that a new view has been added
const lView = viewNode.data;
if (lView.queries) {
lView.queries.insertView(index);
if (lView[QUERIES]) {
lView[QUERIES] !.insertView(index);
}
// If the container's renderParent is null, we know that it is a root node of its own parent view
// and we should wait until that parent processes its nodes (otherwise, we will insert this view's
// nodes twice - once now and once when its parent inserts its views).
if (container.data.renderParent !== null) {
if (container.data[RENDER_PARENT] !== null) {
let beforeNode = findNextRNodeSibling(viewNode, container);
if (!beforeNode) {
@ -371,7 +367,7 @@ export function insertView(
}
// Sets the attached flag
viewNode.data.flags |= LViewFlags.Attached;
viewNode.data[FLAGS] |= LViewFlags.Attached;
return viewNode;
}
@ -387,20 +383,20 @@ export function insertView(
* @returns The detached view
*/
export function detachView(container: LContainerNode, removeIndex: number): LViewNode {
const views = container.data.views;
const views = container.data[VIEWS];
const viewNode = views[removeIndex];
if (removeIndex > 0) {
views[removeIndex - 1].data.next = viewNode.data.next as LView;
views[removeIndex - 1].data[NEXT] = viewNode.data[NEXT] as LViewData;
}
views.splice(removeIndex, 1);
addRemoveViewFromContainer(container, viewNode, false);
// Notify query that view has been removed
const removedLview = viewNode.data;
if (removedLview.queries) {
removedLview.queries.removeView(removeIndex);
if (removedLview[QUERIES]) {
removedLview[QUERIES] !.removeView(removeIndex);
}
// Unsets the attached flag
viewNode.data.flags &= ~LViewFlags.Attached;
viewNode.data[FLAGS] &= ~LViewFlags.Attached;
return viewNode;
}
@ -412,17 +408,17 @@ export function detachView(container: LContainerNode, removeIndex: number): LVie
* @returns The removed view
*/
export function removeView(container: LContainerNode, removeIndex: number): LViewNode {
const viewNode = container.data.views[removeIndex];
const viewNode = container.data[VIEWS][removeIndex];
detachView(container, removeIndex);
destroyLView(viewNode.data);
return viewNode;
}
/** Gets the child of the given LView */
export function getLViewChild(view: LView): LView|LContainer|null {
if (view.tView.childIndex === -1) return null;
/** Gets the child of the given LViewData */
export function getLViewChild(viewData: LViewData): LViewData|LContainer|null {
if (viewData[TVIEW].childIndex === -1) return null;
const hostNode: LElementNode|LContainerNode = view.data[view.tView.childIndex];
const hostNode: LElementNode|LContainerNode = viewData[viewData[TVIEW].childIndex];
return hostNode.data ? hostNode.data : (hostNode.dynamicLContainerNode as LContainerNode).data;
}
@ -433,14 +429,14 @@ export function getLViewChild(view: LView): LView|LContainer|null {
*
* @param view The view to be destroyed.
*/
export function destroyLView(view: LView) {
const renderer = view.renderer;
export function destroyLView(view: LViewData) {
const renderer = view[RENDERER];
if (isProceduralRenderer(renderer) && renderer.destroyNode) {
walkLNodeTree(view.node, view.node, WalkLNodeTreeAction.Destroy, renderer);
walkLNodeTree(view[HOST_NODE], view[HOST_NODE], WalkLNodeTreeAction.Destroy, renderer);
}
destroyViewTree(view);
// Sets the destroyed flag
view.flags |= LViewFlags.Destroyed;
view[FLAGS] |= LViewFlags.Destroyed;
}
/**
@ -455,76 +451,77 @@ export function destroyLView(view: LView) {
* @param rootView The rootView, so we don't propagate too far up the view tree
* @returns The correct parent LViewOrLContainer
*/
export function getParentState(state: LViewOrLContainer, rootView: LView): LViewOrLContainer|null {
export function getParentState(state: LViewData | LContainer, rootView: LViewData): LViewData|
LContainer|null {
let node;
if ((node = (state as LView) !.node) && node.tNode.type === TNodeType.View) {
if ((node = (state as LViewData) ![HOST_NODE]) && 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;
} else {
// otherwise, use parent view for containers or component views
return state.parent === rootView ? null : state.parent;
return state[PARENT] === rootView ? null : state[PARENT];
}
}
/**
* Removes all listeners and call all onDestroys in a given view.
*
* @param view The LView to clean up
* @param view The LViewData to clean up
*/
function cleanUpView(viewOrContainer: LViewOrLContainer): void {
if ((viewOrContainer as LView).tView) {
const view = viewOrContainer as LView;
function cleanUpView(viewOrContainer: LViewData | LContainer): void {
if ((viewOrContainer as LViewData)[TVIEW]) {
const view = viewOrContainer as LViewData;
removeListeners(view);
executeOnDestroys(view);
executePipeOnDestroys(view);
// For component views only, the local renderer is destroyed as clean up time.
if (view.tView.id === -1 && isProceduralRenderer(view.renderer)) {
if (view[TVIEW].id === -1 && isProceduralRenderer(view[RENDERER])) {
ngDevMode && ngDevMode.rendererDestroy++;
view.renderer.destroy();
(view[RENDERER] as ProceduralRenderer3).destroy();
}
}
}
/** Removes listeners and unsubscribes from output subscriptions */
function removeListeners(view: LView): void {
const cleanup = view.tView.cleanup !;
function removeListeners(viewData: LViewData): void {
const cleanup = viewData[TVIEW].cleanup !;
if (cleanup != null) {
for (let i = 0; i < cleanup.length - 1; i += 2) {
if (typeof cleanup[i] === 'string') {
// This is a listener with the native renderer
const native = view.data[cleanup[i + 1]].native;
const listener = view.cleanupInstances ![cleanup[i + 2]];
const native = viewData[cleanup[i + 1]].native;
const listener = viewData[CLEANUP] ![cleanup[i + 2]];
native.removeEventListener(cleanup[i], listener, cleanup[i + 3]);
i += 2;
} else if (typeof cleanup[i] === 'number') {
// This is a listener with renderer2 (cleanup fn can be found by index)
const cleanupFn = view.cleanupInstances ![cleanup[i]];
const cleanupFn = viewData[CLEANUP] ![cleanup[i]];
cleanupFn();
} else {
// This is a cleanup function that is grouped with the index of its context
const context = view.cleanupInstances ![cleanup[i + 1]];
const context = viewData[CLEANUP] ![cleanup[i + 1]];
cleanup[i].call(context);
}
}
view.cleanupInstances = null;
viewData[CLEANUP] = null;
}
}
/** Calls onDestroy hooks for this view */
function executeOnDestroys(view: LView): void {
const tView = view.tView;
function executeOnDestroys(view: LViewData): void {
const tView = view[TVIEW];
let destroyHooks: HookData|null;
if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
callHooks(view.directives !, destroyHooks);
callHooks(view[DIRECTIVES] !, destroyHooks);
}
}
/** Calls pipe destroy hooks for this view */
function executePipeOnDestroys(view: LView): void {
const pipeDestroyHooks = view.tView && view.tView.pipeDestroyHooks;
function executePipeOnDestroys(viewData: LViewData): void {
const pipeDestroyHooks = viewData[TVIEW] && viewData[TVIEW].pipeDestroyHooks;
if (pipeDestroyHooks) {
callHooks(view.data !, pipeDestroyHooks);
callHooks(viewData !, pipeDestroyHooks);
}
}
@ -545,7 +542,7 @@ function executePipeOnDestroys(view: LView): void {
* @param currentView The LView being processed
* @return boolean Whether the child element should be inserted.
*/
export function canInsertNativeNode(parent: LNode, currentView: LView): boolean {
export function canInsertNativeNode(parent: LNode, currentView: LViewData): boolean {
const parentIsElement = parent.tNode.type === TNodeType.Element;
return parentIsElement &&
@ -562,10 +559,10 @@ export function canInsertNativeNode(parent: LNode, currentView: LView): boolean
* @param currentView The current LView
* @returns Whether or not the child was appended
*/
export function appendChild(parent: LNode, child: RNode | null, currentView: LView): boolean {
export function appendChild(parent: LNode, child: RNode | null, currentView: LViewData): boolean {
if (child !== null && canInsertNativeNode(parent, currentView)) {
// We only add element if not in View or not projected.
const renderer = currentView.renderer;
const renderer = currentView[RENDERER];
isProceduralRenderer(renderer) ? renderer.appendChild(parent.native !as RElement, child) :
parent.native !.appendChild(child);
return true;
@ -583,7 +580,7 @@ export function appendChild(parent: LNode, child: RNode | null, currentView: LVi
*/
export function appendProjectedNode(
node: LElementNode | LTextNode | LContainerNode, currentParent: LElementNode,
currentView: LView): void {
currentView: LViewData): void {
if (node.tNode.type !== TNodeType.Container) {
appendChild(currentParent, (node as LElementNode | LTextNode).native, currentView);
} else {
@ -593,13 +590,13 @@ export function appendProjectedNode(
// and can't be re-projected (as not content of any component).
// Assignee the final projection location in those cases.
const lContainer = (node as LContainerNode).data;
lContainer.renderParent = currentParent;
const views = lContainer.views;
lContainer[RENDER_PARENT] = currentParent;
const views = lContainer[VIEWS];
for (let i = 0; i < views.length; i++) {
addRemoveViewFromContainer(node as LContainerNode, views[i], true, null);
}
}
if (node.dynamicLContainerNode) {
node.dynamicLContainerNode.data.renderParent = currentParent;
node.dynamicLContainerNode.data[RENDER_PARENT] = currentParent;
}
}

View File

@ -10,6 +10,7 @@ import {PipeTransform} from '../change_detection/pipe_transform';
import {getTView, load, store} from './instructions';
import {PipeDef, PipeDefList} from './interfaces/definition';
import {HEADER_OFFSET} from './interfaces/view';
import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunctionV} from './pure_function';
/**
@ -22,15 +23,17 @@ import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunction
export function pipe(index: number, pipeName: string): any {
const tView = getTView();
let pipeDef: PipeDef<any>;
const adjustedIndex = index + HEADER_OFFSET;
if (tView.firstTemplatePass) {
pipeDef = getPipeDef(pipeName, tView.pipeRegistry);
tView.data[index] = pipeDef;
tView.data[adjustedIndex] = pipeDef;
if (pipeDef.onDestroy) {
(tView.pipeDestroyHooks || (tView.pipeDestroyHooks = [])).push(index, pipeDef.onDestroy);
(tView.pipeDestroyHooks || (tView.pipeDestroyHooks = [
])).push(adjustedIndex, pipeDef.onDestroy);
}
} else {
pipeDef = tView.data[index] as PipeDef<any>;
pipeDef = tView.data[adjustedIndex] as PipeDef<any>;
}
const pipeInstance = pipeDef.factory();
@ -148,5 +151,5 @@ export function pipeBindV(index: number, slotOffset: number, values: any[]): any
}
function isPure(index: number): boolean {
return (<PipeDef<any>>getTView().data[index]).pure;
return (<PipeDef<any>>getTView().data[index + HEADER_OFFSET]).pure;
}

View File

@ -22,6 +22,7 @@ import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfac
import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, 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';
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4;
@ -222,7 +223,7 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
* @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 defs = node.view[TVIEW].directives !;
const flags = node.tNode.flags;
const count = flags & TNodeFlags.DirectiveCountMask;
const start = flags >> TNodeFlags.DirectiveStartingIndexShift;
@ -244,7 +245,7 @@ function readFromNodeInjector(
} else {
const matchingIdx = getIdxOfMatchingDirective(node, read as Type<any>);
if (matchingIdx !== null) {
return node.view.directives ![matchingIdx];
return node.view[DIRECTIVES] ![matchingIdx];
}
}
return null;
@ -416,7 +417,7 @@ export function query<T>(
const queryList = new QueryList<T>();
const queries = getCurrentQueries(LQueries_);
queries.track(queryList, predicate, descend, read);
storeCleanupWithContext(undefined, queryList, queryList.destroy);
storeCleanupWithContext(null, queryList, queryList.destroy);
if (memoryIndex != null) {
store(memoryIndex, queryList);
}

View File

@ -11,10 +11,10 @@ import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detec
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
import {checkNoChanges, detectChanges, markViewDirty, storeCleanupFn} from './instructions';
import {checkNoChanges, detectChanges, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
import {ComponentTemplate} from './interfaces/definition';
import {LViewNode} from './interfaces/node';
import {LView, LViewFlags} from './interfaces/view';
import {FLAGS, LViewData, LViewFlags} from './interfaces/view';
import {destroyLView} from './node_manipulation';
// Needed due to tsickle downleveling where multiple `implements` with classes creates
@ -29,16 +29,16 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
context: T;
rootNodes: any[];
constructor(protected _view: LView, context: T|null) { this.context = context !; }
constructor(protected _view: LViewData, context: T|null) { this.context = context !; }
/** @internal */
_setComponentContext(view: LView, context: T) {
_setComponentContext(view: LViewData, context: T) {
this._view = view;
this.context = context;
}
get destroyed(): boolean {
return (this._view.flags & LViewFlags.Destroyed) === LViewFlags.Destroyed;
return (this._view[FLAGS] & LViewFlags.Destroyed) === LViewFlags.Destroyed;
}
destroy(): void { destroyLView(this._view); }
@ -132,7 +132,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
* }
* ```
*/
detach(): void { this._view.flags &= ~LViewFlags.Attached; }
detach(): void { this._view[FLAGS] &= ~LViewFlags.Attached; }
/**
* Re-attaches a view to the change detection tree.
@ -189,7 +189,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
* }
* ```
*/
reattach(): void { this._view.flags |= LViewFlags.Attached; }
reattach(): void { this._view[FLAGS] |= LViewFlags.Attached; }
/**
* Checks the view and its children.
@ -240,8 +240,7 @@ export class EmbeddedViewRef<T> extends ViewRef<T> {
}
destroy(): void {
if (this._viewContainerRef &&
(this._view.flags & LViewFlags.Attached) === LViewFlags.Attached) {
if (this._viewContainerRef && viewAttached(this._view)) {
this._viewContainerRef.detach(this._viewContainerRef.indexOf(this));
this._viewContainerRef = null;
}

View File

@ -1,31 +1,82 @@
[
{
"name": "ACTIVE_INDEX"
},
{
"name": "BINDING_INDEX"
},
{
"name": "CLEAN_PROMISE"
},
{
"name": "CONTEXT"
},
{
"name": "DIRECTIVES"
},
{
"name": "EMPTY$2"
},
{
"name": "EMPTY_RENDERER_TYPE_ID"
},
{
"name": "FLAGS"
},
{
"name": "HEADER_FILLER"
},
{
"name": "HEADER_OFFSET"
},
{
"name": "HOST_NODE"
},
{
"name": "HelloWorld"
},
{
"name": "INJECTOR$1"
},
{
"name": "INeedToExistEvenThoughIAmNotNeeded"
},
{
"name": "NEXT"
},
{
"name": "NG_HOST_SYMBOL"
},
{
"name": "NG_PROJECT_AS_ATTR_NAME"
},
{
"name": "QUERIES"
},
{
"name": "RENDERER"
},
{
"name": "ROOT_DIRECTIVE_INDICES"
},
{
"name": "SANITIZER"
},
{
"name": "TVIEW"
},
{
"name": "UNDEFINED_RENDERER_TYPE_ID"
},
{
"name": "VIEWS"
},
{
"name": "_CLEAN_PROMISE"
},
{
"name": "_ROOT_DIRECTIVE_INDICES"
},
{
"name": "_renderCompCount"
},
@ -54,7 +105,7 @@
"name": "createLNodeObject"
},
{
"name": "createLView"
"name": "createLViewData"
},
{
"name": "createRootContext"
@ -68,9 +119,6 @@
{
"name": "createTextNode"
},
{
"name": "currentView"
},
{
"name": "defineComponent"
},
@ -131,9 +179,6 @@
{
"name": "invertObject"
},
{
"name": "isLContainer"
},
{
"name": "isProceduralRenderer"
},

View File

@ -1,16 +1,31 @@
[
{
"name": "ACTIVE_INDEX"
},
{
"name": "BINDING_INDEX"
},
{
"name": "BLOOM_SIZE"
},
{
"name": "CIRCULAR$2"
},
{
"name": "CLEANUP"
},
{
"name": "CLEAN_PROMISE"
},
{
"name": "CONTEXT"
},
{
"name": "CommonModule"
},
{
"name": "DIRECTIVES"
},
{
"name": "DefaultIterableDiffer"
},
@ -29,12 +44,30 @@
{
"name": "EmbeddedViewRef$1"
},
{
"name": "FLAGS"
},
{
"name": "HEADER_FILLER"
},
{
"name": "HEADER_OFFSET"
},
{
"name": "HOST_NODE"
},
{
"name": "INJECTOR$1"
},
{
"name": "IterableChangeRecord_"
},
{
"name": "IterableDiffers"
},
{
"name": "NEXT"
},
{
"name": "NG_ELEMENT_ID"
},
@ -65,15 +98,36 @@
{
"name": "PARAMETERS"
},
{
"name": "PARENT"
},
{
"name": "QUERIES"
},
{
"name": "RENDERER"
},
{
"name": "RENDER_PARENT"
},
{
"name": "ROOT_DIRECTIVE_INDICES"
},
{
"name": "RecordViewTuple"
},
{
"name": "SANITIZER"
},
{
"name": "SkipSelf"
},
{
"name": "TAIL"
},
{
"name": "TVIEW"
},
{
"name": "TemplateRef$1"
},
@ -92,6 +146,9 @@
{
"name": "UNDEFINED_RENDERER_TYPE_ID"
},
{
"name": "VIEWS"
},
{
"name": "ViewContainerRef$1"
},
@ -264,7 +321,7 @@
"name": "createLNodeObject"
},
{
"name": "createLView"
"name": "createLViewData"
},
{
"name": "createOutput"
@ -281,9 +338,6 @@
{
"name": "createTextNode"
},
{
"name": "currentView"
},
{
"name": "defineComponent"
},
@ -500,9 +554,6 @@
{
"name": "isJsObject"
},
{
"name": "isLContainer"
},
{
"name": "isListLikeIterable"
},
@ -530,6 +581,9 @@
{
"name": "load"
},
{
"name": "loadInternal"
},
{
"name": "locateHostElement"
},

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, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLView, createTView, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLViewData, createTView, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {LInjector} from '../../src/render3/interfaces/injector';
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
import {LViewFlags} from '../../src/render3/interfaces/view';
@ -1404,7 +1404,7 @@ describe('di', () => {
describe('getOrCreateNodeInjector', () => {
it('should handle initial undefined state', () => {
const contentView =
createLView(null !, createTView(-1, null, null, null), null, LViewFlags.CheckAlways);
createLViewData(null !, createTView(-1, null, null, null), null, LViewFlags.CheckAlways);
const oldView = enterView(contentView, null !);
try {
const parent = createLNode(0, TNodeType.Element, null, null, null, null);

View File

@ -9,8 +9,8 @@
import {RenderFlags} from '@angular/core/src/render3';
import {defineComponent, defineDirective} from '../../src/render3/index';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClassNamed, elementEnd, elementProperty, elementStart, elementStyleNamed, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, loadDirective, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {LViewFlags} from '../../src/render3/interfaces/view';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClassNamed, elementEnd, elementProperty, elementStart, elementStyleNamed, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, loadDirective, projection, projectionDef, text, textBinding,} from '../../src/render3/instructions';
import {HEADER_OFFSET} from '../../src/render3/interfaces/view';
import {sanitizeUrl} from '../../src/sanitization/sanitization';
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
@ -849,8 +849,8 @@ describe('render3 integration test', () => {
renderToHtml(Template, {condition: true});
const oldTemplateData = (Template as any).ngPrivateData;
const oldContainerData = (oldTemplateData as any).data[0];
const oldElementData = oldContainerData.tViews[0][0];
const oldContainerData = (oldTemplateData as any).data[HEADER_OFFSET];
const oldElementData = oldContainerData.tViews[0][HEADER_OFFSET];
expect(oldContainerData).not.toBeNull();
expect(oldElementData).not.toBeNull();
@ -858,8 +858,8 @@ describe('render3 integration test', () => {
renderToHtml(Template, {condition: true});
const newTemplateData = (Template as any).ngPrivateData;
const newContainerData = (oldTemplateData as any).data[0];
const newElementData = oldContainerData.tViews[0][0];
const newContainerData = (oldTemplateData as any).data[HEADER_OFFSET];
const newElementData = oldContainerData.tViews[0][HEADER_OFFSET];
expect(newTemplateData === oldTemplateData).toBe(true);
expect(newContainerData === oldContainerData).toBe(true);
expect(newElementData === oldElementData).toBe(true);