parent
47f4412650
commit
aedebaf025
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -26,16 +26,17 @@ import {Type} from '../type';
|
|||
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
|
||||
import {ComponentFactoryResolver} from './component_ref';
|
||||
import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
|
||||
import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getPreviousOrParentTNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
|
||||
import {VIEWS} from './interfaces/container';
|
||||
import {_getViewData, addToViewTree, assertPreviousIsParent, createEmbeddedViewAndNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate, resolveDirective} from './instructions';
|
||||
import {RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {DirectiveDefInternal, RenderFlags} from './interfaces/definition';
|
||||
import {LInjector} from './interfaces/injector';
|
||||
import {AttributeMarker, LContainerNode, LElementContainerNode, LElementNode, LNode, LNodeWithLocalRefs, LViewNode, TContainerNode, TElementNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {AttributeMarker, LContainerNode, LElementContainerNode, LElementNode, LNode, LNodeWithLocalRefs, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node';
|
||||
import {LQueries, QueryReadType} from './interfaces/query';
|
||||
import {Renderer3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {DIRECTIVES, HOST_NODE, INJECTOR, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
|
||||
import {RElement, Renderer3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {CONTEXT, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, PARENT, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {addRemoveViewFromContainer, appendChild, detachView, getChildLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
||||
import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getHostElementNode, getParentLNode, getParentOrContainerNode, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||
import {isComponent, loadElementInternal} from './util';
|
||||
import {ViewRef} from './view_ref';
|
||||
|
||||
|
||||
|
@ -101,7 +102,8 @@ export function bloomAdd(injector: LInjector, type: Type<any>): void {
|
|||
export function getOrCreateNodeInjector(): LInjector {
|
||||
ngDevMode && assertPreviousIsParent();
|
||||
return getOrCreateNodeInjectorForNode(
|
||||
getPreviousOrParentNode() as LElementNode | LElementContainerNode | LContainerNode);
|
||||
getPreviousOrParentNode() as LElementNode | LElementContainerNode | LContainerNode,
|
||||
getPreviousOrParentTNode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,16 +113,18 @@ export function getOrCreateNodeInjector(): LInjector {
|
|||
* @returns Node injector
|
||||
*/
|
||||
export function getOrCreateNodeInjectorForNode(
|
||||
node: LElementNode | LElementContainerNode | LContainerNode): LInjector {
|
||||
node: LElementNode | LElementContainerNode | LContainerNode, tNode: TNode): LInjector {
|
||||
// TODO: remove LNode arg when nodeInjector refactor is done
|
||||
const nodeInjector = node.nodeInjector;
|
||||
const parent = getParentLNode(node);
|
||||
const parentInjector = parent && parent.nodeInjector;
|
||||
const parentLNode = getParentOrContainerNode(tNode, node.view);
|
||||
const parentInjector = parentLNode && parentLNode.nodeInjector;
|
||||
if (nodeInjector != parentInjector) {
|
||||
return nodeInjector !;
|
||||
}
|
||||
return node.nodeInjector = {
|
||||
parent: parentInjector,
|
||||
node: node,
|
||||
tNode: tNode,
|
||||
bf0: 0,
|
||||
bf1: 0,
|
||||
bf2: 0,
|
||||
|
@ -302,32 +306,25 @@ export function getOrCreateChangeDetectorRef(
|
|||
di: LInjector, context: any): viewEngine_ChangeDetectorRef {
|
||||
if (di.changeDetectorRef) return di.changeDetectorRef;
|
||||
|
||||
const currentNode = di.node;
|
||||
if (isComponent(currentNode.tNode)) {
|
||||
return di.changeDetectorRef = new ViewRef(currentNode.data as LViewData, context);
|
||||
} else if (currentNode.tNode.type === TNodeType.Element) {
|
||||
return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view[HOST_NODE]);
|
||||
const currentTNode = getPreviousOrParentTNode();
|
||||
if (isComponent(currentTNode)) {
|
||||
return di.changeDetectorRef = new ViewRef(di.node.data as LViewData, context);
|
||||
} else if (currentTNode.type === TNodeType.Element) {
|
||||
return di.changeDetectorRef = getOrCreateHostChangeDetector(_getViewData());
|
||||
}
|
||||
return null !;
|
||||
}
|
||||
|
||||
/** Gets or creates ChangeDetectorRef for the closest host component */
|
||||
function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode):
|
||||
viewEngine_ChangeDetectorRef {
|
||||
const hostNode = getClosestComponentAncestor(currentNode);
|
||||
function getOrCreateHostChangeDetector(currentView: LViewData): viewEngine_ChangeDetectorRef {
|
||||
const hostComponentView = findComponentView(currentView);
|
||||
const hostNode = getHostElementNode(hostComponentView) !;
|
||||
const hostInjector = hostNode.nodeInjector;
|
||||
const existingRef = hostInjector && hostInjector.changeDetectorRef;
|
||||
|
||||
return existingRef ?
|
||||
existingRef :
|
||||
new ViewRef(
|
||||
hostNode.data as LViewData,
|
||||
hostNode
|
||||
.view[DIRECTIVES] ![hostNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift]);
|
||||
return existingRef ? existingRef : new ViewRef(hostComponentView, hostComponentView[CONTEXT]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getOrCreateRenderer2(di: LInjector): Renderer2 {
|
||||
const renderer = di.node.view[RENDERER];
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
|
@ -337,18 +334,6 @@ function getOrCreateRenderer2(di: LInjector): Renderer2 {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the node is an embedded view, traverses up the view tree to return the closest
|
||||
* ancestor view that is attached to a component. If it's already a component node,
|
||||
* returns itself.
|
||||
*/
|
||||
function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNode {
|
||||
while (node.tNode.type === TNodeType.View) {
|
||||
node = node.view[HOST_NODE];
|
||||
}
|
||||
return node as LElementNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value associated to the given token from the injectors.
|
||||
*
|
||||
|
@ -384,7 +369,8 @@ export function getOrCreateInjectable<T>(
|
|||
// At this point, we have an injector which *may* contain the token, so we step through the
|
||||
// directives associated with the injector's corresponding node to get the directive instance.
|
||||
const node = injector.node;
|
||||
const nodeFlags = node.tNode.flags;
|
||||
const tNode = injector.tNode;
|
||||
const nodeFlags = tNode.flags;
|
||||
const count = nodeFlags & TNodeFlags.DirectiveCountMask;
|
||||
|
||||
if (count !== 0) {
|
||||
|
@ -420,7 +406,7 @@ export function getOrCreateInjectable<T>(
|
|||
}
|
||||
}
|
||||
|
||||
const moduleInjector = getPreviousOrParentNode().view[INJECTOR];
|
||||
const moduleInjector = getPreviousOrParentNode() !.view[INJECTOR];
|
||||
const formerInjector = setCurrentInjector(moduleInjector);
|
||||
try {
|
||||
return inject(token, flags);
|
||||
|
@ -546,7 +532,7 @@ function sameHostView(injector: LInjector): boolean {
|
|||
}
|
||||
|
||||
export class ReadFromInjectorFn<T> {
|
||||
constructor(readonly read: (injector: LInjector, node: LNode, directiveIndex?: number) => T) {}
|
||||
constructor(readonly read: (injector: LInjector, tNode: TNode, directiveIndex?: number) => T) {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -573,22 +559,21 @@ export const QUERY_READ_ELEMENT_REF =
|
|||
(injector: LInjector) => getOrCreateElementRef(injector)) as any);
|
||||
|
||||
export const QUERY_READ_FROM_NODE =
|
||||
(new ReadFromInjectorFn<any>((injector: LInjector, node: LNode, directiveIdx: number) => {
|
||||
ngDevMode &&
|
||||
assertNodeOfPossibleTypes(
|
||||
node.tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
|
||||
(new ReadFromInjectorFn<any>((injector: LInjector, tNode: TNode, directiveIdx: number) => {
|
||||
ngDevMode && assertNodeOfPossibleTypes(
|
||||
tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
|
||||
if (directiveIdx > -1) {
|
||||
return node.view[DIRECTIVES] ![directiveIdx];
|
||||
return _getViewData()[DIRECTIVES] ![directiveIdx];
|
||||
}
|
||||
if (node.tNode.type === TNodeType.Element || node.tNode.type === TNodeType.ElementContainer) {
|
||||
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
|
||||
return getOrCreateElementRef(injector);
|
||||
}
|
||||
if (node.tNode.type === TNodeType.Container) {
|
||||
if (tNode.type === TNodeType.Container) {
|
||||
return getOrCreateTemplateRef(injector);
|
||||
}
|
||||
if (ngDevMode) {
|
||||
// should never happen
|
||||
throw new Error(`Unexpected node type: ${node.tNode.type}`);
|
||||
throw new Error(`Unexpected node type: ${tNode.type}`);
|
||||
}
|
||||
}) as any as QueryReadType<any>);
|
||||
|
||||
|
@ -603,29 +588,32 @@ class ElementRef extends viewEngine_ElementRef {}
|
|||
*/
|
||||
export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainerRef {
|
||||
if (!di.viewContainerRef) {
|
||||
const vcRefHost = di.node;
|
||||
const hostTNode = vcRefHost.tNode as TElementNode | TContainerNode;
|
||||
|
||||
const hostLNode =
|
||||
getPreviousOrParentNode() as LElementNode | LContainerNode | LElementContainerNode;
|
||||
const hostTNode = getPreviousOrParentTNode() as TElementNode | TContainerNode;
|
||||
ngDevMode && assertNodeOfPossibleTypes(
|
||||
hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
|
||||
const hostParent = getParentLNode(vcRefHost) !;
|
||||
const lContainer = createLContainer(hostParent, vcRefHost.view, true);
|
||||
const comment = vcRefHost.view[RENDERER].createComment(ngDevMode ? 'container' : '');
|
||||
|
||||
const hostView = hostLNode.view;
|
||||
const lContainer = createLContainer(hostView, true);
|
||||
const comment = hostView[RENDERER].createComment(ngDevMode ? 'container' : '');
|
||||
const lContainerNode: LContainerNode = createLNodeObject(
|
||||
TNodeType.Container, vcRefHost.view, vcRefHost.nodeInjector, comment, lContainer);
|
||||
appendChild(hostParent, comment, vcRefHost.view);
|
||||
TNodeType.Container, hostView, hostLNode.nodeInjector, comment, lContainer);
|
||||
|
||||
lContainer[RENDER_PARENT] = getRenderParent(hostTNode, hostView);
|
||||
|
||||
appendChild(comment, hostTNode, hostView);
|
||||
|
||||
if (!hostTNode.dynamicContainerNode) {
|
||||
hostTNode.dynamicContainerNode =
|
||||
createTNode(TNodeType.Container, -1, null, null, hostTNode, null);
|
||||
}
|
||||
|
||||
lContainerNode.tNode = hostTNode.dynamicContainerNode;
|
||||
vcRefHost.dynamicLContainerNode = lContainerNode;
|
||||
hostLNode.dynamicLContainerNode = lContainerNode;
|
||||
addToViewTree(hostView, hostTNode.index as number, lContainer);
|
||||
|
||||
addToViewTree(vcRefHost.view, hostTNode.index as number, lContainer);
|
||||
|
||||
di.viewContainerRef = new ViewContainerRef(lContainerNode, vcRefHost);
|
||||
di.viewContainerRef = new ViewContainerRef(
|
||||
lContainerNode, hostTNode.dynamicContainerNode as TContainerNode, hostLNode, hostTNode);
|
||||
}
|
||||
|
||||
return di.viewContainerRef;
|
||||
|
@ -663,24 +651,25 @@ class ViewContainerRef extends viewEngine_ViewContainerRef {
|
|||
private _viewRefs: viewEngine_ViewRef[] = [];
|
||||
|
||||
constructor(
|
||||
private _lContainerNode: LContainerNode,
|
||||
private _hostNode: LElementNode|LElementContainerNode|LContainerNode) {
|
||||
private _lContainerNode: LContainerNode, private _tContainerNode: TContainerNode,
|
||||
private _hostNode: LElementNode|LElementContainerNode|LContainerNode,
|
||||
private _hostTNode: TNode) {
|
||||
super();
|
||||
}
|
||||
|
||||
get element(): ElementRef {
|
||||
const injector = getOrCreateNodeInjectorForNode(this._hostNode);
|
||||
const injector = getOrCreateNodeInjectorForNode(this._hostNode, this._hostTNode);
|
||||
return getOrCreateElementRef(injector);
|
||||
}
|
||||
|
||||
get injector(): Injector {
|
||||
const injector = getOrCreateNodeInjectorForNode(this._hostNode);
|
||||
const injector = getOrCreateNodeInjectorForNode(this._hostNode, this._hostTNode);
|
||||
return new NodeInjector(injector);
|
||||
}
|
||||
|
||||
/** @deprecated No replacement */
|
||||
get parentInjector(): Injector {
|
||||
const parentLInjector = getParentLNode(this._hostNode).nodeInjector;
|
||||
const parentLInjector = getParentLNode(this._hostTNode, this._hostNode.view) !.nodeInjector;
|
||||
return parentLInjector ? new NodeInjector(parentLInjector) : new NullInjector();
|
||||
}
|
||||
|
||||
|
@ -701,8 +690,10 @@ class ViewContainerRef extends viewEngine_ViewContainerRef {
|
|||
createEmbeddedView<C>(templateRef: viewEngine_TemplateRef<C>, context?: C, index?: number):
|
||||
viewEngine_EmbeddedViewRef<C> {
|
||||
const adjustedIdx = this._adjustIndex(index);
|
||||
const viewRef = (templateRef as TemplateRef<C>)
|
||||
.createEmbeddedView(context || <any>{}, this._lContainerNode, adjustedIdx);
|
||||
const viewRef =
|
||||
(templateRef as TemplateRef<C>)
|
||||
.createEmbeddedView(
|
||||
context || <any>{}, this._lContainerNode, this._tContainerNode, adjustedIdx);
|
||||
(viewRef as ViewRef<any>).attachToViewContainerRef(this);
|
||||
this._viewRefs.splice(adjustedIdx, 0, viewRef);
|
||||
return viewRef;
|
||||
|
@ -727,15 +718,13 @@ class ViewContainerRef extends viewEngine_ViewContainerRef {
|
|||
if (viewRef.destroyed) {
|
||||
throw new Error('Cannot insert a destroyed View in a ViewContainer!');
|
||||
}
|
||||
const lViewNode = (viewRef as ViewRef<any>)._lViewNode !;
|
||||
const lView = (viewRef as ViewRef<any>)._view !;
|
||||
const adjustedIdx = this._adjustIndex(index);
|
||||
|
||||
insertView(this._lContainerNode, lViewNode, adjustedIdx);
|
||||
const views = this._lContainerNode.data[VIEWS];
|
||||
const beforeNode = adjustedIdx + 1 < views.length ?
|
||||
(getChildLNode(views[adjustedIdx + 1][HOST_NODE]) !).native :
|
||||
this._lContainerNode.native;
|
||||
addRemoveViewFromContainer(this._lContainerNode, lViewNode.data, true, beforeNode);
|
||||
insertView(this._lContainerNode, lView, adjustedIdx, this._tContainerNode.parent !.index);
|
||||
const beforeNode =
|
||||
getBeforeNodeForView(adjustedIdx, this._lContainerNode.data[VIEWS], this._lContainerNode);
|
||||
addRemoveViewFromContainer(lView, true, beforeNode);
|
||||
|
||||
(viewRef as ViewRef<any>).attachToViewContainerRef(this);
|
||||
this._viewRefs.splice(adjustedIdx, 0, viewRef);
|
||||
|
@ -754,13 +743,13 @@ class ViewContainerRef extends viewEngine_ViewContainerRef {
|
|||
|
||||
remove(index?: number): void {
|
||||
const adjustedIdx = this._adjustIndex(index, -1);
|
||||
removeView(this._lContainerNode, adjustedIdx);
|
||||
removeView(this._lContainerNode, this._tContainerNode as TContainerNode, adjustedIdx);
|
||||
this._viewRefs.splice(adjustedIdx, 1);
|
||||
}
|
||||
|
||||
detach(index?: number): viewEngine_ViewRef|null {
|
||||
const adjustedIdx = this._adjustIndex(index, -1);
|
||||
detachView(this._lContainerNode, adjustedIdx);
|
||||
detachView(this._lContainerNode, adjustedIdx, !!this._tContainerNode.detached);
|
||||
return this._viewRefs.splice(adjustedIdx, 1)[0] || null;
|
||||
}
|
||||
|
||||
|
@ -786,13 +775,13 @@ class ViewContainerRef extends viewEngine_ViewContainerRef {
|
|||
*/
|
||||
export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef<T> {
|
||||
if (!di.templateRef) {
|
||||
const hostNode = di.node as LContainerNode;
|
||||
const hostTNode = hostNode.tNode;
|
||||
const hostNode = getPreviousOrParentNode() as LContainerNode;
|
||||
const hostTNode = getPreviousOrParentTNode() as TContainerNode;
|
||||
ngDevMode && assertNodeType(hostTNode, TNodeType.Container);
|
||||
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
|
||||
di.templateRef = new TemplateRef<any>(
|
||||
hostNode.view, getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(),
|
||||
hostNode.data[QUERIES]);
|
||||
hostNode.data ![QUERIES]);
|
||||
}
|
||||
return di.templateRef;
|
||||
}
|
||||
|
@ -828,16 +817,17 @@ class TemplateRef<T> extends viewEngine_TemplateRef<T> {
|
|||
super();
|
||||
}
|
||||
|
||||
createEmbeddedView(context: T, containerNode?: LContainerNode, index?: number):
|
||||
viewEngine_EmbeddedViewRef<T> {
|
||||
const viewNode = createEmbeddedViewNode(
|
||||
createEmbeddedView(
|
||||
context: T, containerNode?: LContainerNode, tContainerNode?: TContainerNode,
|
||||
index?: number): viewEngine_EmbeddedViewRef<T> {
|
||||
const lView = createEmbeddedViewAndNode(
|
||||
this._tView, context, this._declarationParentView, this._renderer, this._queries);
|
||||
if (containerNode) {
|
||||
insertView(containerNode, viewNode, index !);
|
||||
insertView(containerNode, lView, index !, tContainerNode !.parent !.index);
|
||||
}
|
||||
renderEmbeddedTemplate(viewNode.data, this._tView, context, RenderFlags.Create);
|
||||
const viewRef = new ViewRef(viewNode.data, context);
|
||||
viewRef._lViewNode = viewNode;
|
||||
renderEmbeddedTemplate(lView, this._tView, context, RenderFlags.Create);
|
||||
const viewRef = new ViewRef(lView, context);
|
||||
viewRef._tViewNode = lView[HOST_NODE] as TViewNode;
|
||||
return viewRef;
|
||||
}
|
||||
}
|
||||
|
@ -846,6 +836,6 @@ class TemplateRef<T> extends viewEngine_TemplateRef<T> {
|
|||
* Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the
|
||||
* `<ng-template>` element.
|
||||
*/
|
||||
export function templateRefExtractor(lNode: LNodeWithLocalRefs) {
|
||||
return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode));
|
||||
export function templateRefExtractor(lNode: LNodeWithLocalRefs, tNode: TNode) {
|
||||
return getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(lNode, tNode));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -12,26 +12,27 @@ import {QueryList} from '../linker';
|
|||
import {Sanitizer} from '../sanitization/security';
|
||||
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
|
||||
|
||||
import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotEqual} from './assert';
|
||||
import {attachPatchData, getLElementFromComponent, getLElementFromRootComponent, readPatchedData, readPatchedLViewData} from './context_discovery';
|
||||
import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './assert';
|
||||
import {attachPatchData, getLElementFromComponent, readPatchedLViewData} from './context_discovery';
|
||||
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
||||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {LInjector} from './interfaces/injector';
|
||||
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LNodeWithLocalRefs, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node';
|
||||
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementNode, LNode, LNodeWithLocalRefs, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
|
||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||
import {LQueries} from './interfaces/query';
|
||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
|
||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getLViewChild, getParentLNode, insertView, removeView} from './node_manipulation';
|
||||
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getContainerNode, getHostElementNode, getLViewChild, getParentLNode, getParentOrContainerNode, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||
import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling';
|
||||
import {assertDataInRangeInternal, isDifferent, loadElementInternal, loadInternal, readElementValue, stringify} from './util';
|
||||
import {assertDataInRangeInternal, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, readElementValue, stringify} from './util';
|
||||
import {ViewRef} from './view_ref';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A permanent marker promise which signifies that the current CD tree is
|
||||
* clean.
|
||||
|
@ -128,9 +129,9 @@ export function restoreView(viewToRestore: OpaqueViewState) {
|
|||
/** Used to set the parent property when nodes are created and track query results. */
|
||||
let previousOrParentTNode: TNode;
|
||||
|
||||
export function getPreviousOrParentNode(): LNode {
|
||||
return previousOrParentTNode == null || previousOrParentTNode === tView.node ?
|
||||
viewData[HOST_NODE] :
|
||||
export function getPreviousOrParentNode(): LNode|null {
|
||||
return previousOrParentTNode == null || previousOrParentTNode === viewData[HOST_NODE] ?
|
||||
getHostElementNode(viewData) :
|
||||
readElementValue(viewData[previousOrParentTNode.index]);
|
||||
}
|
||||
|
||||
|
@ -162,7 +163,7 @@ export function getOrCreateCurrentQueries(
|
|||
QueryType: {new (parent: null, shallow: null, deep: null): LQueries}): LQueries {
|
||||
// if this is the first content query on a node, any existing LQueries needs to be cloned
|
||||
// in subsequent template passes, the cloning occurs before directive instantiation.
|
||||
if (previousOrParentTNode && previousOrParentTNode !== tView.node &&
|
||||
if (previousOrParentTNode && previousOrParentTNode !== viewData[HOST_NODE] &&
|
||||
!isContentQueryHost(previousOrParentTNode)) {
|
||||
currentQueries && (currentQueries = currentQueries.clone());
|
||||
previousOrParentTNode.flags |= TNodeFlags.hasContentQuery;
|
||||
|
@ -400,7 +401,6 @@ export function createLNodeObject(
|
|||
view: currentView,
|
||||
nodeInjector: nodeInjector,
|
||||
data: state,
|
||||
tNode: null !,
|
||||
dynamicLContainerNode: null
|
||||
};
|
||||
}
|
||||
|
@ -417,40 +417,41 @@ export function createLNodeObject(
|
|||
* @param attrs Any attrs for the native element, if applicable
|
||||
* @param data Any data that should be saved on the LNode
|
||||
*/
|
||||
export function createLNode(
|
||||
export function createNodeAtIndex(
|
||||
index: number, type: TNodeType.Element, native: RElement | RText | null, name: string | null,
|
||||
attrs: TAttributes | null, lViewData?: LViewData | null): LElementNode;
|
||||
export function createLNode(
|
||||
attrs: TAttributes | null, lViewData?: LViewData | null): TElementNode;
|
||||
export function createNodeAtIndex(
|
||||
index: number, type: TNodeType.View, native: null, name: null, attrs: null,
|
||||
lViewData: LViewData): LViewNode;
|
||||
export function createLNode(
|
||||
lViewData: LViewData): TViewNode;
|
||||
export function createNodeAtIndex(
|
||||
index: number, type: TNodeType.Container, native: RComment, name: string | null,
|
||||
attrs: TAttributes | null, lContainer: LContainer): LContainerNode;
|
||||
export function createLNode(
|
||||
attrs: TAttributes | null, lContainer: LContainer): TContainerNode;
|
||||
export function createNodeAtIndex(
|
||||
index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null,
|
||||
lProjection: null): LProjectionNode;
|
||||
export function createLNode(
|
||||
lProjection: null): TProjectionNode;
|
||||
export function createNodeAtIndex(
|
||||
index: number, type: TNodeType.ElementContainer, native: RComment, name: null,
|
||||
attrs: TAttributes | null, data: null): LElementContainerNode;
|
||||
export function createLNode(
|
||||
attrs: TAttributes | null, data: null): TElementContainerNode;
|
||||
export function createNodeAtIndex(
|
||||
index: number, type: TNodeType, native: RText | RElement | RComment | null, name: string | null,
|
||||
attrs: TAttributes | null, state?: null | LViewData | LContainer): LElementNode<extNode&
|
||||
LViewNode&LContainerNode&LProjectionNode {
|
||||
attrs: TAttributes | null, state?: null | LViewData | LContainer): TElementNode&TViewNode&
|
||||
TContainerNode&TElementContainerNode&TProjectionNode {
|
||||
const parent =
|
||||
isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent;
|
||||
|
||||
// Parents cannot cross component boundaries because components will be used in multiple places,
|
||||
// so it's only set if the view is the same.
|
||||
const parentInSameView = parent && tView && parent !== tView.node;
|
||||
const parentInSameView = parent && viewData && parent !== viewData[HOST_NODE];
|
||||
const tParent = parentInSameView ? parent as TElementNode | TContainerNode : null;
|
||||
|
||||
const isState = state != null;
|
||||
const node = createLNodeObject(type, viewData, null, native, isState ? state as any : null);
|
||||
let tNode: TNode;
|
||||
|
||||
if (index === -1 || type === TNodeType.View) {
|
||||
// View nodes are not stored in data because they can be added / removed at runtime (which
|
||||
// would cause indices to change). Their TNodes are instead stored in TView.node.
|
||||
node.tNode = (state ? (state as LViewData)[TVIEW].node : null) ||
|
||||
// would cause indices to change). Their TNodes are instead stored in tView.node.
|
||||
tNode = (state ? (state as LViewData)[TVIEW].node : null) ||
|
||||
createTNode(type, index, null, null, tParent, null);
|
||||
} else {
|
||||
const adjustedIndex = index + HEADER_OFFSET;
|
||||
|
@ -472,9 +473,9 @@ export function createLNode(
|
|||
}
|
||||
}
|
||||
|
||||
node.tNode = tData[adjustedIndex] as TNode;
|
||||
tNode = tData[adjustedIndex] as TNode;
|
||||
if (!tView.firstChild && type === TNodeType.Element) {
|
||||
tView.firstChild = node.tNode;
|
||||
tView.firstChild = tNode;
|
||||
}
|
||||
|
||||
// Now link ourselves into the tree.
|
||||
|
@ -482,13 +483,17 @@ export function createLNode(
|
|||
if (previousOrParentTNode.child == null && parentInSameView ||
|
||||
previousOrParentTNode.type === TNodeType.View) {
|
||||
// We are in the same view, which means we are adding content node to the parent View.
|
||||
previousOrParentTNode.child = node.tNode;
|
||||
previousOrParentTNode.child = tNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: temporary, remove when removing LNode.nodeInjector
|
||||
const parentLNode = index === -1 ? null : getParentLNode(node);
|
||||
if (parentLNode) node.nodeInjector = parentLNode.nodeInjector;
|
||||
// TODO: temporary, remove this when removing nodeInjector (bringing in fns to hello world)
|
||||
if (index !== -1 && !(viewData[FLAGS] & LViewFlags.IsRoot)) {
|
||||
const parentLNode: LNode|null = type === TNodeType.View ?
|
||||
getContainerNode(tNode, state as LViewData) :
|
||||
getParentOrContainerNode(tNode, viewData);
|
||||
parentLNode && (node.nodeInjector = parentLNode.nodeInjector);
|
||||
}
|
||||
|
||||
// View nodes and host elements need to set their host node (components do not save host TNodes)
|
||||
if ((type & TNodeType.ViewOrElement) === TNodeType.ViewOrElement && isState) {
|
||||
|
@ -496,15 +501,16 @@ export function createLNode(
|
|||
ngDevMode &&
|
||||
assertEqual(
|
||||
lViewData[HOST_NODE], null, 'lViewData[HOST_NODE] should not have been initialized');
|
||||
lViewData[HOST_NODE] = node;
|
||||
lViewData[HOST_NODE] = tNode as TElementNode | TViewNode;
|
||||
if (lViewData[TVIEW].firstTemplatePass) {
|
||||
lViewData[TVIEW].node = node.tNode as TViewNode | TElementNode;
|
||||
lViewData[TVIEW].node = tNode as TViewNode | TElementNode;
|
||||
}
|
||||
}
|
||||
|
||||
previousOrParentTNode = node.tNode;
|
||||
previousOrParentTNode = tNode;
|
||||
isParent = true;
|
||||
return node;
|
||||
return tNode as TElementNode & TViewNode & TContainerNode & TElementContainerNode &
|
||||
TProjectionNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -554,17 +560,23 @@ export function renderTemplate<T>(
|
|||
if (host == null) {
|
||||
resetComponentState();
|
||||
rendererFactory = providedRendererFactory;
|
||||
const tView =
|
||||
renderer = providedRendererFactory.createRenderer(null, null);
|
||||
|
||||
// We need to create a root view so it's possible to look up the host element through its index
|
||||
tView = createTView(-1, null, 1, 0, null, null, null);
|
||||
viewData = createLViewData(renderer, tView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot);
|
||||
|
||||
const componentTView =
|
||||
getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null);
|
||||
host = createLNode(
|
||||
-1, TNodeType.Element, hostNode, null, null,
|
||||
createLViewData(
|
||||
providedRendererFactory.createRenderer(null, null), tView, {}, LViewFlags.CheckAlways,
|
||||
sanitizer));
|
||||
const componentLView =
|
||||
createLViewData(renderer, componentTView, context, LViewFlags.CheckAlways, sanitizer);
|
||||
createNodeAtIndex(0, TNodeType.Element, hostNode, null, null, componentLView);
|
||||
host = loadElement(0);
|
||||
}
|
||||
const hostView = host.data !;
|
||||
ngDevMode && assertDefined(hostView, 'Host node should have an LView defined in host.data.');
|
||||
renderComponentOrTemplate(hostView, context, templateFn);
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
|
@ -573,9 +585,9 @@ export function renderTemplate<T>(
|
|||
* either through ViewContainerRef.createEmbeddedView() or TemplateRef.createEmbeddedView().
|
||||
* Such lViewNode will then be renderer with renderEmbeddedTemplate() (see below).
|
||||
*/
|
||||
export function createEmbeddedViewNode<T>(
|
||||
export function createEmbeddedViewAndNode<T>(
|
||||
tView: TView, context: T, declarationView: LViewData, renderer: Renderer3,
|
||||
queries?: LQueries | null): LViewNode {
|
||||
queries?: LQueries | null): LViewData {
|
||||
const _isParent = isParent;
|
||||
const _previousOrParentTNode = previousOrParentTNode;
|
||||
isParent = true;
|
||||
|
@ -588,11 +600,11 @@ export function createEmbeddedViewNode<T>(
|
|||
if (queries) {
|
||||
lView[QUERIES] = queries.createView();
|
||||
}
|
||||
const viewNode = createLNode(-1, TNodeType.View, null, null, null, lView);
|
||||
createNodeAtIndex(-1, TNodeType.View, null, null, null, lView);
|
||||
|
||||
isParent = _isParent;
|
||||
previousOrParentTNode = _previousOrParentTNode;
|
||||
return viewNode;
|
||||
return lView;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -610,7 +622,7 @@ export function renderEmbeddedTemplate<T>(
|
|||
const _isParent = isParent;
|
||||
const _previousOrParentTNode = previousOrParentTNode;
|
||||
let oldView: LViewData;
|
||||
if (viewToRender[PARENT] == null && viewToRender[CONTEXT] && !tView.template) {
|
||||
if (viewToRender[FLAGS] & LViewFlags.IsRoot) {
|
||||
// This is a root view inside the view tree
|
||||
tickRootContext(viewToRender[CONTEXT] as RootContext);
|
||||
} else {
|
||||
|
@ -618,7 +630,7 @@ export function renderEmbeddedTemplate<T>(
|
|||
isParent = true;
|
||||
previousOrParentTNode = null !;
|
||||
|
||||
oldView = enterView(viewToRender, tView.node);
|
||||
oldView = enterView(viewToRender, viewToRender[HOST_NODE]);
|
||||
namespaceHTML();
|
||||
tView.template !(rf, context);
|
||||
if (rf & RenderFlags.Update) {
|
||||
|
@ -654,7 +666,7 @@ export function nextContext<T = any>(level: number = 1): T {
|
|||
|
||||
export function renderComponentOrTemplate<T>(
|
||||
hostView: LViewData, componentOrContext: T, templateFn?: ComponentTemplate<T>) {
|
||||
const oldView = enterView(hostView, null);
|
||||
const oldView = enterView(hostView, hostView[HOST_NODE]);
|
||||
try {
|
||||
if (rendererFactory.begin) {
|
||||
rendererFactory.begin();
|
||||
|
@ -751,12 +763,11 @@ export function elementContainerStart(
|
|||
const native = renderer.createComment(ngDevMode ? 'ng-container' : '');
|
||||
|
||||
ngDevMode && assertDataInRange(index - 1);
|
||||
const tNode =
|
||||
createNodeAtIndex(index, TNodeType.ElementContainer, native, null, attrs || null, null);
|
||||
|
||||
const node: LElementContainerNode =
|
||||
createLNode(index, TNodeType.ElementContainer, native, null, attrs || null, null);
|
||||
|
||||
appendChild(getParentLNode(node), native, viewData);
|
||||
createDirectivesAndLocals(node, localRefs);
|
||||
appendChild(native, tNode, viewData);
|
||||
createDirectivesAndLocals(localRefs);
|
||||
}
|
||||
|
||||
/** Mark the end of the <ng-container>. */
|
||||
|
@ -768,10 +779,8 @@ export function elementContainerEnd(): void {
|
|||
previousOrParentTNode = previousOrParentTNode.parent !;
|
||||
}
|
||||
|
||||
// Can be read directly because ng-containers can't have style bindings
|
||||
const previousNode = viewData[previousOrParentTNode.index];
|
||||
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.ElementContainer);
|
||||
currentQueries && (currentQueries = currentQueries.addNode(previousNode));
|
||||
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentTNode));
|
||||
|
||||
queueLifecycleHooks(previousOrParentTNode.flags, tView);
|
||||
}
|
||||
|
@ -800,14 +809,13 @@ export function elementStart(
|
|||
|
||||
ngDevMode && assertDataInRange(index - 1);
|
||||
|
||||
const node: LElementNode =
|
||||
createLNode(index, TNodeType.Element, native !, name, attrs || null, null);
|
||||
const tNode = createNodeAtIndex(index, TNodeType.Element, native !, name, attrs || null, null);
|
||||
|
||||
if (attrs) {
|
||||
setUpAttributes(native, attrs);
|
||||
}
|
||||
appendChild(getParentLNode(node), native, viewData);
|
||||
createDirectivesAndLocals(node, localRefs);
|
||||
appendChild(native, tNode, viewData);
|
||||
createDirectivesAndLocals(localRefs);
|
||||
|
||||
// any immediate children of a component or template container must be pre-emptively
|
||||
// monkey-patched with the component view data so that the element can be inspected
|
||||
|
@ -840,7 +848,7 @@ export function elementCreate(name: string, overriddenRenderer?: Renderer3): REl
|
|||
return native;
|
||||
}
|
||||
|
||||
function nativeNodeLocalRefExtractor(lNode: LNodeWithLocalRefs): RNode {
|
||||
function nativeNodeLocalRefExtractor(lNode: LNodeWithLocalRefs, tNode: TNode): RNode {
|
||||
return lNode.native;
|
||||
}
|
||||
|
||||
|
@ -852,7 +860,7 @@ function nativeNodeLocalRefExtractor(lNode: LNodeWithLocalRefs): RNode {
|
|||
* @param localRefExtractor mapping function that extracts local ref value from LNode
|
||||
*/
|
||||
function createDirectivesAndLocals(
|
||||
lNode: LNodeWithLocalRefs, localRefs: string[] | null | undefined,
|
||||
localRefs: string[] | null | undefined,
|
||||
localRefExtractor: LocalRefExtractor = nativeNodeLocalRefExtractor) {
|
||||
if (firstTemplatePass) {
|
||||
ngDevMode && ngDevMode.firstTemplatePass++;
|
||||
|
@ -860,7 +868,7 @@ function createDirectivesAndLocals(
|
|||
} else {
|
||||
instantiateDirectivesDirectly();
|
||||
}
|
||||
saveResolvedLocalsInData(lNode, localRefExtractor);
|
||||
saveResolvedLocalsInData(localRefExtractor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -949,14 +957,6 @@ export function initChangeDetectorIfExisting(
|
|||
}
|
||||
}
|
||||
|
||||
export function isContentQueryHost(tNode: TNode): boolean {
|
||||
return (tNode.flags & TNodeFlags.hasContentQuery) !== 0;
|
||||
}
|
||||
|
||||
export function isComponent(tNode: TNode): boolean {
|
||||
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function instantiates the given directives.
|
||||
*/
|
||||
|
@ -1016,14 +1016,15 @@ function saveNameToExportMap(
|
|||
* Takes a list of local names and indices and pushes the resolved local variable values
|
||||
* to LViewData in the same order as they are loaded in the template with load().
|
||||
*/
|
||||
function saveResolvedLocalsInData(
|
||||
lNode: LNodeWithLocalRefs, localRefExtractor: LocalRefExtractor): void {
|
||||
const localNames = lNode.tNode.localNames;
|
||||
function saveResolvedLocalsInData(localRefExtractor: LocalRefExtractor): void {
|
||||
const localNames = previousOrParentTNode.localNames;
|
||||
const node = getPreviousOrParentNode() as LElementNode;
|
||||
if (localNames) {
|
||||
let localIndex = lNode.tNode.index + 1;
|
||||
let localIndex = previousOrParentTNode.index + 1;
|
||||
for (let i = 0; i < localNames.length; i += 2) {
|
||||
const index = localNames[i + 1] as number;
|
||||
const value = index === -1 ? localRefExtractor(lNode) : directives ![index];
|
||||
const value =
|
||||
index === -1 ? localRefExtractor(node, previousOrParentTNode) : directives ![index];
|
||||
viewData[localIndex++] = value;
|
||||
}
|
||||
}
|
||||
|
@ -1192,7 +1193,7 @@ export function hostElement(
|
|||
tag: string, rNode: RElement | null, def: ComponentDefInternal<any>,
|
||||
sanitizer?: Sanitizer | null): LElementNode {
|
||||
resetComponentState();
|
||||
const node = createLNode(
|
||||
const tNode = createNodeAtIndex(
|
||||
0, TNodeType.Element, rNode, null, null,
|
||||
createLViewData(
|
||||
renderer,
|
||||
|
@ -1201,12 +1202,12 @@ export function hostElement(
|
|||
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer));
|
||||
|
||||
if (firstTemplatePass) {
|
||||
node.tNode.flags = TNodeFlags.isComponent;
|
||||
tNode.flags = TNodeFlags.isComponent;
|
||||
if (def.diPublic) def.diPublic(def);
|
||||
tView.directives = [def];
|
||||
}
|
||||
|
||||
return node;
|
||||
return viewData[HEADER_OFFSET];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1316,7 +1317,7 @@ export function elementEnd(): void {
|
|||
previousOrParentTNode = previousOrParentTNode.parent !;
|
||||
}
|
||||
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Element);
|
||||
currentQueries && (currentQueries = currentQueries.addNode(getPreviousOrParentNode()));
|
||||
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentTNode));
|
||||
|
||||
queueLifecycleHooks(previousOrParentTNode.flags, tView);
|
||||
elementDepthCount--;
|
||||
|
@ -1366,12 +1367,12 @@ export function elementProperty<T>(
|
|||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void {
|
||||
if (value === NO_CHANGE) return;
|
||||
const node = loadElement(index) as LElementNode;
|
||||
const tNode = node.tNode;
|
||||
const tNode = getTNode(index);
|
||||
// if tNode.inputs is undefined, a listener has created outputs, but inputs haven't
|
||||
// yet been checked
|
||||
if (tNode && tNode.inputs === undefined) {
|
||||
// mark inputs as checked
|
||||
tNode.inputs = generatePropertyAliases(node.tNode.flags, BindingDirection.Input);
|
||||
tNode.inputs = generatePropertyAliases(tNode.flags, BindingDirection.Input);
|
||||
}
|
||||
|
||||
const inputData = tNode && tNode.inputs;
|
||||
|
@ -1547,7 +1548,7 @@ function getStylingContext(index: number): StylingContext {
|
|||
let stylingContext = load<StylingContext>(index);
|
||||
if (!Array.isArray(stylingContext)) {
|
||||
const lElement = stylingContext as any as LElementNode;
|
||||
const tNode = lElement.tNode;
|
||||
const tNode = getTNode(index);
|
||||
ngDevMode &&
|
||||
assertDefined(tNode.stylingTemplate, 'getStylingContext() called before elementStyling()');
|
||||
stylingContext = viewData[index + HEADER_OFFSET] =
|
||||
|
@ -1657,12 +1658,12 @@ export function text(index: number, value?: any): void {
|
|||
viewData[BINDING_INDEX], tView.bindingStartIndex,
|
||||
'text nodes should be created before any bindings');
|
||||
ngDevMode && ngDevMode.rendererCreateTextNode++;
|
||||
const textNode = createTextNode(value, renderer);
|
||||
const node = createLNode(index, TNodeType.Element, textNode, null, null);
|
||||
const textNative = createTextNode(value, renderer);
|
||||
const tNode = createNodeAtIndex(index, TNodeType.Element, textNative, null, null);
|
||||
|
||||
// Text nodes are self closing.
|
||||
isParent = false;
|
||||
appendChild(getParentLNode(node), textNode, viewData);
|
||||
appendChild(textNative, tNode, viewData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1700,7 +1701,7 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
|
|||
export function directiveCreate<T>(
|
||||
directiveDefIdx: number, directive: T,
|
||||
directiveDef: DirectiveDefInternal<T>| ComponentDefInternal<T>): T {
|
||||
const hostNode = getPreviousOrParentNode();
|
||||
const hostNode = getPreviousOrParentNode() !;
|
||||
const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, hostNode);
|
||||
|
||||
const isComponent = (directiveDef as ComponentDefInternal<T>).template;
|
||||
|
@ -1745,7 +1746,7 @@ function addComponentLogic<T>(
|
|||
// We need to set the host node/data here because when the component LNode was created,
|
||||
// we didn't yet know it was a component (just an element).
|
||||
(hostNode as{data: LViewData}).data = componentView;
|
||||
(componentView as LViewData)[HOST_NODE] = hostNode as LElementNode;
|
||||
(componentView as LViewData)[HOST_NODE] = getPreviousOrParentTNode() as TElementNode;
|
||||
|
||||
initChangeDetectorIfExisting(hostNode.nodeInjector, instance, componentView);
|
||||
|
||||
|
@ -1877,27 +1878,19 @@ function generateInitialInputs(
|
|||
/**
|
||||
* Creates a LContainer, either from a container instruction, or for a ViewContainerRef.
|
||||
*
|
||||
* @param parentLNode the LNode in which the container's content will be rendered
|
||||
* @param currentView The parent view of the LContainer
|
||||
* @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case
|
||||
* @returns LContainer
|
||||
*/
|
||||
export function createLContainer(
|
||||
parentLNode: LNode, currentView: LViewData, isForViewContainerRef?: boolean): LContainer {
|
||||
ngDevMode && assertDefined(parentLNode, 'containers should have a parent');
|
||||
let renderParent = canInsertNativeNode(parentLNode, currentView) ?
|
||||
parentLNode as LElementNode | LViewNode :
|
||||
null;
|
||||
if (renderParent && renderParent.tNode.type === TNodeType.View) {
|
||||
renderParent = getParentLNode(renderParent as LViewNode) !.data[RENDER_PARENT];
|
||||
}
|
||||
currentView: LViewData, isForViewContainerRef?: boolean): LContainer {
|
||||
return [
|
||||
isForViewContainerRef ? null : 0, // active index
|
||||
currentView, // parent
|
||||
null, // next
|
||||
null, // queries
|
||||
[], // views
|
||||
renderParent as LElementNode
|
||||
null // renderParent, set after node creation
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -1923,16 +1916,16 @@ export function template(
|
|||
tagName?: string | null, attrs?: TAttributes | null, localRefs?: string[] | null,
|
||||
localRefExtractor?: LocalRefExtractor) {
|
||||
// TODO: consider a separate node type for templates
|
||||
const node = containerInternal(index, tagName || null, attrs || null, localRefs || null);
|
||||
const tNode = containerInternal(index, tagName || null, attrs || null);
|
||||
|
||||
if (firstTemplatePass) {
|
||||
node.tNode.tViews = createTView(
|
||||
tNode.tViews = createTView(
|
||||
-1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null);
|
||||
}
|
||||
|
||||
createDirectivesAndLocals(node, localRefs, localRefExtractor);
|
||||
currentQueries && (currentQueries = currentQueries.addNode(node));
|
||||
queueLifecycleHooks(node.tNode.flags, tView);
|
||||
createDirectivesAndLocals(localRefs, localRefExtractor);
|
||||
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentTNode));
|
||||
queueLifecycleHooks(tNode.flags, tView);
|
||||
isParent = false;
|
||||
}
|
||||
|
||||
|
@ -1946,30 +1939,28 @@ export function template(
|
|||
* @param index The index of the container in the data array
|
||||
*/
|
||||
export function container(index: number): void {
|
||||
const node = containerInternal(index, null, null, null);
|
||||
firstTemplatePass && (node.tNode.tViews = []);
|
||||
const tNode = containerInternal(index, null, null);
|
||||
firstTemplatePass && (tNode.tViews = []);
|
||||
isParent = false;
|
||||
}
|
||||
|
||||
function containerInternal(
|
||||
index: number, tagName: string | null, attrs: TAttributes | null,
|
||||
localRefs: string[] | null): LContainerNode {
|
||||
index: number, tagName: string | null, attrs: TAttributes | null): TNode {
|
||||
ngDevMode && assertEqual(
|
||||
viewData[BINDING_INDEX], tView.bindingStartIndex,
|
||||
'container nodes should be created before any bindings');
|
||||
|
||||
const previousNode: LNode = getPreviousOrParentNode();
|
||||
const currentParent = isParent ? previousNode : getParentLNode(previousNode) !;
|
||||
const lContainer = createLContainer(currentParent, viewData);
|
||||
|
||||
const lContainer = createLContainer(viewData);
|
||||
ngDevMode && ngDevMode.rendererCreateComment++;
|
||||
const comment = renderer.createComment(ngDevMode ? 'container' : '');
|
||||
const node = createLNode(index, TNodeType.Container, comment, tagName, attrs, lContainer);
|
||||
appendChild(getParentLNode(node), comment, viewData);
|
||||
const tNode = createNodeAtIndex(index, TNodeType.Container, comment, tagName, attrs, lContainer);
|
||||
|
||||
lContainer[RENDER_PARENT] = getRenderParent(tNode, viewData);
|
||||
appendChild(comment, tNode, viewData);
|
||||
|
||||
// Containers are added to the current view tree instead of their embedded views
|
||||
// because views can be removed and re-inserted.
|
||||
addToViewTree(viewData, index + HEADER_OFFSET, node.data);
|
||||
addToViewTree(viewData, index + HEADER_OFFSET, lContainer);
|
||||
|
||||
if (currentQueries) {
|
||||
// prepare place for matching nodes from views inserted into a given container
|
||||
|
@ -1977,7 +1968,7 @@ function containerInternal(
|
|||
}
|
||||
|
||||
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
|
||||
return node;
|
||||
return tNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2021,7 +2012,7 @@ export function containerRefreshEnd(): void {
|
|||
|
||||
// remove extra views at the end of the container
|
||||
while (nextIndex < container.data[VIEWS].length) {
|
||||
removeView(container, nextIndex);
|
||||
removeView(container, previousOrParentTNode as TContainerNode, nextIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2059,7 +2050,8 @@ function refreshDynamicEmbeddedViews(lViewData: LViewData) {
|
|||
* @returns index of a found view or -1 if not found
|
||||
*/
|
||||
function scanForView(
|
||||
containerNode: LContainerNode, startIdx: number, viewBlockId: number): LViewData|null {
|
||||
containerNode: LContainerNode, tContainerNode: TContainerNode, startIdx: number,
|
||||
viewBlockId: number): LViewData|null {
|
||||
const views = containerNode.data[VIEWS];
|
||||
for (let i = startIdx; i < views.length; i++) {
|
||||
const viewAtPositionId = views[i][TVIEW].id;
|
||||
|
@ -2067,7 +2059,7 @@ function scanForView(
|
|||
return views[i];
|
||||
} else if (viewAtPositionId < viewBlockId) {
|
||||
// found a view that should not be at this position - remove
|
||||
removeView(containerNode, i);
|
||||
removeView(containerNode, tContainerNode, i);
|
||||
} else {
|
||||
// found a view with id greater than the one we are searching for
|
||||
// which means that required view doesn't exist and can't be found at
|
||||
|
@ -2094,12 +2086,11 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
|
|||
|
||||
ngDevMode && assertNodeType(containerTNode, TNodeType.Container);
|
||||
const lContainer = container.data;
|
||||
let viewNode: LViewNode|null;
|
||||
let viewToRender = scanForView(container, lContainer[ACTIVE_INDEX] !, viewBlockId);
|
||||
let viewToRender = scanForView(
|
||||
container, containerTNode as TContainerNode, lContainer[ACTIVE_INDEX] !, viewBlockId);
|
||||
|
||||
if (viewToRender) {
|
||||
isParent = true;
|
||||
viewNode = viewToRender[HOST_NODE] as LViewNode;
|
||||
enterView(viewToRender, viewToRender[TVIEW].node);
|
||||
} else {
|
||||
// When we create a new LView, we always reset the state of the instructions.
|
||||
|
@ -2112,13 +2103,13 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
|
|||
viewToRender[QUERIES] = lContainer[QUERIES] !.createView();
|
||||
}
|
||||
|
||||
viewNode = createLNode(viewBlockId, TNodeType.View, null, null, null, viewToRender);
|
||||
createNodeAtIndex(viewBlockId, TNodeType.View, null, null, null, viewToRender);
|
||||
enterView(viewToRender, viewToRender[TVIEW].node);
|
||||
}
|
||||
if (container) {
|
||||
if (creationMode) {
|
||||
// it is a new view, insert it into collection of views for a given container
|
||||
insertView(container, viewNode, lContainer[ACTIVE_INDEX] !);
|
||||
insertView(container, viewToRender, lContainer[ACTIVE_INDEX] !, -1);
|
||||
}
|
||||
lContainer[ACTIVE_INDEX] !++;
|
||||
}
|
||||
|
@ -2153,7 +2144,7 @@ function getOrCreateEmbeddedTView(
|
|||
|
||||
/** Marks the end of an embedded view. */
|
||||
export function embeddedViewEnd(): void {
|
||||
const viewHost = tView.node;
|
||||
const viewHost = viewData[HOST_NODE];
|
||||
refreshDescendantViews();
|
||||
leaveView(viewData[PARENT] !);
|
||||
previousOrParentTNode = viewHost !;
|
||||
|
@ -2208,15 +2199,15 @@ export function viewAttached(view: LViewData): boolean {
|
|||
* @param rawSelectors A collection of CSS selectors in the raw, un-parsed form
|
||||
*/
|
||||
export function projectionDef(selectors?: CssSelectorList[], textSelectors?: string[]): void {
|
||||
const componentNode: LElementNode = findComponentHost(viewData);
|
||||
const componentNode = findComponentView(viewData)[HOST_NODE] as TElementNode;
|
||||
|
||||
if (!componentNode.tNode.projection) {
|
||||
if (!componentNode.projection) {
|
||||
const noOfNodeBuckets = selectors ? selectors.length + 1 : 1;
|
||||
const pData: (TNode | null)[] = componentNode.tNode.projection =
|
||||
const pData: (TNode | null)[] = componentNode.projection =
|
||||
new Array(noOfNodeBuckets).fill(null);
|
||||
const tails: (TNode | null)[] = pData.slice();
|
||||
|
||||
let componentChild = componentNode.tNode.child;
|
||||
let componentChild: TNode|null = componentNode.child;
|
||||
|
||||
while (componentChild !== null) {
|
||||
const bucketIndex =
|
||||
|
@ -2243,7 +2234,7 @@ export function projectionDef(selectors?: CssSelectorList[], textSelectors?: str
|
|||
* a new array each time the function is called. Instead the array will be
|
||||
* re-used by each invocation. This works because the function is not reentrant.
|
||||
*/
|
||||
const projectionNodeStack: LProjectionNode[] = [];
|
||||
const projectionNodeStack: (LViewData | TNode)[] = [];
|
||||
|
||||
/**
|
||||
* Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
|
||||
|
@ -2255,63 +2246,52 @@ const projectionNodeStack: LProjectionNode[] = [];
|
|||
* - 1 based index of the selector from the {@link projectionDef}
|
||||
*/
|
||||
export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?: string[]): void {
|
||||
const node = createLNode(nodeIndex, TNodeType.Projection, null, null, attrs || null, null);
|
||||
const tProjectionNode =
|
||||
createNodeAtIndex(nodeIndex, TNodeType.Projection, null, null, attrs || null, null);
|
||||
|
||||
// We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views.
|
||||
if (node.tNode.projection === null) node.tNode.projection = selectorIndex;
|
||||
if (tProjectionNode.projection === null) tProjectionNode.projection = selectorIndex;
|
||||
|
||||
// `<ng-content>` has no content
|
||||
isParent = false;
|
||||
|
||||
// re-distribution of projectable nodes is stored on a component's view level
|
||||
const parent = getParentLNode(node);
|
||||
|
||||
const componentNode = findComponentHost(viewData);
|
||||
let nodeToProject = (componentNode.tNode.projection as(TNode | null)[])[selectorIndex];
|
||||
let projectedView = componentNode.view;
|
||||
const componentView = findComponentView(viewData);
|
||||
const componentNode = componentView[HOST_NODE] as TElementNode;
|
||||
let nodeToProject = (componentNode.projection as(TNode | null)[])[selectorIndex];
|
||||
let projectedView = componentView[PARENT] !;
|
||||
let projectionNodeIndex = -1;
|
||||
let renderParent: LElementNode|null = null;
|
||||
|
||||
while (nodeToProject) {
|
||||
if (nodeToProject.type === TNodeType.Projection) {
|
||||
// This node is re-projected, so we must go up the tree to get its projected nodes.
|
||||
const currentComponentHost = findComponentHost(projectedView);
|
||||
const firstProjectedNode = (currentComponentHost.tNode.projection as(
|
||||
TNode | null)[])[nodeToProject.projection as number];
|
||||
const currentComponentView = findComponentView(projectedView);
|
||||
const currentComponentHost = currentComponentView[HOST_NODE] as TElementNode;
|
||||
const firstProjectedNode =
|
||||
(currentComponentHost.projection as(TNode | null)[])[nodeToProject.projection as number];
|
||||
|
||||
if (firstProjectedNode) {
|
||||
projectionNodeStack[++projectionNodeIndex] = projectedView[nodeToProject.index];
|
||||
projectionNodeStack[++projectionNodeIndex] = nodeToProject;
|
||||
projectionNodeStack[++projectionNodeIndex] = projectedView;
|
||||
|
||||
nodeToProject = firstProjectedNode;
|
||||
projectedView = currentComponentHost.view;
|
||||
projectedView = currentComponentView[PARENT] !;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const lNode = projectedView[nodeToProject.index];
|
||||
const lNode = projectedView[nodeToProject.index] as LTextNode | LElementNode | LContainerNode;
|
||||
// This flag must be set now or we won't know that this node is projected
|
||||
// if the nodes are inserted into a container later.
|
||||
lNode.tNode.flags |= TNodeFlags.isProjected;
|
||||
if (canInsertNativeNode(parent, viewData)) {
|
||||
let grandparent: LContainerNode;
|
||||
if (renderParent == null) {
|
||||
renderParent = parent.tNode.type === TNodeType.View ?
|
||||
(grandparent = getParentLNode(parent) as LContainerNode) &&
|
||||
grandparent.data[RENDER_PARENT] ! :
|
||||
parent as LElementNode;
|
||||
}
|
||||
nodeToProject.flags |= TNodeFlags.isProjected;
|
||||
|
||||
appendProjectedNode(
|
||||
lNode as LTextNode | LElementNode | LContainerNode, parent, viewData, renderParent,
|
||||
projectedView);
|
||||
}
|
||||
appendProjectedNode(lNode, nodeToProject, tProjectionNode, viewData, projectedView);
|
||||
}
|
||||
|
||||
// If we are finished with a list of re-projected nodes, we need to get
|
||||
// back to the root projection node that was re-projected.
|
||||
if (nodeToProject.next === null && projectedView !== componentNode.view) {
|
||||
// move down into the view of the component we're projecting right now
|
||||
const lNode = projectionNodeStack[projectionNodeIndex--];
|
||||
nodeToProject = lNode.tNode;
|
||||
projectedView = lNode.view;
|
||||
if (nodeToProject.next === null && projectedView !== componentView[PARENT] !) {
|
||||
projectedView = projectionNodeStack[projectionNodeIndex--] as LViewData;
|
||||
nodeToProject = projectionNodeStack[projectionNodeIndex--] as TNode;
|
||||
}
|
||||
nodeToProject = nodeToProject.next;
|
||||
}
|
||||
|
@ -2383,12 +2363,12 @@ export function wrapListenerWithDirtyAndDefault(
|
|||
export function markViewDirty(view: LViewData): void {
|
||||
let currentView: LViewData = view;
|
||||
|
||||
while (currentView[PARENT] != null) {
|
||||
while (currentView && !(currentView[FLAGS] & LViewFlags.IsRoot)) {
|
||||
currentView[FLAGS] |= LViewFlags.Dirty;
|
||||
currentView = currentView[PARENT] !;
|
||||
}
|
||||
currentView[FLAGS] |= LViewFlags.Dirty;
|
||||
ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext');
|
||||
ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext should be defined');
|
||||
scheduleTick(currentView[CONTEXT] as RootContext);
|
||||
}
|
||||
|
||||
|
@ -2451,7 +2431,7 @@ function tickRootContext(rootContext: RootContext) {
|
|||
export function getRootView(component: any): LViewData {
|
||||
ngDevMode && assertDefined(component, 'component');
|
||||
let lViewData = readPatchedLViewData(component) !;
|
||||
while (lViewData[PARENT]) {
|
||||
while (lViewData && !(lViewData[FLAGS] & LViewFlags.IsRoot)) {
|
||||
lViewData = lViewData[PARENT] !;
|
||||
}
|
||||
return lViewData;
|
||||
|
@ -2523,7 +2503,7 @@ export function checkNoChangesInRootView(lViewData: LViewData): void {
|
|||
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
|
||||
export function detectChangesInternal<T>(hostView: LViewData, component: T) {
|
||||
const hostTView = hostView[TVIEW];
|
||||
const oldView = enterView(hostView, null);
|
||||
const oldView = enterView(hostView, hostView[HOST_NODE]);
|
||||
const templateFn = hostTView.template !;
|
||||
const viewQuery = hostTView.viewQuery;
|
||||
|
||||
|
@ -2790,6 +2770,10 @@ export function loadElement(index: number): LElementNode {
|
|||
return loadElementInternal(index, viewData);
|
||||
}
|
||||
|
||||
export function getTNode(index: number): TNode {
|
||||
return tView.data[index + HEADER_OFFSET] as TNode;
|
||||
}
|
||||
|
||||
/** Gets the current binding value. */
|
||||
export function getBinding(bindingIndex: number): any {
|
||||
ngDevMode && assertDataInRange(viewData[bindingIndex]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,51 +10,66 @@ import {assertDefined} from './assert';
|
|||
import {attachPatchData} from './context_discovery';
|
||||
import {callHooks} from './hooks';
|
||||
import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
||||
import {LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||
import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TTextNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
|
||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
||||
import {CLEANUP, CONTAINER_INDEX, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {readElementValue, stringify} from './util';
|
||||
import {CLEANUP, CONTAINER_INDEX, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {assertNodeType} from './node_assert';
|
||||
import {isComponent, readElementValue, stringify} from './util';
|
||||
|
||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
||||
|
||||
/** Retrieves the first child of a given node */
|
||||
export function getChildLNode(node: LNode): LNode|null {
|
||||
if (node.tNode.child) {
|
||||
const viewData = node.tNode.type === TNodeType.View ? node.data as LViewData : node.view;
|
||||
return readElementValue(viewData[node.tNode.child.index]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Retrieves the parent LNode of a given node. */
|
||||
export function getParentLNode(
|
||||
node: LContainerNode | LElementNode | LElementContainerNode | LTextNode |
|
||||
LProjectionNode): LElementNode|LElementContainerNode|LViewNode;
|
||||
export function getParentLNode(node: LViewNode): LContainerNode|null;
|
||||
export function getParentLNode(node: LElementContainerNode): LElementNode|LElementContainerNode|
|
||||
LViewNode;
|
||||
export function getParentLNode(node: LNode): LElementNode|LElementContainerNode|LContainerNode|
|
||||
LViewNode|null;
|
||||
export function getParentLNode(node: LNode): LElementNode|LElementContainerNode|LContainerNode|
|
||||
LViewNode|null {
|
||||
if (node.tNode.index === -1 && node.tNode.type === TNodeType.View) {
|
||||
// This is a dynamically created view inside a dynamic container.
|
||||
// If the host index is -1, the view has not yet been inserted, so it has no parent.
|
||||
const containerHostIndex = (node.data as LViewData)[CONTAINER_INDEX];
|
||||
return containerHostIndex === -1 ? null : node.view[containerHostIndex].dynamicLContainerNode;
|
||||
}
|
||||
const parent = node.tNode.parent;
|
||||
return readElementValue(parent ? node.view[parent.index] : node.view[HOST_NODE]);
|
||||
export function getParentLNode(tNode: TNode, currentView: LViewData): LElementNode|
|
||||
LElementContainerNode|LContainerNode|null {
|
||||
return tNode.parent == null ? getHostElementNode(currentView) :
|
||||
readElementValue(currentView[tNode.parent.index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves render parent LElementNode for a given view.
|
||||
* Might be null if a view is not yet attatched to any container.
|
||||
* Gets the host LElementNode given a view. Will return null if the host element is an
|
||||
* LViewNode, since they are being phased out.
|
||||
*/
|
||||
function getRenderParent(viewNode: LViewNode): LElementNode|null {
|
||||
const container = getParentLNode(viewNode);
|
||||
export function getHostElementNode(currentView: LViewData): LElementNode|null {
|
||||
const hostTNode = currentView[HOST_NODE] as TElementNode;
|
||||
return hostTNode && hostTNode.type !== TNodeType.View ?
|
||||
readElementValue(currentView[PARENT] ![hostTNode.index]) :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent LNode if it's not a view. If it's a view, it will instead return the view's
|
||||
* parent container node.
|
||||
*/
|
||||
export function getParentOrContainerNode(tNode: TNode, currentView: LViewData): LElementNode|
|
||||
LElementContainerNode|LContainerNode|null {
|
||||
const parentTNode = tNode.parent || currentView[HOST_NODE];
|
||||
return parentTNode && parentTNode.type === TNodeType.View ?
|
||||
getContainerNode(parentTNode, currentView) :
|
||||
getParentLNode(tNode, currentView);
|
||||
}
|
||||
|
||||
export function getContainerNode(tNode: TNode, embeddedView: LViewData): LContainerNode|null {
|
||||
if (tNode.index === -1) {
|
||||
// This is a dynamically created view inside a dynamic container.
|
||||
// If the host index is -1, the view has not yet been inserted, so it has no parent.
|
||||
const containerHostIndex = embeddedView[CONTAINER_INDEX];
|
||||
return containerHostIndex > -1 ?
|
||||
embeddedView[PARENT] ![containerHostIndex].dynamicLContainerNode :
|
||||
null;
|
||||
} else {
|
||||
// This is a inline view node (e.g. embeddedViewStart)
|
||||
return getParentLNode(tNode, embeddedView[PARENT] !) as LContainerNode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves render parent LElementNode for a given view.
|
||||
* Might be null if a view is not yet attached to any container.
|
||||
*/
|
||||
export function getContainerRenderParent(tViewNode: TViewNode, view: LViewData): LElementNode|null {
|
||||
const container = getContainerNode(tViewNode, view);
|
||||
return container ? container.data[RENDER_PARENT] : null;
|
||||
}
|
||||
|
||||
|
@ -129,16 +144,17 @@ function walkTNodeTree(
|
|||
lContainerNode.native;
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Projection) {
|
||||
const componentHost = findComponentHost(currentView !);
|
||||
const componentView = findComponentView(currentView !);
|
||||
const componentHost = componentView[HOST_NODE] as TElementNode;
|
||||
const head: TNode|null =
|
||||
(componentHost.tNode.projection as(TNode | null)[])[tNode.projection as number];
|
||||
(componentHost.projection as(TNode | null)[])[tNode.projection as number];
|
||||
|
||||
// Must store both the TNode and the view because this projection node could be nested
|
||||
// deeply inside embedded views, and we need to get back down to this particular nested view.
|
||||
projectionNodeStack[++projectionNodeIndex] = tNode;
|
||||
projectionNodeStack[++projectionNodeIndex] = currentView !;
|
||||
if (head) {
|
||||
currentView = (componentHost.data as LViewData)[PARENT] !;
|
||||
currentView = componentView[PARENT] !;
|
||||
nextTNode = currentView[TVIEW].data[head.index] as TNode;
|
||||
}
|
||||
} else {
|
||||
|
@ -191,19 +207,16 @@ function walkTNodeTree(
|
|||
* @param lViewData LViewData for which we want a host element node
|
||||
* @returns The host node
|
||||
*/
|
||||
export function findComponentHost(lViewData: LViewData): LElementNode {
|
||||
let viewRootLNode = lViewData[HOST_NODE];
|
||||
export function findComponentView(lViewData: LViewData): LViewData {
|
||||
let rootTNode = lViewData[HOST_NODE];
|
||||
|
||||
while (viewRootLNode.tNode.type === TNodeType.View) {
|
||||
ngDevMode && assertDefined(lViewData[PARENT], 'lViewData.parent');
|
||||
while (rootTNode && rootTNode.type === TNodeType.View) {
|
||||
ngDevMode && assertDefined(lViewData[PARENT], 'viewData.parent');
|
||||
lViewData = lViewData[PARENT] !;
|
||||
viewRootLNode = lViewData[HOST_NODE];
|
||||
rootTNode = lViewData[HOST_NODE];
|
||||
}
|
||||
|
||||
ngDevMode && assertNodeType(viewRootLNode.tNode, TNodeType.Element);
|
||||
ngDevMode && assertDefined(viewRootLNode.data, 'node.data');
|
||||
|
||||
return viewRootLNode as LElementNode;
|
||||
return lViewData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,24 +252,20 @@ export function createTextNode(value: any, renderer: Renderer3): RText {
|
|||
* to propagate deeply into the nested containers to remove all elements in the
|
||||
* views beneath it.
|
||||
*
|
||||
* @param container The container to which the root view belongs
|
||||
* @param viewToWalk The view from which elements should be added or removed
|
||||
* @param insertMode Whether or not elements should be added (if false, removing)
|
||||
* @param beforeNode The node before which elements should be added, if insert mode
|
||||
*/
|
||||
export function addRemoveViewFromContainer(
|
||||
container: LContainerNode, viewToWalk: LViewData, insertMode: true,
|
||||
beforeNode: RNode | null): void;
|
||||
viewToWalk: LViewData, insertMode: true, beforeNode: RNode | null): void;
|
||||
export function addRemoveViewFromContainer(viewToWalk: LViewData, insertMode: false): void;
|
||||
export function addRemoveViewFromContainer(
|
||||
container: LContainerNode, viewToWalk: LViewData, insertMode: false): void;
|
||||
export function addRemoveViewFromContainer(
|
||||
container: LContainerNode, viewToWalk: LViewData, insertMode: boolean,
|
||||
beforeNode?: RNode | null): void {
|
||||
const parentNode = container.data[RENDER_PARENT];
|
||||
viewToWalk: LViewData, insertMode: boolean, beforeNode?: RNode | null): void {
|
||||
const parentNode = getContainerRenderParent(viewToWalk[TVIEW].node as TViewNode, viewToWalk);
|
||||
const parent = parentNode ? parentNode.native : null;
|
||||
ngDevMode && assertNodeType(viewToWalk[TVIEW].node as TNode, TNodeType.View);
|
||||
if (parent) {
|
||||
const renderer = container.view[RENDERER];
|
||||
const renderer = viewToWalk[RENDERER];
|
||||
walkTNodeTree(
|
||||
viewToWalk, insertMode ? WalkTNodeTreeAction.Insert : WalkTNodeTreeAction.Detach, renderer,
|
||||
parentNode, beforeNode);
|
||||
|
@ -323,10 +332,10 @@ export function destroyViewTree(rootView: LViewData): void {
|
|||
* @param index The index at which to insert the view
|
||||
* @returns The inserted view
|
||||
*/
|
||||
export function insertView(container: LContainerNode, viewNode: LViewNode, index: number) {
|
||||
export function insertView(
|
||||
container: LContainerNode, lView: LViewData, index: number, containerIndex: number) {
|
||||
const state = container.data;
|
||||
const views = state[VIEWS];
|
||||
const lView = viewNode.data as LViewData;
|
||||
|
||||
if (index > 0) {
|
||||
// This is a new view, we need to add it to the children.
|
||||
|
@ -341,11 +350,11 @@ export function insertView(container: LContainerNode, viewNode: LViewNode, index
|
|||
lView[NEXT] = null;
|
||||
}
|
||||
|
||||
// Dynamically inserted views need a reference to their parent container'S host so it's
|
||||
// Dynamically inserted views need a reference to their parent container's host so it's
|
||||
// possible to jump from a view to its container's next when walking the node tree.
|
||||
if (viewNode.tNode.index === -1) {
|
||||
lView[CONTAINER_INDEX] = container.tNode.parent !.index;
|
||||
(viewNode as{view: LViewData}).view = container.view;
|
||||
if (containerIndex > -1) {
|
||||
lView[CONTAINER_INDEX] = containerIndex;
|
||||
lView[PARENT] = container.view;
|
||||
}
|
||||
|
||||
// Notify query that a new view has been added
|
||||
|
@ -367,23 +376,22 @@ export function insertView(container: LContainerNode, viewNode: LViewNode, index
|
|||
* @param removeIndex The index of the view to detach
|
||||
* @returns The detached view
|
||||
*/
|
||||
export function detachView(container: LContainerNode, removeIndex: number) {
|
||||
export function detachView(container: LContainerNode, removeIndex: number, detached: boolean) {
|
||||
const views = container.data[VIEWS];
|
||||
const viewToDetach = views[removeIndex];
|
||||
const viewNode = viewToDetach[HOST_NODE] as LViewNode;
|
||||
if (removeIndex > 0) {
|
||||
views[removeIndex - 1][NEXT] = viewToDetach[NEXT] as LViewData;
|
||||
}
|
||||
views.splice(removeIndex, 1);
|
||||
if (!container.tNode.detached) {
|
||||
addRemoveViewFromContainer(container, viewToDetach, false);
|
||||
if (!detached) {
|
||||
addRemoveViewFromContainer(viewToDetach, false);
|
||||
}
|
||||
|
||||
if (viewToDetach[QUERIES]) {
|
||||
viewToDetach[QUERIES] !.removeView();
|
||||
}
|
||||
viewToDetach[CONTAINER_INDEX] = -1;
|
||||
(viewNode as{view: LViewData | null}).view = null;
|
||||
viewToDetach[PARENT] = null;
|
||||
// Unsets the attached flag
|
||||
viewToDetach[FLAGS] &= ~LViewFlags.Attached;
|
||||
}
|
||||
|
@ -395,10 +403,11 @@ export function detachView(container: LContainerNode, removeIndex: number) {
|
|||
* @param removeIndex The index of the view to remove
|
||||
* @returns The removed view
|
||||
*/
|
||||
export function removeView(container: LContainerNode, removeIndex: number) {
|
||||
const viewToRemove = container.data[VIEWS][removeIndex];
|
||||
destroyLView(viewToRemove);
|
||||
detachView(container, removeIndex);
|
||||
export function removeView(
|
||||
container: LContainerNode, tContainer: TContainerNode, removeIndex: number) {
|
||||
const view = container.data[VIEWS][removeIndex];
|
||||
destroyLView(view);
|
||||
detachView(container, removeIndex, !!tContainer.detached);
|
||||
}
|
||||
|
||||
/** Gets the child of the given LViewData */
|
||||
|
@ -440,11 +449,12 @@ export function destroyLView(view: LViewData) {
|
|||
*/
|
||||
export function getParentState(state: LViewData | LContainer, rootView: LViewData): LViewData|
|
||||
LContainer|null {
|
||||
let node;
|
||||
if ((node = (state as LViewData) ![HOST_NODE]) && node.tNode.type === TNodeType.View) {
|
||||
let tNode;
|
||||
if (state.length >= HEADER_OFFSET && (tNode = (state as LViewData) ![HOST_NODE]) &&
|
||||
tNode.type === TNodeType.View) {
|
||||
// if it's an embedded view, the state needs to go up to the container, in case the
|
||||
// container has a next
|
||||
return getParentLNode(node) !.data as any;
|
||||
return getContainerNode(tNode, state as LViewData) !.data as any;
|
||||
} else {
|
||||
// otherwise, use parent view for containers or component views
|
||||
return state[PARENT] === rootView ? null : state[PARENT];
|
||||
|
@ -512,17 +522,24 @@ function executePipeOnDestroys(viewData: LViewData): void {
|
|||
}
|
||||
}
|
||||
|
||||
function canInsertNativeChildOfElement(parent: LElementNode, currentView: LViewData): boolean {
|
||||
if (parent.view !== currentView) {
|
||||
// If the Parent view is not the same as current view than we are inserting across
|
||||
// Views. This happens when we insert a root element of the component view into
|
||||
// the component host element and it should always be eager.
|
||||
return true;
|
||||
export function getRenderParent(tNode: TNode, currentView: LViewData): LElementNode|null {
|
||||
if (canInsertNativeNode(tNode, currentView)) {
|
||||
const hostTNode = currentView[HOST_NODE];
|
||||
return tNode.parent == null && hostTNode !.type === TNodeType.View ?
|
||||
getContainerRenderParent(hostTNode as TViewNode, currentView) :
|
||||
getParentLNode(tNode, currentView) as LElementNode;
|
||||
}
|
||||
// Parent elements can be a component which may have projection.
|
||||
if (parent.data === null) {
|
||||
// Parent is a regular non-component element. We should eagerly insert into it
|
||||
// since we know that this relationship will never be broken.
|
||||
return null;
|
||||
}
|
||||
|
||||
function canInsertNativeChildOfElement(tNode: TNode): boolean {
|
||||
// If the parent is null, then we are inserting across views. This happens when we
|
||||
// insert a root element of the component view into the component host element and it
|
||||
// should always be eager.
|
||||
if (tNode.parent == null ||
|
||||
// We should also eagerly insert if the parent is a regular, non-component element
|
||||
// since we know that this relationship will never be broken.
|
||||
tNode.parent.type === TNodeType.Element && !(tNode.parent.flags & TNodeFlags.isComponent)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -534,26 +551,21 @@ function canInsertNativeChildOfElement(parent: LElementNode, currentView: LViewD
|
|||
|
||||
/**
|
||||
* We might delay insertion of children for a given view if it is disconnected.
|
||||
* This might happen for 2 main reason:
|
||||
* - view is not inserted into any container (view was created but not iserted yet)
|
||||
* This might happen for 2 main reasons:
|
||||
* - view is not inserted into any container (view was created but not inserted yet)
|
||||
* - view is inserted into a container but the container itself is not inserted into the DOM
|
||||
* (container might be part of projection or child of a view that is not inserted yet).
|
||||
*
|
||||
* In other words we can insert children of a given view this view was inserted into a container and
|
||||
* the container itself has it render parent determined.
|
||||
* In other words we can insert children of a given view if this view was inserted into a container
|
||||
* and
|
||||
* the container itself has its render parent determined.
|
||||
*/
|
||||
function canInsertNativeChildOfView(parent: LViewNode): boolean {
|
||||
ngDevMode && assertNodeType(parent.tNode, TNodeType.View);
|
||||
|
||||
function canInsertNativeChildOfView(viewTNode: TViewNode, view: LViewData): boolean {
|
||||
// Because we are inserting into a `View` the `View` may be disconnected.
|
||||
const grandParentContainer = getParentLNode(parent) as LContainerNode;
|
||||
if (grandParentContainer == null) {
|
||||
// The `View` is not inserted into a `Container` we have to delay insertion.
|
||||
return false;
|
||||
}
|
||||
ngDevMode && assertNodeType(grandParentContainer.tNode, TNodeType.Container);
|
||||
if (grandParentContainer.data[RENDER_PARENT] == null) {
|
||||
// The parent `Container` itself is disconnected. So we have to delay.
|
||||
const container = getContainerNode(viewTNode, view) !;
|
||||
if (container == null || container.data[RENDER_PARENT] == null) {
|
||||
// The `View` is not inserted into a `Container` or the parent `Container`
|
||||
// itself is disconnected. So we have to delay.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -580,31 +592,21 @@ function canInsertNativeChildOfView(parent: LViewNode): boolean {
|
|||
* @param currentView Current LView being processed.
|
||||
* @return boolean Whether the child should be inserted now (or delayed until later).
|
||||
*/
|
||||
export function canInsertNativeNode(parent: LNode, currentView: LViewData): boolean {
|
||||
// We can only insert into a Component or View. Any other type should be an Error.
|
||||
ngDevMode && assertNodeOfPossibleTypes(
|
||||
parent.tNode, TNodeType.Element, TNodeType.ElementContainer, TNodeType.View);
|
||||
export function canInsertNativeNode(tNode: TNode, currentView: LViewData): boolean {
|
||||
let currentNode = tNode;
|
||||
let parent: TNode|null = tNode.parent;
|
||||
|
||||
if (parent.tNode.type === TNodeType.Element) {
|
||||
// Parent is a regular element or a component
|
||||
return canInsertNativeChildOfElement(parent as LElementNode, currentView);
|
||||
} else if (parent.tNode.type === TNodeType.ElementContainer) {
|
||||
// Parent is an element container (ng-container).
|
||||
// Its grand-parent might be an element, view or a sequence of ng-container parents.
|
||||
let grandParent = getParentLNode(parent);
|
||||
while (grandParent !== null && grandParent.tNode.type === TNodeType.ElementContainer) {
|
||||
grandParent = getParentLNode(grandParent);
|
||||
}
|
||||
if (grandParent === null) {
|
||||
return false;
|
||||
} else if (grandParent.tNode.type === TNodeType.Element) {
|
||||
return canInsertNativeChildOfElement(grandParent as LElementNode, currentView);
|
||||
} else {
|
||||
return canInsertNativeChildOfView(grandParent as LViewNode);
|
||||
}
|
||||
if (tNode.parent && tNode.parent.type === TNodeType.ElementContainer) {
|
||||
currentNode = getHighestElementContainer(tNode);
|
||||
parent = currentNode.parent;
|
||||
}
|
||||
if (parent === null) parent = currentView[HOST_NODE];
|
||||
|
||||
if (parent && parent.type === TNodeType.View) {
|
||||
return canInsertNativeChildOfView(parent as TViewNode, currentView);
|
||||
} else {
|
||||
// Parent is a View.
|
||||
return canInsertNativeChildOfView(parent as LViewNode);
|
||||
// Parent is a regular element or a component
|
||||
return canInsertNativeChildOfElement(currentNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,58 +629,79 @@ function nativeInsertBefore(
|
|||
*
|
||||
* The element insertion might be delayed {@link canInsertNativeNode}.
|
||||
*
|
||||
* @param parent The parent to which to append the child
|
||||
* @param child The child that should be appended
|
||||
* @param childEl The child that should be appended
|
||||
* @param childTNode The TNode of the child element
|
||||
* @param currentView The current LView
|
||||
* @returns Whether or not the child was appended
|
||||
*/
|
||||
export function appendChild(parent: LNode, child: RNode | null, currentView: LViewData): boolean {
|
||||
if (child !== null && canInsertNativeNode(parent, currentView)) {
|
||||
export function appendChild(
|
||||
childEl: RNode | null, childTNode: TNode, currentView: LViewData): boolean {
|
||||
const parentLNode = getParentLNode(childTNode, currentView);
|
||||
const parentEl = parentLNode ? parentLNode.native : null;
|
||||
|
||||
if (childEl !== null && canInsertNativeNode(childTNode, currentView)) {
|
||||
const renderer = currentView[RENDERER];
|
||||
if (parent.tNode.type === TNodeType.View) {
|
||||
const container = getParentLNode(parent) as LContainerNode;
|
||||
const parentTNode: TNode = childTNode.parent || currentView[HOST_NODE] !;
|
||||
|
||||
if (parentTNode.type === TNodeType.View) {
|
||||
const container = getContainerNode(parentTNode, currentView) as LContainerNode;
|
||||
const renderParent = container.data[RENDER_PARENT];
|
||||
const views = container.data[VIEWS];
|
||||
const index = views.indexOf(currentView);
|
||||
const beforeNode = index + 1 < views.length ?
|
||||
(getChildLNode(views[index + 1][HOST_NODE]) !).native :
|
||||
container.native;
|
||||
nativeInsertBefore(renderer, renderParent !.native, child, beforeNode);
|
||||
} else if (parent.tNode.type === TNodeType.ElementContainer) {
|
||||
const beforeNode = parent.native;
|
||||
let grandParent = getParentLNode(parent as LElementContainerNode);
|
||||
while (grandParent.tNode.type === TNodeType.ElementContainer) {
|
||||
grandParent = getParentLNode(grandParent as LElementContainerNode);
|
||||
}
|
||||
if (grandParent.tNode.type === TNodeType.View) {
|
||||
const renderParent = getRenderParent(grandParent as LViewNode);
|
||||
nativeInsertBefore(renderer, renderParent !.native, child, beforeNode);
|
||||
} else {
|
||||
nativeInsertBefore(renderer, (grandParent as LElementNode).native, child, beforeNode);
|
||||
}
|
||||
nativeInsertBefore(
|
||||
renderer, renderParent !.native, childEl, getBeforeNodeForView(index, views, container));
|
||||
} else if (parentTNode.type === TNodeType.ElementContainer) {
|
||||
let elementContainer = getHighestElementContainer(childTNode);
|
||||
let node: LElementNode = getRenderParent(elementContainer, currentView) !;
|
||||
nativeInsertBefore(renderer, node.native, childEl, parentEl);
|
||||
} else {
|
||||
isProceduralRenderer(renderer) ? renderer.appendChild(parent.native !as RElement, child) :
|
||||
parent.native !.appendChild(child);
|
||||
isProceduralRenderer(renderer) ? renderer.appendChild(parentEl !as RElement, childEl) :
|
||||
parentEl !.appendChild(childEl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the top-level ng-container if ng-containers are nested.
|
||||
*
|
||||
* @param ngContainer The TNode of the starting ng-container
|
||||
* @returns tNode The TNode of the highest level ng-container
|
||||
*/
|
||||
function getHighestElementContainer(ngContainer: TNode): TNode {
|
||||
while (ngContainer.parent != null && ngContainer.parent.type === TNodeType.ElementContainer) {
|
||||
ngContainer = ngContainer.parent;
|
||||
}
|
||||
return ngContainer;
|
||||
}
|
||||
|
||||
export function getBeforeNodeForView(index: number, views: LViewData[], container: LContainerNode) {
|
||||
if (index + 1 < views.length) {
|
||||
const view = views[index + 1] as LViewData;
|
||||
const viewTNode = view[HOST_NODE] as TViewNode;
|
||||
return viewTNode.child ? readElementValue(view[viewTNode.child.index]).native :
|
||||
container.native;
|
||||
} else {
|
||||
return container.native;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the `child` element of the `parent` from the DOM.
|
||||
*
|
||||
* @param parent The parent from which to remove the child
|
||||
* @param parentEl The parent element from which to remove the child
|
||||
* @param child The child that should be removed
|
||||
* @param currentView The current LView
|
||||
* @returns Whether or not the child was removed
|
||||
*/
|
||||
export function removeChild(parent: LNode, child: RNode | null, currentView: LViewData): boolean {
|
||||
if (child !== null && canInsertNativeNode(parent, currentView)) {
|
||||
export function removeChild(tNode: TNode, child: RNode | null, currentView: LViewData): boolean {
|
||||
const parentNative = getParentLNode(tNode, currentView) !.native as RElement;
|
||||
if (child !== null && canInsertNativeNode(tNode, currentView)) {
|
||||
// We only remove the element if not in View or not projected.
|
||||
const renderer = currentView[RENDERER];
|
||||
isProceduralRenderer(renderer) ? renderer.removeChild(parent.native as RElement, child) :
|
||||
parent.native !.removeChild(child);
|
||||
isProceduralRenderer(renderer) ? renderer.removeChild(parentNative as RElement, child) :
|
||||
parentNative !.removeChild(child);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -688,47 +711,49 @@ export function removeChild(parent: LNode, child: RNode | null, currentView: LVi
|
|||
* Appends a projected node to the DOM, or in the case of a projected container,
|
||||
* appends the nodes from all of the container's active views to the DOM.
|
||||
*
|
||||
* @param node The node to process
|
||||
* @param currentParent The last parent element to be processed
|
||||
* @param projectedLNode The node to process
|
||||
* @param parentNode The last parent element to be processed
|
||||
* @param tProjectionNode
|
||||
* @param currentView Current LView
|
||||
* @param projectionView Projection view
|
||||
*/
|
||||
export function appendProjectedNode(
|
||||
node: LElementNode | LElementContainerNode | LTextNode | LContainerNode,
|
||||
currentParent: LElementNode | LElementContainerNode | LViewNode, currentView: LViewData,
|
||||
renderParent: LElementNode, parentView: LViewData): void {
|
||||
appendChild(currentParent, node.native, currentView);
|
||||
projectedLNode: LElementNode | LElementContainerNode | LTextNode | LContainerNode,
|
||||
projectedTNode: TNode, tProjectionNode: TNode, currentView: LViewData,
|
||||
projectionView: LViewData): void {
|
||||
appendChild(projectedLNode.native, tProjectionNode, currentView);
|
||||
|
||||
// the projected contents are processed while in the shadow view (which is the currentView)
|
||||
// therefore we need to extract the view where the host element lives since it's the
|
||||
// logical container of the content projected views
|
||||
attachPatchData(node.native, parentView);
|
||||
attachPatchData(projectedLNode.native, projectionView);
|
||||
|
||||
if (node.tNode.type === TNodeType.Container) {
|
||||
const renderParent = getRenderParent(tProjectionNode, currentView);
|
||||
|
||||
if (projectedTNode.type === TNodeType.Container) {
|
||||
// The node we are adding is a container and we are adding it to an element which
|
||||
// is not a component (no more re-projection).
|
||||
// Alternatively a container is projected at the root of a component's template
|
||||
// and can't be re-projected (as not content of any component).
|
||||
// Assign the final projection location in those cases.
|
||||
const lContainer = (node as LContainerNode).data;
|
||||
const lContainer = (projectedLNode as LContainerNode).data;
|
||||
lContainer[RENDER_PARENT] = renderParent;
|
||||
const views = lContainer[VIEWS];
|
||||
for (let i = 0; i < views.length; i++) {
|
||||
addRemoveViewFromContainer(node as LContainerNode, views[i], true, node.native);
|
||||
addRemoveViewFromContainer(views[i], true, projectedLNode.native);
|
||||
}
|
||||
} else if (node.tNode.type === TNodeType.ElementContainer) {
|
||||
let ngContainerChild = getChildLNode(node as LElementContainerNode);
|
||||
const parentView = currentView[PARENT] !;
|
||||
while (ngContainerChild) {
|
||||
} else if (projectedTNode.type === TNodeType.ElementContainer) {
|
||||
let ngContainerChildTNode: TNode|null = projectedTNode.child as TNode;
|
||||
while (ngContainerChildTNode) {
|
||||
let ngContainerChild = readElementValue(projectionView[ngContainerChildTNode.index]);
|
||||
appendProjectedNode(
|
||||
ngContainerChild as LElementNode | LElementContainerNode | LTextNode | LContainerNode,
|
||||
currentParent, currentView, renderParent, parentView);
|
||||
ngContainerChild = ngContainerChild.tNode.next ?
|
||||
readElementValue(ngContainerChild.view[ngContainerChild.tNode.next.index]) :
|
||||
null;
|
||||
ngContainerChildTNode, tProjectionNode, currentView, projectionView);
|
||||
ngContainerChildTNode = ngContainerChildTNode.next;
|
||||
}
|
||||
}
|
||||
if (node.dynamicLContainerNode) {
|
||||
node.dynamicLContainerNode.data[RENDER_PARENT] = renderParent;
|
||||
appendChild(currentParent, node.dynamicLContainerNode.native, currentView);
|
||||
if (projectedLNode.dynamicLContainerNode) {
|
||||
projectedLNode.dynamicLContainerNode.data[RENDER_PARENT] = renderParent;
|
||||
appendChild(projectedLNode.dynamicLContainerNode.native, tProjectionNode, currentView);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,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"
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue