refactor(ivy): revert LNode.data into LViewData[HOST] (#26424)

PR Close #26424
This commit is contained in:
Kara Erickson 2018-10-12 15:02:54 -07:00 committed by Miško Hevery
parent 45732e5b91
commit 931e603f80
26 changed files with 634 additions and 518 deletions

View File

@ -158,7 +158,6 @@ export {
} from './sanitization/bypass';
export {
LContext as ɵLContext,
getContext as ɵgetContext
} from './render3/context_discovery';
@ -168,6 +167,10 @@ export {
PlayerHandler as ɵPlayerHandler,
} from './render3/interfaces/player';
export {
LContext as ɵLContext,
} from './render3/interfaces/context';
export {
addPlayer as ɵaddPlayer,
getPlayers as ɵgetPlayers,

View File

@ -13,19 +13,22 @@ import {Injector} from '../di/injector';
import {Sanitizer} from '../sanitization/security';
import {assertComponentType, assertDefined} from './assert';
import {getLElementFromComponent, readPatchedLViewData} from './context_discovery';
import {getComponentViewByInstance} from './context_discovery';
import {getComponentDef} from './definition';
import {queueInitHooks, queueLifecycleHooks} from './hooks';
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, prefillHostVars, setHostBindings} from './instructions';
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createNodeAtIndex, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getOrCreateTView, leaveView, locateHostElement, prefillHostVars, resetComponentState, setHostBindings} from './instructions';
import {ComponentDef, ComponentType} from './interfaces/definition';
import {LElementNode, TNodeFlags} from './interfaces/node';
import {TNodeFlags, TNodeType} from './interfaces/node';
import {PlayerHandler} from './interfaces/player';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
import {getRootView, stringify} from './util';
import {RElement, RNode, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, HEADER_OFFSET, HOST, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
import {getRootView, readElementValue, readPatchedLViewData, stringify} from './util';
// Root component will always have an element index of 0 and an injector size of 1
const ROOT_EXPANDO_INSTRUCTIONS = [0, 1];
/** Options that control how the component should be bootstrapped. */
export interface CreateComponentOptions {
/** Which renderer factory to use. */
@ -111,30 +114,29 @@ 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 hostRNode = 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), opts.playerHandler || null);
const renderer = rendererFactory.createRenderer(hostRNode, componentDef);
const rootView: LViewData = createLViewData(
rendererFactory.createRenderer(hostNode, componentDef),
createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
rootView[INJECTOR] = opts.injector || null;
const oldView = enterView(rootView, null);
let elementNode: LElementNode;
let component: T;
try {
if (rendererFactory.begin) rendererFactory.begin();
// Create element node at index 0 in data array
elementNode = hostElement(componentTag, hostNode, componentDef, sanitizer);
const componentView =
createRootComponentView(hostRNode, componentDef, rootView, renderer, sanitizer);
component = createRootComponent(
elementNode, componentDef, rootView, rootContext, opts.hostFeatures || null);
hostRNode, componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
executeInitAndContentHooks();
detectChangesInternal(elementNode.data as LViewData, component);
detectChangesInternal(componentView, component);
} finally {
leaveView(oldView);
if (rendererFactory.end) rendererFactory.end();
@ -143,19 +145,55 @@ export function renderComponent<T>(
return component;
}
/**
* Creates the root component view and the root component node.
*
* @param rNode Render host element.
* @param def ComponentDef
* @param rootView The parent view where the host node is stored
* @param renderer The current renderer
* @param sanitizer The sanitizer, if provided
*
* @returns Component view created
*/
export function createRootComponentView(
rNode: RElement | null, def: ComponentDef<any>, rootView: LViewData, renderer: Renderer3,
sanitizer?: Sanitizer | null): LViewData {
resetComponentState();
const tView = rootView[TVIEW];
const componentView = createLViewData(
renderer,
getOrCreateTView(
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery),
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer);
const tNode = createNodeAtIndex(0, TNodeType.Element, rNode, null, null, componentView);
if (tView.firstTemplatePass) {
tView.expandoInstructions = ROOT_EXPANDO_INSTRUCTIONS.slice();
if (def.diPublic) def.diPublic(def);
tNode.flags =
rootView.length << TNodeFlags.DirectiveStartingIndexShift | TNodeFlags.isComponent;
}
// Store component view at node index, with node as the HOST
componentView[HOST] = rootView[HEADER_OFFSET];
return rootView[HEADER_OFFSET] = componentView;
}
/**
* 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>, rootView: LViewData,
rootContext: RootContext, hostFeatures: HostFeature[] | null): any {
hostRNode: RNode | null, componentView: LViewData, componentDef: ComponentDef<T>,
rootView: LViewData, rootContext: RootContext, hostFeatures: HostFeature[] | null): any {
// Create directive instance with factory() and store at next index in viewData
const component =
baseDirectiveCreate(rootView.length, componentDef.factory() as T, componentDef, elementNode);
baseDirectiveCreate(rootView.length, componentDef.factory() as T, componentDef, hostRNode);
rootContext.components.push(component);
(elementNode.data as LViewData)[CONTEXT] = component;
componentView[CONTEXT] = component;
hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef));
if (rootView[TVIEW].firstTemplatePass) prefillHostVars(componentDef.hostVars);
@ -217,7 +255,7 @@ function getRootContext(component: any): RootContext {
* @param component Component for which the host element should be retrieved.
*/
export function getHostElement<T>(component: T): HTMLElement {
return getLElementFromComponent(component).native as any;
return readElementValue(getComponentViewByInstance(component)).native as HTMLElement;
}
/**

View File

@ -17,13 +17,14 @@ import {RendererFactory2} from '../render/api';
import {Type} from '../type';
import {assertComponentType, assertDefined} from './assert';
import {LifecycleHooksFeature, createRootComponent, createRootContext} from './component';
import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
import {getComponentDef} from './definition';
import {adjustBlueprintForNewNode, createLViewData, createNodeAtIndex, createTView, elementCreate, enterView, getTNode, hostElement, locateHostElement, renderEmbeddedTemplate} from './instructions';
import {adjustBlueprintForNewNode, createLViewData, createNodeAtIndex, createTView, elementCreate, enterView, locateHostElement, renderEmbeddedTemplate} from './instructions';
import {ComponentDef, RenderFlags} from './interfaces/definition';
import {LElementNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {getTNode} from './util';
import {createElementRef} from './view_engine_compatibility';
import {RootViewRef, ViewRef} from './view_ref';
@ -109,7 +110,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
rendererFactory = domRendererFactory3;
}
const hostNode = isInternalRootView ?
const hostRNode = isInternalRootView ?
elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef)) :
locateHostElement(rendererFactory, rootSelectorOrNode);
@ -122,24 +123,23 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
ngModule.injector.get(ROOT_CONTEXT) :
createRootContext(requestAnimationFrame.bind(window));
const renderer = rendererFactory.createRenderer(hostRNode, this.componentDef);
// 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, rootFlags);
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
rootView[INJECTOR] = ngModule && ngModule.injector || null;
// rootView is the parent when bootstrapping
const oldView = enterView(rootView, null);
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);
tElementNode = getTNode(0) as TElementNode;
const componentView =
createRootComponentView(hostRNode, this.componentDef, rootView, renderer);
tElementNode = getTNode(0, rootView) 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.
@ -165,10 +165,10 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
// executed here?
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
component = createRootComponent(
elementNode, this.componentDef, rootView, rootContext, [LifecycleHooksFeature]);
hostRNode, componentView, this.componentDef, rootView, rootContext,
[LifecycleHooksFeature]);
// 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 {

View File

@ -8,59 +8,12 @@
import './ng_dev_mode';
import {assertEqual} from './assert';
import {ACTIVE_INDEX, HOST_NATIVE, LContainer} from './interfaces/container';
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
import {RElement} from './interfaces/renderer';
import {StylingContext, StylingIndex} from './interfaces/styling';
import {CONTEXT, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view';
import {CONTEXT, HEADER_OFFSET, HOST, LViewData, TVIEW} from './interfaces/view';
import {getComponentViewByIndex, readElementValue, readPatchedData} from './util';
/**
* This property will be monkey-patched on elements, components and directives
*/
export const MONKEY_PATCH_KEY_NAME = '__ngContext__';
/**
* The internal view context which is specific to a given DOM element, directive or
* component instance. Each value in here (besides the LViewData and element node details)
* can be present, null or undefined. If undefined then it implies the value has not been
* looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
*
* Each value will get filled when the respective value is examined within the getContext
* function. The component, element and each directive instance will share the same instance
* of the context.
*/
export interface LContext {
/**
* The component's parent view data.
*/
lViewData: LViewData;
/**
* The index instance of the node.
*/
nodeIndex: number;
/**
* The instance of the DOM node that is attached to the lNode.
*/
native: RElement;
/**
* The instance of the Component node.
*/
component: {}|null|undefined;
/**
* The list of active directives that exist on this element.
*/
directives: any[]|null|undefined;
/**
* The map of local references (local reference name => element or directive instance) that exist
* on this element.
*/
localRefs: {[key: string]: any}|null|undefined;
}
/** Returns the matching `LContext` data for a given DOM node, directive or component instance.
*
@ -187,29 +140,27 @@ function createLContext(lViewData: LViewData, lNodeIndex: number, native: REleme
}
/**
* A simplified lookup function for finding the LElementNode from a component instance.
* Takes a component instance and returns the view for that component.
*
* This function exists for tree-shaking purposes to avoid having to pull in everything
* that `getContext` has in the event that an Angular application doesn't need to have
* any programmatic access to an element's context (only change detection uses this function).
* @param componentInstance
* @returns The component's view
*/
export function getLElementFromComponent(componentInstance: {}): LElementNode {
export function getComponentViewByInstance(componentInstance: {}): LViewData {
let lViewData = readPatchedData(componentInstance);
let lNode: LElementNode;
let view: LViewData;
if (Array.isArray(lViewData)) {
const lNodeIndex = findViaComponent(lViewData, componentInstance);
lNode = readElementValue(lViewData[lNodeIndex]);
const context = createLContext(lViewData, lNodeIndex, lNode.native);
view = getComponentViewByIndex(lNodeIndex, lViewData);
const context = createLContext(lViewData, lNodeIndex, (view[HOST] as LElementNode).native);
context.component = componentInstance;
attachPatchData(componentInstance, context);
attachPatchData(context.native, context);
} else {
const context = lViewData as any as LContext;
lNode = readElementValue(context.lViewData[context.nodeIndex]);
view = getComponentViewByIndex(context.nodeIndex, context.lViewData);
}
return lNode;
return view;
}
/**
@ -220,22 +171,6 @@ export function attachPatchData(target: any, data: LViewData | LContext) {
target[MONKEY_PATCH_KEY_NAME] = data;
}
/**
* Returns the monkey-patch value data present on the target (which could be
* a component, directive or a DOM node).
*/
export function readPatchedData(target: any): LViewData|LContext|null {
return target[MONKEY_PATCH_KEY_NAME];
}
export function readPatchedLViewData(target: any): LViewData|null {
const value = readPatchedData(target);
if (value) {
return Array.isArray(value) ? value : (value as LContext).lViewData;
}
return null;
}
export function isComponentInstance(instance: any): boolean {
return instance && instance.constructor && instance.constructor.ngComponentDef;
}
@ -282,14 +217,14 @@ function findViaComponent(lViewData: LViewData, componentInstance: {}): number {
if (componentIndices) {
for (let i = 0; i < componentIndices.length; i++) {
const elementComponentIndex = componentIndices[i];
const lNodeData = readElementValue(lViewData[elementComponentIndex] !).data !;
if (lNodeData[CONTEXT] === componentInstance) {
const componentView = getComponentViewByIndex(elementComponentIndex, lViewData);
if (componentView[CONTEXT] === componentInstance) {
return elementComponentIndex;
}
}
} else {
const rootNode = lViewData[HEADER_OFFSET];
const rootComponent = rootNode.data[CONTEXT];
const rootComponentView = getComponentViewByIndex(HEADER_OFFSET, lViewData);
const rootComponent = rootComponentView[CONTEXT];
if (rootComponent === componentInstance) {
// we are dealing with the root element here therefore we know that the
// element is the very first element after the HEADER data in the lView
@ -391,27 +326,3 @@ function getDirectiveEndIndex(tNode: TNode, startIndex: number): number {
const count = tNode.flags & TNodeFlags.DirectiveCountMask;
return count ? (startIndex + count) : -1;
}
/**
* Takes the value of a slot in `LViewData` and returns the element node.
*
* Normally, element nodes are stored flat, but if the node has styles/classes on it,
* it might be wrapped in a styling context. Or if that node has a directive that injects
* ViewContainerRef, it may be wrapped in an LContainer.
*
* @param value The initial value in `LViewData`
*/
export function readElementValue(value: LElementNode | StylingContext | LContainer): LElementNode {
if (Array.isArray(value)) {
if (typeof value[ACTIVE_INDEX] === 'number') {
// This is an LContainer. It may also have a styling context.
value = value[HOST_NATIVE] as LElementNode | StylingContext;
return Array.isArray(value) ? value[StylingIndex.ElementPosition] ! : value;
} else {
// This is a StylingContext, which stores the element node at 0.
return value[StylingIndex.ElementPosition] as LElementNode;
}
} else {
return value; // Regular LNode is stored here
}
}

View File

@ -8,10 +8,13 @@
import {Injector} from '../di/injector';
import {assertDefined} from './assert';
import {LContext, discoverDirectives, discoverLocalRefs, getContext, isComponentInstance, readPatchedLViewData} from './context_discovery';
import {discoverDirectives, discoverLocalRefs, getContext, isComponentInstance} from './context_discovery';
import {NodeInjector} from './di';
import {LElementNode, TElementNode, TNode, TNodeFlags} from './interfaces/node';
import {LContext} from './interfaces/context';
import {TElementNode, TNode, TNodeFlags} from './interfaces/node';
import {CONTEXT, FLAGS, LViewData, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
import {getComponentViewByIndex, readPatchedLViewData} from './util';
/**
* NOTE: The following functions might not be ideal for core usage in Angular...
@ -61,8 +64,8 @@ export function getHostComponent<T = {}>(target: {}): T|null {
const context = loadContext(target);
const tNode = context.lViewData[TVIEW].data[context.nodeIndex] as TNode;
if (tNode.flags & TNodeFlags.isComponent) {
const lNode = context.lViewData[context.nodeIndex] as LElementNode;
return lNode.data ![CONTEXT] as any as T;
const componentView = getComponentViewByIndex(context.nodeIndex, context.lViewData);
return componentView[CONTEXT] as any as T;
}
return null;
}

View File

@ -7,13 +7,14 @@
*/
import {assertEqual, assertLessThan} from './assert';
import {NO_CHANGE, _getViewData, adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createNodeAtIndex, getRenderer, getTNode, load, loadElement, resetComponentState} from './instructions';
import {NO_CHANGE, _getViewData, adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createNodeAtIndex, getRenderer, load, loadElement, resetComponentState} from './instructions';
import {LContainer, NATIVE, RENDER_PARENT} from './interfaces/container';
import {LContainerNode, LNode, TElementNode, TNode, TNodeType} from './interfaces/node';
import {StylingContext} from './interfaces/styling';
import {BINDING_INDEX, HEADER_OFFSET, HOST_NODE, TVIEW} from './interfaces/view';
import {appendChild, createTextNode, removeChild} from './node_manipulation';
import {getLNode, isLContainer, stringify} from './util';
import {getNative, getTNode, isLContainer, stringify} from './util';
/**
@ -272,8 +273,7 @@ function appendI18nNode(tNode: TNode, parentTNode: TNode, previousTNode: TNode):
}
}
const native = getLNode(tNode, viewData).native;
appendChild(native, tNode, viewData);
appendChild(getNative(tNode, viewData), tNode, viewData);
const slotValue = viewData[tNode.index];
if (tNode.type !== TNodeType.Container && isLContainer(slotValue)) {
@ -320,7 +320,7 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
}
const renderer = getRenderer();
const startTNode = getTNode(startIndex);
const startTNode = getTNode(startIndex, viewData);
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
@ -329,7 +329,7 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
const instruction = instructions[i] as number;
switch (instruction & I18nInstructions.InstructionMask) {
case I18nInstructions.Element:
const elementTNode = getTNode(instruction & I18nInstructions.IndexMask);
const elementTNode = getTNode(instruction & I18nInstructions.IndexMask, viewData);
localPreviousTNode = appendI18nNode(elementTNode, localParentTNode, localPreviousTNode);
localParentTNode = elementTNode;
break;
@ -338,7 +338,7 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
case I18nInstructions.Any:
const nodeIndex = instruction & I18nInstructions.IndexMask;
localPreviousTNode =
appendI18nNode(getTNode(nodeIndex), localParentTNode, localPreviousTNode);
appendI18nNode(getTNode(nodeIndex, viewData), localParentTNode, localPreviousTNode);
break;
case I18nInstructions.Text:
if (ngDevMode) {
@ -365,7 +365,7 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
}
const removeIndex = instruction & I18nInstructions.IndexMask;
const removedNode: LNode|LContainerNode = loadElement(removeIndex);
const removedTNode = getTNode(removeIndex);
const removedTNode = getTNode(removeIndex, viewData);
removeChild(removedTNode, removedNode.native || null, viewData);
const slotValue = load(removeIndex) as LNode | LContainer | StylingContext;

View File

@ -13,23 +13,24 @@ import {Sanitizer} from '../sanitization/security';
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './assert';
import {attachPatchData, getLElementFromComponent, readElementValue, readPatchedLViewData} from './context_discovery';
import {attachPatchData, getComponentViewByInstance} from './context_discovery';
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
import {ACTIVE_INDEX, HOST_NATIVE, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
import {ACTIVE_INDEX, LContainer, VIEWS} from './interfaces/container';
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {INJECTOR_SIZE} from './interfaces/injector';
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LTextNode, 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, isProceduralRenderer} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling';
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getHostElementNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
import {allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
import {assertDataInRangeInternal, getLNode, getRootView, isContentQueryHost, isDifferent, isLContainer, loadElementInternal, loadInternal, stringify} from './util';
import {createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
import {getStylingContext} from './styling/util';
import {assertDataInRangeInternal, getComponentViewByIndex, getNative, getRootView, getTNode, isComponent, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, readPatchedLViewData, stringify} from './util';
/**
@ -139,12 +140,6 @@ 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|null {
return previousOrParentTNode == null || previousOrParentTNode === viewData[HOST_NODE] ?
getHostElementNode(viewData) :
getLNode(previousOrParentTNode, viewData);
}
export function getPreviousOrParentTNode(): TNode {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return previousOrParentTNode;
@ -252,9 +247,6 @@ export function getBindingRoot() {
return bindingRootIndex;
}
// Root component will always have an element index of 0 and an injector size of 1
const ROOT_EXPANDO_INSTRUCTIONS = [0, 1];
const enum BindingDirection {
Input,
Output,
@ -413,8 +405,8 @@ export function createLViewData<T>(
renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags,
sanitizer?: Sanitizer | null): LViewData {
const instance = tView.blueprint.slice() as LViewData;
instance[PARENT] = instance[DECLARATION_VIEW] = viewData;
instance[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit;
instance[PARENT] = instance[DECLARATION_VIEW] = viewData;
instance[CONTEXT] = context;
instance[INJECTOR] = viewData ? viewData[INJECTOR] : null;
instance[RENDERER] = renderer;
@ -422,17 +414,6 @@ export function createLViewData<T>(
return instance;
}
/**
* Creation of LNode object is extracted to a separate function so we always create LNode object
* with the same shape
* (same properties assigned in the same order).
*/
export function createLNodeObject(
type: TNodeType, native: RText | RElement | RComment | null, state: any): LElementNode&
LTextNode&LViewNode&LContainerNode&LProjectionNode {
return {native: native as any, data: state};
}
/**
* A common way of creating the LNode to make sure that all of them have same shape to
* keep the execution code monomorphic and fast.
@ -456,14 +437,14 @@ export function createNodeAtIndex(
attrs: TAttributes | null, data: null): TContainerNode;
export function createNodeAtIndex(
index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null,
lProjection: null): TProjectionNode;
data: null): TProjectionNode;
export function createNodeAtIndex(
index: number, type: TNodeType.ElementContainer, native: RComment, name: null,
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): TElementNode&TViewNode&
TContainerNode&TElementContainerNode&TProjectionNode {
attrs: TAttributes | null, state?: null | LViewData): TElementNode&TViewNode&TContainerNode&
TElementContainerNode&TProjectionNode {
const parent =
isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent;
@ -473,7 +454,7 @@ export function createNodeAtIndex(
const tParent = parentInSameView ? parent as TElementNode | TContainerNode : null;
const isState = state != null;
const node = createLNodeObject(type, native, isState ? state as any : null);
const node = {native: native as any};
let tNode: TNode;
if (index === -1 || type === TNodeType.View) {
@ -574,10 +555,10 @@ export function resetComponentState() {
*/
export function renderTemplate<T>(
hostNode: RElement, templateFn: ComponentTemplate<T>, consts: number, vars: number, context: T,
providedRendererFactory: RendererFactory3, host: LElementNode | null,
providedRendererFactory: RendererFactory3, hostView: LViewData | null,
directives?: DirectiveDefListOrFactory | null, pipes?: PipeDefListOrFactory | null,
sanitizer?: Sanitizer | null): LElementNode {
if (host == null) {
sanitizer?: Sanitizer | null): LViewData {
if (hostView == null) {
resetComponentState();
rendererFactory = providedRendererFactory;
renderer = providedRendererFactory.createRenderer(null, null);
@ -588,16 +569,13 @@ export function renderTemplate<T>(
const componentTView =
getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null);
const componentLView =
hostView =
createLViewData(renderer, componentTView, context, LViewFlags.CheckAlways, sanitizer);
createNodeAtIndex(0, TNodeType.Element, hostNode, null, null, componentLView);
host = loadElement(0);
createNodeAtIndex(0, TNodeType.Element, hostNode, null, null, hostView);
}
const hostView = host.data !;
ngDevMode && assertDefined(hostView, 'Host node should have an LView defined in host.data.');
renderComponentOrTemplate(hostView, context, templateFn);
return host;
return hostView;
}
/**
@ -877,10 +855,6 @@ export function elementCreate(name: string, overriddenRenderer?: Renderer3): REl
return native;
}
function nativeNodeLocalRefExtractor(tNode: TNode, currentView: LViewData): RNode {
return getLNode(tNode, currentView).native;
}
/**
* Creates directive instances and populates local refs.
*
@ -889,8 +863,7 @@ function nativeNodeLocalRefExtractor(tNode: TNode, currentView: LViewData): RNod
* @param localRefExtractor mapping function that extracts local ref value from LNode
*/
function createDirectivesAndLocals(
localRefs: string[] | null | undefined,
localRefExtractor: LocalRefExtractor = nativeNodeLocalRefExtractor) {
localRefs: string[] | null | undefined, localRefExtractor: LocalRefExtractor = getNative) {
if (!bindingsEnabled) return;
if (firstTemplatePass) {
ngDevMode && ngDevMode.firstTemplatePass++;
@ -1096,7 +1069,7 @@ function saveResolvedLocalsInData(localRefExtractor: LocalRefExtractor): void {
* @param pipes Pipe defs that should be saved on TView
* @returns TView
*/
function getOrCreateTView(
export function getOrCreateTView(
templateFn: ComponentTemplate<any>, consts: number, vars: number,
directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null,
viewQuery: ComponentQuery<any>| null): TView {
@ -1235,35 +1208,6 @@ export function locateHostElement(
return rNode;
}
/**
* Creates the host LNode.
*
* @param rNode Render host element.
* @param def ComponentDef
*
* @returns LElementNode created
*/
export function hostElement(
tag: string, rNode: RElement | null, def: ComponentDef<any>,
sanitizer?: Sanitizer | null): LElementNode {
resetComponentState();
const tNode = createNodeAtIndex(
0, TNodeType.Element, rNode, null, null,
createLViewData(
renderer,
getOrCreateTView(
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery),
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer));
if (firstTemplatePass) {
tView.expandoInstructions = ROOT_EXPANDO_INSTRUCTIONS.slice();
if (def.diPublic) def.diPublic(def);
tNode.flags =
viewData.length << TNodeFlags.DirectiveStartingIndexShift | TNodeFlags.isComponent;
}
return viewData[HEADER_OFFSET];
}
/**
* Adds an event listener to the current node.
*
@ -1282,17 +1226,17 @@ export function listener(
// add native event listener - applicable to elements only
if (tNode.type === TNodeType.Element) {
const node = getPreviousOrParentNode() as LElementNode;
const native = getNative(previousOrParentTNode, viewData) as RElement;
ngDevMode && ngDevMode.rendererAddEventListener++;
// In order to match current behavior, native DOM event listeners must be added for all
// events (including outputs).
if (isProceduralRenderer(renderer)) {
const cleanupFn = renderer.listen(node.native, eventName, listenerFn);
const cleanupFn = renderer.listen(native, eventName, listenerFn);
storeCleanupFn(viewData, cleanupFn);
} else {
const wrappedListener = wrapListenerWithPreventDefault(listenerFn);
node.native.addEventListener(eventName, wrappedListener, useCapture);
native.addEventListener(eventName, wrappedListener, useCapture);
const cleanupInstances = getCleanup(viewData);
cleanupInstances.push(wrappedListener);
if (firstTemplatePass) {
@ -1421,7 +1365,7 @@ 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 | LContainerNode | LElementContainerNode;
const tNode = getTNode(index);
const tNode = getTNode(index, viewData);
// if tNode.inputs is undefined, a listener has created outputs, but inputs haven't
// yet been checked
if (tNode && tNode.inputs === undefined) {
@ -1433,7 +1377,7 @@ export function elementProperty<T>(
let dataValue: PropertyAliasValue|undefined;
if (inputData && (dataValue = inputData[propName])) {
setInputsForProperty(dataValue, value);
if (tNode.type === TNodeType.Element) markDirtyIfOnPush(node as LElementNode);
if (isComponent(tNode)) markDirtyIfOnPush(index + HEADER_OFFSET);
} else if (tNode.type === TNodeType.Element) {
// It is assumed that the sanitizer is only added when the compiler determines that the property
// is risky, so sanitization can be done without further checks.
@ -1583,7 +1527,7 @@ function generatePropertyAliases(
*/
export function elementClassProp<T>(
index: number, stylingIndex: number, value: T | NO_CHANGE): void {
updateElementClassProp(getStylingContext(index), stylingIndex, value ? true : false);
updateElementClassProp(getStylingContext(index, viewData), stylingIndex, value ? true : false);
}
/**
@ -1630,34 +1574,6 @@ export function elementStyling<T>(
}
}
/**
* Retrieve the `StylingContext` at a given index.
*
* This method lazily creates the `StylingContext`. This is because in most cases
* we have styling without any bindings. Creating `StylingContext` eagerly would mean that
* every style declaration such as `<div style="color: red">` would result `StyleContext`
* which would create unnecessary memory pressure.
*
* @param index Index of the style allocation. See: `elementStyling`.
*/
function getStylingContext(index: number): StylingContext {
let slotValue = viewData[index + HEADER_OFFSET];
if (isLContainer(slotValue)) {
const lContainer = slotValue;
slotValue = lContainer[HOST_NATIVE];
if (!Array.isArray(slotValue)) {
return lContainer[HOST_NATIVE] =
allocStylingContext(slotValue, getTNode(index).stylingTemplate !);
}
} else if (!Array.isArray(slotValue)) {
// This is a regular ElementNode
return viewData[index + HEADER_OFFSET] =
allocStylingContext(slotValue, getTNode(index).stylingTemplate !);
}
return slotValue as StylingContext;
}
/**
* Apply all styling values to the element which have been queued by any styling instructions.
@ -1674,7 +1590,7 @@ function getStylingContext(index: number): StylingContext {
* index.)
*/
export function elementStylingApply<T>(index: number): void {
renderElementStyles(getStylingContext(index), renderer);
renderElementStyles(getStylingContext(index, viewData), renderer);
}
/**
@ -1713,7 +1629,7 @@ export function elementStyleProp<T>(
valueToAdd = value as any as string;
}
}
updateElementStyleProp(getStylingContext(index), styleIndex, valueToAdd);
updateElementStyleProp(getStylingContext(index, viewData), styleIndex, valueToAdd);
}
/**
@ -1740,7 +1656,7 @@ export function elementStyleProp<T>(
export function elementStylingMap<T>(
index: number, classes: {[key: string]: any} | string | null,
styles?: {[styleName: string]: any} | null): void {
updateStylingMap(getStylingContext(index), classes, styles);
updateStylingMap(getStylingContext(index, viewData), classes, styles);
}
//////////////////////////
@ -1800,11 +1716,12 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
*/
export function directiveCreate<T>(
directiveDefIdx: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>): T {
const hostNode = getLNode(previousOrParentTNode, viewData);
const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, hostNode);
const native = getNative(previousOrParentTNode, viewData);
const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, native);
if ((directiveDef as ComponentDef<T>).template) {
hostNode.data ![CONTEXT] = directive;
const componentView = getComponentViewByIndex(previousOrParentTNode.index, viewData);
componentView[CONTEXT] = directive;
}
if (firstTemplatePass) {
@ -1826,7 +1743,7 @@ export function directiveCreate<T>(
}
function addComponentLogic<T>(def: ComponentDef<T>): void {
const hostNode = getLNode(previousOrParentTNode, viewData);
const native = getNative(previousOrParentTNode, viewData);
const tView = getOrCreateTView(
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery);
@ -1836,13 +1753,15 @@ function addComponentLogic<T>(def: ComponentDef<T>): void {
const componentView = addToViewTree(
viewData, previousOrParentTNode.index as number,
createLViewData(
rendererFactory.createRenderer(hostNode.native as RElement, def), tView, null,
rendererFactory.createRenderer(native as RElement, def), tView, null,
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer()));
// 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] = previousOrParentTNode as TElementNode;
componentView[HOST_NODE] = previousOrParentTNode as TElementNode;
// Component view will always be created before any injected LContainers,
// so this is a regular LNode, wrap it with the component view
componentView[HOST] = viewData[previousOrParentTNode.index];
viewData[previousOrParentTNode.index] = componentView;
if (firstTemplatePass) {
queueComponentIndexForCheck();
@ -1859,15 +1778,15 @@ function addComponentLogic<T>(def: ComponentDef<T>): void {
*/
export function baseDirectiveCreate<T>(
index: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>,
hostNode: LNode): T {
native: RNode | null): T {
ngDevMode && assertEqual(
viewData[BINDING_INDEX], tView.bindingStartIndex,
'directives should be created before any bindings');
ngDevMode && assertPreviousIsParent();
attachPatchData(directive, viewData);
if (hostNode) {
attachPatchData(hostNode.native, viewData);
if (native) {
attachPatchData(native, viewData);
}
viewData[index] = directive;
@ -1897,7 +1816,7 @@ export function baseDirectiveCreate<T>(
}
if (directiveDef !.attributes != null && previousOrParentTNode.type == TNodeType.Element) {
setUpAttributes((hostNode as LElementNode).native, directiveDef !.attributes as string[]);
setUpAttributes(native as RElement, directiveDef !.attributes as string[]);
}
return directive;
@ -1990,12 +1909,12 @@ export function createLContainer(
native: RComment, isForViewContainerRef?: boolean): LContainer {
return [
isForViewContainerRef ? -1 : 0, // active index
[], // views
currentView, // parent
null, // next
null, // queries
hostLNode, // host native
native, // native
[], // views
getRenderParent(hostTNode, currentView) // renderParent
];
}
@ -2269,11 +2188,8 @@ export function embeddedViewEnd(): void {
export function componentRefresh<T>(
adjustedElementIndex: number, parentFirstTemplatePass: boolean): void {
ngDevMode && assertDataInRange(adjustedElementIndex);
const element = readElementValue(viewData[adjustedElementIndex]) as LElementNode;
const hostView = getComponentViewByIndex(adjustedElementIndex, viewData);
ngDevMode && assertNodeType(tView.data[adjustedElementIndex] as TNode, TNodeType.Element);
ngDevMode &&
assertDefined(element.data, `Component's host node should have an LViewData attached.`);
const hostView = element.data !;
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
@ -2465,10 +2381,10 @@ export function addToViewTree<T extends LViewData|LContainer>(
///////////////////////////////
/** If node is an OnPush component, marks its LViewData dirty. */
export function markDirtyIfOnPush(node: LElementNode): void {
// Because data flows down the component tree, ancestors do not need to be marked dirty
if (node.data && !(node.data[FLAGS] & LViewFlags.CheckAlways)) {
node.data[FLAGS] |= LViewFlags.Dirty;
export function markDirtyIfOnPush(viewIndex: number): void {
const view = getComponentViewByIndex(viewIndex, viewData);
if (!(view[FLAGS] & LViewFlags.CheckAlways)) {
view[FLAGS] |= LViewFlags.Dirty;
}
}
@ -2576,10 +2492,7 @@ function tickRootContext(rootContext: RootContext) {
* @param component The component which the change detection should be performed on.
*/
export function detectChanges<T>(component: T): void {
const hostNode = getLElementFromComponent(component) !;
ngDevMode &&
assertDefined(hostNode, 'Component host node should be attached to an LViewData instance.');
detectChangesInternal(hostNode.data !, component);
detectChangesInternal(getComponentViewByInstance(component) !, component);
}
/**
@ -2673,8 +2586,7 @@ function updateViewQuery<T>(viewQuery: ComponentQuery<{}>| null, component: T):
*/
export function markDirty<T>(component: T) {
ngDevMode && assertDefined(component, 'component');
const elementNode = getLElementFromComponent(component) !;
markViewDirty(elementNode.data as LViewData);
markViewDirty(getComponentViewByInstance(component));
}
///////////////////////////////
@ -2888,10 +2800,6 @@ export function loadElement(index: number): LElementNode {
return loadElementInternal(index, viewData);
}
export function getTNode(index: number): TNode {
return tView.data[index + HEADER_OFFSET] as TNode;
}
/** Gets the current binding value. */
export function getBinding(bindingIndex: number): any {
ngDevMode && assertDataInRange(viewData[bindingIndex]);

View File

@ -10,7 +10,8 @@ import {LContainerNode, LElementContainerNode, LElementNode} from './node';
import {LQueries} from './query';
import {RComment} from './renderer';
import {StylingContext} from './styling';
import {LViewData, NEXT, PARENT, QUERIES} from './view';
import {HOST, LViewData, NEXT, PARENT, QUERIES} from './view';
/**
* Below are constants for LContainer indices to help us look up LContainer members
@ -18,11 +19,10 @@ import {LViewData, NEXT, PARENT, QUERIES} from './view';
* Uglify will inline these when minifying so there shouldn't be a cost.
*/
export const ACTIVE_INDEX = 0;
// PARENT, NEXT, and QUERIES are indices 1, 2, and 3.
export const VIEWS = 1;
// PARENT, NEXT, QUERIES, and HOST are indices 2, 3, 4, and 5.
// As we already have these constants in LViewData, we don't need to re-create them.
export const HOST_NATIVE = 4;
export const NATIVE = 5;
export const VIEWS = 6;
export const NATIVE = 6;
export const RENDER_PARENT = 7;
/**
@ -43,6 +43,15 @@ export interface LContainer extends Array<any> {
*/
[ACTIVE_INDEX]: number;
/**
* A list of the container's currently active child views. Views will be inserted
* here as they are added and spliced from here when they are removed. We need
* to keep a record of current views so we know which views are already in the DOM
* (and don't need to be re-added) and so we can remove views from the DOM when they
* are no longer required.
*/
[VIEWS]: LViewData[];
/**
* Access to the parent view is necessary so we can propagate back
* up from inside a container to parent[NEXT].
@ -63,20 +72,11 @@ export interface LContainer extends Array<any> {
/** The host node of this LContainer. */
// TODO: Should contain just the native element once LNode is removed.
[HOST_NATIVE]: LElementNode|LContainerNode|LElementContainerNode|StylingContext;
[HOST]: LElementNode|LContainerNode|LElementContainerNode|StylingContext|LViewData;
/** The comment element that serves as an anchor for this LContainer. */
[NATIVE]: RComment;
/**
* A list of the container's currently active child views. Views will be inserted
* here as they are added and spliced from here when they are removed. We need
* to keep a record of current views so we know which views are already in the DOM
* (and don't need to be re-added) and so we can remove views from the DOM when they
* are no longer required.
*/
[VIEWS]: LViewData[];
/**
* Parent Element which will contain the location where all of the Views will be
* inserted into to.

View File

@ -0,0 +1,59 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {RElement} from './renderer';
import {LViewData} from './view';
/**
* This property will be monkey-patched on elements, components and directives
*/
export const MONKEY_PATCH_KEY_NAME = '__ngContext__';
/**
* The internal view context which is specific to a given DOM element, directive or
* component instance. Each value in here (besides the LViewData and element node details)
* can be present, null or undefined. If undefined then it implies the value has not been
* looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
*
* Each value will get filled when the respective value is examined within the getContext
* function. The component, element and each directive instance will share the same instance
* of the context.
*/
export interface LContext {
/**
* The component's parent view data.
*/
lViewData: LViewData;
/**
* The index instance of the node.
*/
nodeIndex: number;
/**
* The instance of the DOM node that is attached to the lNode.
*/
native: RElement;
/**
* The instance of the Component node.
*/
component: {}|null|undefined;
/**
* The list of active directives that exist on this element.
*/
directives: any[]|null|undefined;
/**
* The map of local references (local reference name => element or directive instance) that exist
* on this element.
*/
localRefs: {[key: string]: any}|null|undefined;
}

View File

@ -69,14 +69,6 @@ export interface LNode {
* - retrieve the sibling elements of text nodes whose creation / insertion has been delayed
*/
readonly native: RComment|RElement|RText|null;
/**
* If regular LElementNode, LTextNode, LContainerNode, and LProjectionNode then `data` will be
* null.
* If LElementNode with component, then `data` contains LViewData.
* If LViewNode, then `data` contains the LViewData.
*/
readonly data: LViewData|null;
}
@ -84,30 +76,22 @@ export interface LNode {
export interface LElementNode extends LNode {
/** The DOM element associated with this node. */
readonly native: RElement;
/** If Component then data has LView (light DOM) */
readonly data: LViewData|null;
}
/** LNode representing <ng-container>. */
export interface LElementContainerNode extends LNode {
/** The DOM comment associated with this node. */
readonly native: RComment;
readonly data: null;
}
/** LNode representing a #text node. */
export interface LTextNode extends LNode {
/** The text node associated with this node. */
native: RText;
readonly data: null;
}
/** Abstract node which contains root nodes of a view. */
export interface LViewNode extends LNode {
readonly native: null;
readonly data: LViewData;
}
export interface LViewNode extends LNode { readonly native: null; }
/** Abstract node container which contains other views. */
export interface LContainerNode extends LNode {
@ -119,14 +103,10 @@ export interface LContainerNode extends LNode {
* until the parent view is processed.
*/
native: RComment;
readonly data: null;
}
export interface LProjectionNode extends LNode {
readonly native: null;
readonly data: null;
}
export interface LProjectionNode extends LNode { readonly native: null; }
/**
* A set of marker values to be used in the attributes arrays. Those markers indicate that some

View File

@ -121,11 +121,6 @@ import {PlayerContext} from './player';
export interface StylingContext extends
Array<InitialStyles|{[key: string]: any}|number|string|boolean|LElementNode|StyleSanitizeFn|
PlayerContext|null> {
/**
* Location of element that is used as a target for this context.
*/
[StylingIndex.ElementPosition]: LElementNode|null;
/**
* Location of animation context (which contains the active players) for this element styling
* context.
@ -156,6 +151,11 @@ export interface StylingContext extends
*/
[StylingIndex.ClassOffsetPosition]: number;
/**
* Location of element that is used as a target for this context.
*/
[StylingIndex.ElementPosition]: LElementNode|null;
/**
* The last class value that was interpreted by elementStylingMap. This is cached
* So that the algorithm can exit early incase the value has not changed.
@ -201,17 +201,18 @@ export const enum StylingFlags {
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
export const enum StylingIndex {
// Position of where the initial styles are stored in the styling context
ElementPosition = 0,
// Position of where the initial styles are stored in the styling context
PlayerContext = 1,
PlayerContext = 0,
// Position of where the style sanitizer is stored within the styling context
StyleSanitizerPosition = 2,
StyleSanitizerPosition = 1,
// Position of where the initial styles are stored in the styling context
InitialStylesPosition = 3,
InitialStylesPosition = 2,
// Index of location where the start of single properties are stored. (`updateStyleProp`)
MasterFlagPosition = 4,
MasterFlagPosition = 3,
// Index of location where the class index offset value is located
ClassOffsetPosition = 5,
ClassOffsetPosition = 4,
// Position of where the initial styles are stored in the styling context
// This index must align with HOST, see interfaces/view.ts
ElementPosition = 5,
// Position of where the last string-based CSS class value was stored
PreviousMultiClassValue = 6,
// Position of where the last string-based CSS class value was stored

View File

@ -13,32 +13,34 @@ import {PlayerHandler} from '../interfaces/player';
import {LContainer} from './container';
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList} from './definition';
import {TElementNode, TNode, TViewNode} from './node';
import {LContainerNode, LElementContainerNode, LElementNode, TElementNode, TNode, TViewNode} from './node';
import {LQueries} from './query';
import {Renderer3} from './renderer';
import {StylingContext} from './styling';
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */
export const HEADER_OFFSET = 16;
export const HEADER_OFFSET = 17;
// Below are constants for LViewData indices to help us look up LViewData members
// without having to remember the specific indices.
// Uglify will inline these when minifying so there shouldn't be a cost.
export const TVIEW = 0;
export const PARENT = 1;
export const NEXT = 2;
export const QUERIES = 3;
export const FLAGS = 4;
export const HOST_NODE = 5;
export const BINDING_INDEX = 6;
export const CLEANUP = 7;
export const CONTEXT = 8;
export const INJECTOR = 9;
export const RENDERER = 10;
export const SANITIZER = 11;
export const TAIL = 12;
export const CONTAINER_INDEX = 13;
export const CONTENT_QUERIES = 14;
export const DECLARATION_VIEW = 15;
export const FLAGS = 1;
export const PARENT = 2;
export const NEXT = 3;
export const QUERIES = 4;
export const HOST = 5;
export const HOST_NODE = 6;
export const BINDING_INDEX = 7;
export const CLEANUP = 8;
export const CONTEXT = 9;
export const INJECTOR = 10;
export const RENDERER = 11;
export const SANITIZER = 12;
export const TAIL = 13;
export const CONTAINER_INDEX = 14;
export const CONTENT_QUERIES = 15;
export const DECLARATION_VIEW = 16;
// This interface replaces the real LViewData interface if it is an arg or a
// return value of a public instruction. This ensures we don't need to expose
@ -66,6 +68,9 @@ export interface LViewData extends Array<any> {
*/
[TVIEW]: TView;
/** Flags for this view. See LViewFlags for more info. */
[FLAGS]: LViewFlags;
/**
* The parent view is needed when we exit the view and must restore the previous
* `LViewData`. Without this, the render method would have to keep a stack of
@ -90,8 +95,13 @@ export interface LViewData extends Array<any> {
/** Queries active for this view - nodes from a view are reported to those queries. */
[QUERIES]: LQueries|null;
/** Flags for this view. See LViewFlags for more info. */
[FLAGS]: LViewFlags;
/**
* The host node for this LViewData instance, if this is a component view.
*
* If this is an embedded view, HOST will be null.
*/
// TODO: should store native elements directly when we remove LNode
[HOST]: LElementNode|LContainerNode|LElementContainerNode|StylingContext|null;
/**
* Pointer to the `TViewNode` or `TElementNode` which represents the root of the view.

View File

@ -7,16 +7,15 @@
*/
import {assertDefined} from './assert';
import {attachPatchData, readElementValue} from './context_discovery';
import {attachPatchData} from './context_discovery';
import {callHooks} from './hooks';
import {HOST_NATIVE, LContainer, NATIVE, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {LContainer, NATIVE, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
import {LContainerNode, LElementContainerNode, LElementNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, 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 {StylingIndex} from './interfaces/styling';
import {CLEANUP, CONTAINER_INDEX, 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 {getLNode, isLContainer, stringify} from './util';
import {getLNode, getNative, isLContainer, readElementValue, stringify} from './util';
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
@ -104,8 +103,7 @@ function walkTNodeTree(
let nextTNode: TNode|null = null;
const parent = renderParentNode ? renderParentNode.native : null;
if (tNode.type === TNodeType.Element) {
const elementNode = getLNode(tNode, currentView);
executeNodeAction(action, renderer, parent, elementNode.native !, beforeNode);
executeNodeAction(action, renderer, parent, getNative(tNode, currentView), beforeNode);
const nodeOrContainer = currentView[tNode.index];
if (isLContainer(nodeOrContainer)) {
// This element has an LContainer, and its comment needs to be handled
@ -397,13 +395,7 @@ export function removeView(
/** Gets the child of the given LViewData */
export function getLViewChild(viewData: LViewData): LViewData|LContainer|null {
const childIndex = viewData[TVIEW].childIndex;
if (childIndex === -1) return null;
const value: LElementNode|LContainerNode|LContainer = viewData[childIndex];
// If it's an array, it's an LContainer. Otherwise, it's a component node, so LViewData
// is stored in data.
return Array.isArray(value) ? value : value.data;
return childIndex === -1 ? null : viewData[childIndex];
}
/**
@ -668,7 +660,7 @@ export function getBeforeNodeForView(index: number, views: LViewData[], containe
if (index + 1 < views.length) {
const view = views[index + 1] as LViewData;
const viewTNode = view[HOST_NODE] as TViewNode;
return viewTNode.child ? getLNode(viewTNode.child, view).native : containerNative;
return viewTNode.child ? getNative(viewTNode.child, view) : containerNative;
} else {
return containerNative;
}
@ -706,7 +698,7 @@ export function removeChild(tNode: TNode, child: RNode | null, currentView: LVie
export function appendProjectedNode(
projectedTNode: TNode, tProjectionNode: TNode, currentView: LViewData,
projectionView: LViewData): void {
const native = getLNode(projectedTNode, projectionView).native;
const native = getNative(projectedTNode, projectionView);
appendChild(native, tProjectionNode, currentView);
// the projected contents are processed while in the shadow view (which is the currentView)

View File

@ -14,21 +14,6 @@ import {InitialStyles, StylingContext, StylingFlags, StylingIndex} from '../inte
import {EMPTY_ARR, EMPTY_OBJ, createEmptyStylingContext} from './util';
/**
* Used clone a copy of a pre-computed template of a styling context.
*
* A pre-computed template is designed to be computed once for a given element
* (instructions.ts has logic for caching this).
*/
export function allocStylingContext(
lElement: LElementNode | null, templateStyleContext: StylingContext): StylingContext {
// each instance gets a copy
const context = templateStyleContext.slice() as any as StylingContext;
context[StylingIndex.ElementPosition] = lElement;
return context;
}
/**
* Creates a styling context template where styling information is stored.
* Any styles that are later referenced using `updateStyleProp` must be

View File

@ -5,11 +5,16 @@
* 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 {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {LContext, getContext} from '../context_discovery';
import {getContext} from '../context_discovery';
import {ACTIVE_INDEX, LContainer} from '../interfaces/container';
import {LContext} from '../interfaces/context';
import {LElementNode} from '../interfaces/node';
import {PlayerContext} from '../interfaces/player';
import {InitialStyles, StylingContext, StylingIndex} from '../interfaces/styling';
import {FLAGS, HEADER_OFFSET, HOST, LViewData} from '../interfaces/view';
import {getTNode} from '../util';
export const EMPTY_ARR: any[] = [];
export const EMPTY_OBJ: {[key: string]: any} = {};
@ -18,10 +23,70 @@ export function createEmptyStylingContext(
element?: LElementNode | null, sanitizer?: StyleSanitizeFn | null,
initialStylingValues?: InitialStyles): StylingContext {
return [
element || null, null, sanitizer || null, initialStylingValues || [null], 0, 0, null, null
null, // PlayerContext
sanitizer || null, // StyleSanitizer
initialStylingValues || [null], // InitialStyles
0, // MasterFlags
0, // ClassOffset
element || null, // Element
null, // PreviousMultiClassValue
null // PreviousMultiStyleValue
];
}
/**
* Used clone a copy of a pre-computed template of a styling context.
*
* A pre-computed template is designed to be computed once for a given element
* (instructions.ts has logic for caching this).
*/
export function allocStylingContext(
lElement: LElementNode | null, templateStyleContext: StylingContext): StylingContext {
// each instance gets a copy
const context = templateStyleContext.slice() as any as StylingContext;
context[StylingIndex.ElementPosition] = lElement;
return context;
}
/**
* Retrieve the `StylingContext` at a given index.
*
* This method lazily creates the `StylingContext`. This is because in most cases
* we have styling without any bindings. Creating `StylingContext` eagerly would mean that
* every style declaration such as `<div style="color: red">` would result `StyleContext`
* which would create unnecessary memory pressure.
*
* @param index Index of the style allocation. See: `elementStyling`.
* @param viewData The view to search for the styling context
*/
export function getStylingContext(index: number, viewData: LViewData): StylingContext {
let storageIndex = index + HEADER_OFFSET;
let slotValue: LContainer|LViewData|StylingContext|LElementNode = viewData[storageIndex];
let wrapper: LContainer|LViewData|StylingContext = viewData;
while (Array.isArray(slotValue)) {
wrapper = slotValue;
slotValue = slotValue[HOST] as LViewData | StylingContext | LElementNode;
}
if (isStylingContext(wrapper)) {
return wrapper as StylingContext;
} else {
// This is an LViewData or an LContainer
const stylingTemplate = getTNode(index, viewData).stylingTemplate;
if (wrapper !== viewData) storageIndex = HOST;
return wrapper[storageIndex] = stylingTemplate ?
allocStylingContext(slotValue, stylingTemplate) :
createEmptyStylingContext(slotValue);
}
}
function isStylingContext(value: LViewData | LContainer | StylingContext) {
// Not an LViewData or an LContainer
return typeof value[FLAGS] !== 'number' && typeof value[ACTIVE_INDEX] !== 'number';
}
export function getOrCreatePlayerContext(target: {}, context?: LContext | null): PlayerContext {
context = context || getContext(target) !;
if (ngDevMode && !context) {
@ -30,11 +95,7 @@ export function getOrCreatePlayerContext(target: {}, context?: LContext | null):
}
const {lViewData, nodeIndex} = context;
const value = lViewData[nodeIndex];
let stylingContext = value as StylingContext;
if (!Array.isArray(value)) {
stylingContext = lViewData[nodeIndex] = createEmptyStylingContext(value as LElementNode);
}
const stylingContext = getStylingContext(nodeIndex - HEADER_OFFSET, lViewData);
return stylingContext[StylingIndex.PlayerContext] || allocPlayerContext(stylingContext);
}

View File

@ -9,11 +9,12 @@
import {devModeEqual} from '../change_detection/change_detection_util';
import {assertDefined, assertLessThan} from './assert';
import {readElementValue, readPatchedLViewData} from './context_discovery';
import {ACTIVE_INDEX, LContainer} from './interfaces/container';
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
import {LContainerNode, LElementContainerNode, LElementNode, LNode, TNode, TNodeFlags} from './interfaces/node';
import {RComment, RElement, RText} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling';
import {CONTEXT, FLAGS, HEADER_OFFSET, LViewData, LViewFlags, PARENT, RootContext, TData, TVIEW} from './interfaces/view';
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, LViewData, LViewFlags, PARENT, RootContext, TData, TVIEW, TView} from './interfaces/view';
/**
@ -92,11 +93,45 @@ export function loadElementInternal(index: number, arr: LViewData): LElementNode
return readElementValue(value);
}
/**
* Takes the value of a slot in `LViewData` and returns the element node.
*
* Normally, element nodes are stored flat, but if the node has styles/classes on it,
* it might be wrapped in a styling context. Or if that node has a directive that injects
* ViewContainerRef, it may be wrapped in an LContainer. Or if that node is a component,
* it will be wrapped in LViewData. It could even have all three, so we keep looping
* until we find something that isn't an array.
*
* @param value The initial value in `LViewData`
*/
export function readElementValue(value: LElementNode | StylingContext | LContainer | LViewData):
LElementNode {
while (Array.isArray(value)) {
value = value[HOST] as any;
}
return value;
}
export function getNative(tNode: TNode, hostView: LViewData): RElement|RText|RComment {
return getLNode(tNode, hostView).native;
}
// TODO(kara): remove when removing LNode.native
export function getLNode(tNode: TNode, hostView: LViewData): LElementNode|LContainerNode|
LElementContainerNode {
return readElementValue(hostView[tNode.index]);
}
export function getTNode(index: number, view: LViewData): TNode {
return view[TVIEW].data[index + HEADER_OFFSET] as TNode;
}
export function getComponentViewByIndex(nodeIndex: number, hostView: LViewData): LViewData {
// Could be an LViewData or an LContainer. If LContainer, unwrap to find LViewData.
const slotValue = hostView[nodeIndex];
return slotValue.length >= HEADER_OFFSET ? slotValue : slotValue[HOST];
}
export function isContentQueryHost(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.hasContentQuery) !== 0;
}
@ -128,3 +163,19 @@ export function getRootView(target: LViewData | {}): LViewData {
export function getRootContext(viewOrComponent: LViewData | {}): RootContext {
return getRootView(viewOrComponent)[CONTEXT] as RootContext;
}
/**
* Returns the monkey-patch value data present on the target (which could be
* a component, directive or a DOM node).
*/
export function readPatchedData(target: any): LViewData|LContext|null {
return target[MONKEY_PATCH_KEY_NAME];
}
export function readPatchedLViewData(target: any): LViewData|null {
const value = readPatchedData(target);
if (value) {
return Array.isArray(value) ? value : (value as LContext).lViewData;
}
return null;
}

View File

@ -28,7 +28,7 @@ import {RComment, RElement, Renderer3, isProceduralRenderer} from './interfaces/
import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getRenderParent, insertView, removeView} from './node_manipulation';
import {getLNode, isComponent, isLContainer} from './util';
import {getComponentViewByIndex, getNative, isComponent, isLContainer} from './util';
import {ViewRef} from './view_ref';
@ -60,7 +60,7 @@ export function createElementRef(
// TODO: Fix class name, should be ElementRef, but there appears to be a rollup bug
R3ElementRef = class ElementRef_ extends ElementRefToken {};
}
return new R3ElementRef(getLNode(tNode, view).native);
return new R3ElementRef(getNative(tNode, view));
}
let R3TemplateRef: {
@ -325,7 +325,7 @@ export function createViewRef(
hostTNode: TNode, hostView: LViewData, context: any): ViewEngine_ChangeDetectorRef {
if (isComponent(hostTNode)) {
const componentIndex = hostTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
const componentView = getLNode(hostTNode, hostView).data as LViewData;
const componentView = getComponentViewByIndex(hostTNode.index, hostView);
return new ViewRef(componentView, context, componentIndex);
} else if (hostTNode.type === TNodeType.Element) {
const hostComponentView = findComponentView(hostView);
@ -346,4 +346,4 @@ function getOrCreateRenderer2(view: LViewData): Renderer2 {
/** Returns a Renderer2 (or throws when application was bootstrapped with Renderer3) */
export function injectRenderer2(): Renderer2 {
return getOrCreateRenderer2(_getViewData());
}
}

View File

@ -69,7 +69,7 @@
"name": "HEADER_OFFSET"
},
{
"name": "HOST_NATIVE"
"name": "HOST"
},
{
"name": "HOST_NODE"
@ -374,9 +374,6 @@
{
"name": "createLContext"
},
{
"name": "createLNodeObject"
},
{
"name": "createLViewData"
},
@ -389,6 +386,9 @@
{
"name": "createRootComponent"
},
{
"name": "createRootComponentView"
},
{
"name": "createRootContext"
},
@ -551,6 +551,12 @@
{
"name": "getComponentDef"
},
{
"name": "getComponentViewByIndex"
},
{
"name": "getComponentViewByInstance"
},
{
"name": "getContainerRenderParent"
},
@ -590,9 +596,6 @@
{
"name": "getLContainer"
},
{
"name": "getLElementFromComponent"
},
{
"name": "getLNode"
},
@ -608,6 +611,9 @@
{
"name": "getMultiStartIndex"
},
{
"name": "getNative"
},
{
"name": "getOrCreateInjectable"
},
@ -644,9 +650,6 @@
{
"name": "getPreviousIndex"
},
{
"name": "getPreviousOrParentNode"
},
{
"name": "getPreviousOrParentTNode"
},
@ -698,9 +701,6 @@
{
"name": "hasValueChanged"
},
{
"name": "hostElement"
},
{
"name": "inject"
},
@ -734,6 +734,9 @@
{
"name": "isClassBased"
},
{
"name": "isComponent"
},
{
"name": "isComponentInstance"
},
@ -782,6 +785,9 @@
{
"name": "isSanitizable"
},
{
"name": "isStylingContext"
},
{
"name": "iterateListLike"
},
@ -824,9 +830,6 @@
{
"name": "nativeInsertBefore"
},
{
"name": "nativeNodeLocalRefExtractor"
},
{
"name": "nextContext"
},

View File

@ -39,7 +39,7 @@
"name": "HEADER_OFFSET"
},
{
"name": "HOST_NATIVE"
"name": "HOST"
},
{
"name": "HOST_NODE"
@ -155,9 +155,6 @@
{
"name": "componentRefresh"
},
{
"name": "createLNodeObject"
},
{
"name": "createLViewData"
},
@ -167,6 +164,9 @@
{
"name": "createRootComponent"
},
{
"name": "createRootComponentView"
},
{
"name": "createRootContext"
},
@ -230,6 +230,9 @@
{
"name": "getComponentDef"
},
{
"name": "getComponentViewByIndex"
},
{
"name": "getContainerRenderParent"
},
@ -254,6 +257,9 @@
{
"name": "getLViewChild"
},
{
"name": "getNative"
},
{
"name": "getOrCreateNodeInjector"
},
@ -284,9 +290,6 @@
{
"name": "getRenderParent"
},
{
"name": "hostElement"
},
{
"name": "invertObject"
},

View File

@ -54,7 +54,7 @@
"name": "HEADER_OFFSET"
},
{
"name": "HOST_NATIVE"
"name": "HOST"
},
{
"name": "HOST_NODE"
@ -437,9 +437,6 @@
{
"name": "createLContext"
},
{
"name": "createLNodeObject"
},
{
"name": "createLViewData"
},
@ -452,6 +449,9 @@
{
"name": "createRootComponent"
},
{
"name": "createRootComponentView"
},
{
"name": "createRootContext"
},
@ -602,6 +602,12 @@
{
"name": "getComponentDef"
},
{
"name": "getComponentViewByIndex"
},
{
"name": "getComponentViewByInstance"
},
{
"name": "getContainerRenderParent"
},
@ -635,9 +641,6 @@
{
"name": "getLContainer"
},
{
"name": "getLElementFromComponent"
},
{
"name": "getLNode"
},
@ -650,6 +653,9 @@
{
"name": "getMultiStartIndex"
},
{
"name": "getNative"
},
{
"name": "getOrCreateInjectable"
},
@ -683,9 +689,6 @@
{
"name": "getPreviousIndex"
},
{
"name": "getPreviousOrParentNode"
},
{
"name": "getPreviousOrParentTNode"
},
@ -731,9 +734,6 @@
{
"name": "hasValueChanged"
},
{
"name": "hostElement"
},
{
"name": "inject"
},
@ -761,6 +761,9 @@
{
"name": "invertObject"
},
{
"name": "isComponent"
},
{
"name": "isContentQueryHost"
},
@ -800,6 +803,9 @@
{
"name": "isProceduralRenderer"
},
{
"name": "isStylingContext"
},
{
"name": "iterateListLike"
},
@ -845,9 +851,6 @@
{
"name": "nativeInsertBefore"
},
{
"name": "nativeNodeLocalRefExtractor"
},
{
"name": "nextContext"
},

View File

@ -321,10 +321,10 @@
"name": "HEADER_OFFSET"
},
{
"name": "HOST_ATTR$1"
"name": "HOST"
},
{
"name": "HOST_NATIVE"
"name": "HOST_ATTR$1"
},
{
"name": "HOST_NODE"
@ -1319,9 +1319,6 @@
{
"name": "createLContext"
},
{
"name": "createLNodeObject"
},
{
"name": "createLViewData"
},
@ -1340,6 +1337,9 @@
{
"name": "createRootComponent"
},
{
"name": "createRootComponentView"
},
{
"name": "createRootContext"
},
@ -1610,6 +1610,12 @@
{
"name": "getComponentDef"
},
{
"name": "getComponentViewByIndex"
},
{
"name": "getComponentViewByInstance"
},
{
"name": "getContainerRenderParent"
},
@ -1673,9 +1679,6 @@
{
"name": "getLContainer"
},
{
"name": "getLElementFromComponent"
},
{
"name": "getLNode"
},
@ -1736,6 +1739,9 @@
{
"name": "getNamedFormat"
},
{
"name": "getNative"
},
{
"name": "getNgModuleDef"
},
@ -1793,9 +1799,6 @@
{
"name": "getPreviousIndex"
},
{
"name": "getPreviousOrParentNode"
},
{
"name": "getPreviousOrParentTNode"
},
@ -1865,9 +1868,6 @@
{
"name": "hasValueChanged"
},
{
"name": "hostElement"
},
{
"name": "hostReportError"
},
@ -2018,6 +2018,9 @@
{
"name": "isScheduler"
},
{
"name": "isStylingContext"
},
{
"name": "isTemplateElement"
},
@ -2111,9 +2114,6 @@
{
"name": "nativeInsertBefore"
},
{
"name": "nativeNodeLocalRefExtractor"
},
{
"name": "nextContext"
},

View File

@ -6,17 +6,18 @@
* found in the LICENSE file at https://angular.io/license
*/
import {TemplateRef, ViewContainerRef} from '@angular/core';
import {withBody} from '@angular/private/testing';
import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core';
import {getRenderedText, whenRendered} from '../../src/render3/component';
import {directiveInject} from '../../src/render3/di';
import {LifecycleHooksFeature, defineComponent, defineDirective} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, text, textBinding, tick} from '../../src/render3/instructions';
import {LifecycleHooksFeature, defineComponent, defineDirective, templateRefExtractor} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, reference, text, template, textBinding, tick} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer';
import {containerEl, createComponent, renderComponent, requestAnimationFrame} from './render_util';
import {ComponentFixture, containerEl, createComponent, renderComponent, requestAnimationFrame} from './render_util';
describe('change detection', () => {
describe('markDirty, detectChanges, whenRendered, getRenderedText', () => {
@ -86,6 +87,73 @@ describe('change detection', () => {
await whenRendered(myComp);
expect(getRenderedText(myComp)).toEqual('updated');
}));
it('should support detectChanges on components that have LContainers', () => {
let structuralComp !: StructuralComp;
class StructuralComp {
tmp !: TemplateRef<any>;
value = 'one';
constructor(public vcr: ViewContainerRef) {}
create() { this.vcr.createEmbeddedView(this.tmp); }
static ngComponentDef = defineComponent({
type: StructuralComp,
selectors: [['structural-comp']],
factory: () => structuralComp =
new StructuralComp(directiveInject(ViewContainerRef as any)),
inputs: {tmp: 'tmp'},
consts: 1,
vars: 1,
template: (rf: RenderFlags, ctx: StructuralComp) => {
if (rf & RenderFlags.Create) {
text(0);
}
if (rf & RenderFlags.Update) {
textBinding(0, bind(ctx.value));
}
}
});
}
function FooTemplate(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
text(0, 'Temp content');
}
}
/**
* <ng-template #foo>
* Temp content
* </ng-template>
* <structural-comp [tmp]="foo"></structural-comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
template(0, FooTemplate, 1, 0, '', null, ['foo', ''], templateRefExtractor);
element(2, 'structural-comp');
}
if (rf & RenderFlags.Update) {
const foo = reference(1) as any;
elementProperty(2, 'tmp', bind(foo));
}
}, 3, 1, [StructuralComp]);
const fixture = new ComponentFixture(App);
fixture.update();
expect(fixture.html).toEqual('<structural-comp>one</structural-comp>');
structuralComp.create();
fixture.update();
expect(fixture.html).toEqual('<structural-comp>one</structural-comp>Temp content');
structuralComp.value = 'two';
detectChanges(structuralComp);
expect(fixture.html).toEqual('<structural-comp>two</structural-comp>Temp content');
});
});
describe('onPush', () => {

View File

@ -81,7 +81,7 @@ describe('instructions', () => {
element(0, 'div', ['id', 'test', 'title', 'Hello']);
}, () => {}, 1);
const div = (t.hostNode.native as HTMLElement).querySelector('div') !;
const div = (t.hostElement as HTMLElement).querySelector('div') !;
expect(div.id).toEqual('test');
expect(div.title).toEqual('Hello');
expect(ngDevMode).toHaveProperties({
@ -109,7 +109,7 @@ describe('instructions', () => {
]);
}, () => {}, 1);
const div = (t.hostNode.native as HTMLElement).querySelector('div') !;
const div = (t.hostElement as HTMLElement).querySelector('div') !;
const attrs: any = div.attributes;
expect(attrs['id'].name).toEqual('id');
@ -179,7 +179,7 @@ describe('instructions', () => {
t.update(() => elementProperty(0, 'hidden', false));
// The hidden property would be true if `false` was stringified into `"false"`.
expect((t.hostNode.native as HTMLElement).querySelector('div') !.hidden).toEqual(false);
expect((t.hostElement as HTMLElement).querySelector('div') !.hidden).toEqual(false);
expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1,
tNode: 2, // 1 for div, 1 for host element

View File

@ -20,8 +20,9 @@ import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
import {NgIf} from './common_with_def';
import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util';
import {MONKEY_PATCH_KEY_NAME, getContext} from '../../src/render3/context_discovery';
import {getContext} from '../../src/render3/context_discovery';
import {StylingIndex} from '../../src/render3/interfaces/styling';
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
import {directiveInject} from '../../src/render3/di';
describe('render3 integration test', () => {
@ -1414,6 +1415,39 @@ describe('render3 integration test', () => {
expect(fixture.html).toEqual('<span class="existing"></span>');
});
it('should apply classes properly when nodes are components', () => {
const MyComp = createComponent('my-comp', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
text(0, 'Comp Content');
}
}, 1, 0, []);
/**
* <my-comp [class.active]="class"></my-comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'my-comp');
{ elementStyling(['active']); }
elementEnd();
}
if (rf & RenderFlags.Update) {
elementClassProp(0, 0, ctx.class);
elementStylingApply(0);
}
}, 1, 0, [MyComp]);
const fixture = new ComponentFixture(App);
fixture.component.class = true;
fixture.update();
expect(fixture.html).toEqual('<my-comp class="active">Comp Content</my-comp>');
fixture.component.class = false;
fixture.update();
expect(fixture.html).toEqual('<my-comp class="">Comp Content</my-comp>');
});
it('should apply classes properly when nodes have LContainers', () => {
let structuralComp !: StructuralComp;
@ -1448,7 +1482,7 @@ describe('render3 integration test', () => {
/**
* <ng-template #foo>
* Content
* Temp Content
* </ng-template>
* <structural-comp [class.active]="class" [tmp]="foo"></structural-comp>
*/
@ -1477,8 +1511,12 @@ describe('render3 integration test', () => {
fixture.update();
expect(fixture.html)
.toEqual('<structural-comp class="active">Comp Content</structural-comp>Temp Content');
});
fixture.component.class = false;
fixture.update();
expect(fixture.html)
.toEqual('<structural-comp class="">Comp Content</structural-comp>Temp Content');
});
});
});
@ -2168,12 +2206,11 @@ describe('render3 integration test', () => {
const div1 = hostElm.querySelector('div:first-child') !as any;
const div2 = hostElm.querySelector('div:last-child') !as any;
const context = getContext(hostElm) !;
const elementNode = context.lViewData[context.nodeIndex];
const elmData = elementNode.data !;
const componentView = context.lViewData[context.nodeIndex];
expect(elmData).toContain(myDir1Instance);
expect(elmData).toContain(myDir2Instance);
expect(elmData).toContain(myDir3Instance);
expect(componentView).toContain(myDir1Instance);
expect(componentView).toContain(myDir2Instance);
expect(componentView).toContain(myDir3Instance);
expect(Array.isArray((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();
expect(Array.isArray((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();
@ -2183,9 +2220,9 @@ describe('render3 integration test', () => {
const d2Context = getContext(myDir2Instance) !;
const d3Context = getContext(myDir3Instance) !;
expect(d1Context.lViewData).toEqual(elmData);
expect(d2Context.lViewData).toEqual(elmData);
expect(d3Context.lViewData).toEqual(elmData);
expect(d1Context.lViewData).toEqual(componentView);
expect(d2Context.lViewData).toEqual(componentView);
expect(d3Context.lViewData).toEqual(componentView);
expect((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d1Context);
expect((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d2Context);
@ -2349,7 +2386,7 @@ describe('render3 integration test', () => {
const context = getContext(child) !;
expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy();
const componentData = context.lViewData[context.nodeIndex].data;
const componentData = context.lViewData[context.nodeIndex];
const component = componentData[CONTEXT];
expect(component instanceof ChildComp).toBeTruthy();
expect(component[MONKEY_PATCH_KEY_NAME]).toBe(context.lViewData);

View File

@ -25,7 +25,7 @@ import {DirectiveDefList, DirectiveTypesOrFactory, PipeDef, PipeDefList, PipeTyp
import {LElementNode} from '../../src/render3/interfaces/node';
import {PlayerHandler} from '../../src/render3/interfaces/player';
import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
import {HEADER_OFFSET} from '../../src/render3/interfaces/view';
import {HEADER_OFFSET, LViewData} from '../../src/render3/interfaces/view';
import {Sanitizer} from '../../src/sanitization/security';
import {Type} from '../../src/type';
@ -55,7 +55,7 @@ function noop() {}
* - access to the render `html`.
*/
export class TemplateFixture extends BaseFixture {
hostNode: LElementNode;
hostView: LViewData;
private _directiveDefs: DirectiveDefList|null;
private _pipeDefs: PipeDefList|null;
private _sanitizer: Sanitizer|null;
@ -78,7 +78,7 @@ export class TemplateFixture extends BaseFixture {
this._pipeDefs = toDefs(pipes, extractPipeDef);
this._sanitizer = sanitizer || null;
this._rendererFactory = rendererFactory || domRendererFactory3;
this.hostNode = renderTemplate(
this.hostView = renderTemplate(
this.hostElement,
(rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
@ -99,8 +99,8 @@ export class TemplateFixture extends BaseFixture {
*/
update(updateBlock?: () => void): void {
renderTemplate(
this.hostNode.native, updateBlock || this.updateBlock, 0, this.vars, null !,
this._rendererFactory, this.hostNode, this._directiveDefs, this._pipeDefs, this._sanitizer);
this.hostElement, updateBlock || this.updateBlock, 0, this.vars, null !,
this._rendererFactory, this.hostView, this._directiveDefs, this._pipeDefs, this._sanitizer);
}
}
@ -152,7 +152,7 @@ export class ComponentFixture<T> extends BaseFixture {
export const document = ((typeof global == 'object' && global || window) as any).document;
export let containerEl: HTMLElement = null !;
let host: LElementNode|null;
let hostView: LViewData|null;
const isRenderer2 =
typeof process == 'object' && process.argv[3] && process.argv[3] === '--r=renderer2';
// tslint:disable-next-line:no-console
@ -181,7 +181,7 @@ export function resetDOM() {
containerEl = document.createElement('div');
containerEl.setAttribute('host', '');
document.body.appendChild(containerEl);
host = null;
hostView = null;
// TODO: assert that the global state is clean (e.g. ngData, previousOrParentNode, etc)
}
@ -192,9 +192,9 @@ export function renderToHtml(
template: ComponentTemplate<any>, ctx: any, consts: number = 0, vars: number = 0,
directives?: DirectiveTypesOrFactory | null, pipes?: PipeTypesOrFactory | null,
providedRendererFactory?: RendererFactory3 | null) {
host = renderTemplate(
hostView = renderTemplate(
containerEl, template, consts, vars, ctx, providedRendererFactory || testRendererFactory,
host, toDefs(directives, extractDirectiveDef), toDefs(pipes, extractPipeDef));
hostView, toDefs(directives, extractDirectiveDef), toDefs(pipes, extractPipeDef));
return toHtml(containerEl);
}

View File

@ -10,10 +10,10 @@ import {InitialStylingFlags, RenderFlags} from '../../../src/render3/interfaces/
import {LElementNode} from '../../../src/render3/interfaces/node';
import {Renderer3} from '../../../src/render3/interfaces/renderer';
import {StylingContext, StylingFlags, StylingIndex} from '../../../src/render3/interfaces/styling';
import {allocStylingContext, createStylingContextTemplate, isContextDirty, renderStyling as _renderStyling, setContextDirty, updateClassProp, updateStyleProp, updateStylingMap} from '../../../src/render3/styling/class_and_style_bindings';
import {createStylingContextTemplate, isContextDirty, renderStyling as _renderStyling, setContextDirty, updateClassProp, updateStyleProp, updateStylingMap} from '../../../src/render3/styling/class_and_style_bindings';
import {allocStylingContext} from '../../../src/render3/styling/util';
import {defaultStyleSanitizer} from '../../../src/sanitization/sanitization';
import {StyleSanitizeFn} from '../../../src/sanitization/style_sanitizer';
import {renderToHtml} from '../render_util';
describe('styling', () => {
@ -110,19 +110,19 @@ describe('styling', () => {
describe('createStylingContextTemplate', () => {
it('should initialize empty template', () => {
const template = initContext();
expect(template).toEqual([element, null, null, [null], cleanStyle(0, 8), 0, null, null]);
expect(template).toEqual([null, null, [null], cleanStyle(0, 8), 0, element, null, null]);
});
it('should initialize static styles', () => {
const template =
initContext([InitialStylingFlags.VALUES_MODE, 'color', 'red', 'width', '10px']);
expect(template).toEqual([
element,
null,
null,
[null, 'red', '10px'],
dirtyStyle(0, 14), //
0,
element,
null,
null,
@ -321,12 +321,12 @@ describe('styling', () => {
updateStyles(stylingContext, {width: '100px', height: '100px'});
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 14), //
2,
element,
null,
{width: '100px', height: '100px'},
@ -355,12 +355,12 @@ describe('styling', () => {
updateStyles(stylingContext, {width: '200px', opacity: '0'});
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 14), //
2,
element,
null,
{width: '200px', opacity: '0'},
@ -392,12 +392,12 @@ describe('styling', () => {
getStyles(stylingContext);
expect(stylingContext).toEqual([
element,
null,
null,
[null],
cleanStyle(0, 14), //
2,
element,
null,
{width: '200px', opacity: '0'},
@ -431,12 +431,12 @@ describe('styling', () => {
updateStyleProp(stylingContext, 0, '300px');
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 14), //
2,
element,
null,
{width: null},
@ -470,12 +470,12 @@ describe('styling', () => {
updateStyleProp(stylingContext, 0, null);
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 14), //
2,
element,
null,
{width: null},
@ -514,12 +514,12 @@ describe('styling', () => {
updateStyles(stylingContext, {width: '100px', height: '100px', opacity: '0.5'});
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 11), //
1,
element,
null,
{width: '100px', height: '100px', opacity: '0.5'},
@ -553,12 +553,12 @@ describe('styling', () => {
updateStyles(stylingContext, {});
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 11), //
1,
element,
null,
{},
@ -594,12 +594,12 @@ describe('styling', () => {
});
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 11), //
1,
element,
null,
{borderWidth: '5px'},
@ -637,12 +637,12 @@ describe('styling', () => {
updateStyleProp(stylingContext, 0, '200px');
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 11), //
1,
element,
null,
{borderWidth: '5px'},
@ -680,12 +680,12 @@ describe('styling', () => {
updateStyles(stylingContext, {borderWidth: '15px', borderColor: 'red'});
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 11), //
1,
element,
null,
{borderWidth: '15px', borderColor: 'red'},
@ -737,12 +737,12 @@ describe('styling', () => {
updateStyleProp(stylingContext, 0, '200px');
expect(stylingContext).toEqual([
element,
null,
null,
[null],
dirtyStyle(0, 11), //
1,
element,
null,
{width: '100px'},
@ -765,12 +765,12 @@ describe('styling', () => {
getStyles(stylingContext);
expect(stylingContext).toEqual([
element,
null,
null,
[null],
cleanStyle(0, 11), //
1,
element,
null,
{width: '100px'},
@ -802,12 +802,12 @@ describe('styling', () => {
updateStyleProp(stylingContext, 1, '100px');
expect(stylingContext).toEqual([
element,
null,
styleSanitizer,
[null],
dirtyStyle(0, 14), //
2,
element,
null,
null,
@ -835,12 +835,12 @@ describe('styling', () => {
updateStyles(stylingContext, {'background-image': 'unsafe'});
expect(stylingContext).toEqual([
element,
null,
styleSanitizer,
[null],
dirtyStyle(0, 14), //
2,
element,
null,
{'background-image': 'unsafe'},
@ -873,12 +873,12 @@ describe('styling', () => {
getStyles(stylingContext);
expect(stylingContext).toEqual([
element,
null,
styleSanitizer,
[null],
cleanStyle(0, 14), //
2,
element,
null,
{'background-image': 'unsafe'},
@ -916,8 +916,8 @@ describe('styling', () => {
const template =
initContext(null, [InitialStylingFlags.VALUES_MODE, 'one', true, 'two', true]);
expect(template).toEqual([
element, null, null, [null, true, true], dirtyStyle(0, 14), //
0, null, null,
null, null, [null, true, true], dirtyStyle(0, 14), //
0, element, null, null,
// #8
cleanClass(1, 14), 'one', null,
@ -979,12 +979,12 @@ describe('styling', () => {
const initialClasses = ['wide', 'tall', InitialStylingFlags.VALUES_MODE, 'wide', true];
const stylingContext = initContext(initialStyles, initialClasses);
expect(stylingContext).toEqual([
element,
null,
null,
[null, '100px', true],
dirtyStyle(0, 20), //
2,
element,
null,
null,
@ -1033,12 +1033,12 @@ describe('styling', () => {
updateStylingMap(stylingContext, 'tall round', {width: '200px', opacity: '0.5'});
expect(stylingContext).toEqual([
element,
null,
null,
[null, '100px', true],
dirtyStyle(0, 20), //
2,
element,
'tall round',
{width: '200px', opacity: '0.5'},
@ -1101,12 +1101,12 @@ describe('styling', () => {
updateStyleProp(stylingContext, 0, '300px');
expect(stylingContext).toEqual([
element,
null,
null,
[null, '100px', true],
dirtyStyle(0, 20), //
2,
element,
{tall: true, wide: true},
{width: '500px'},
@ -1179,12 +1179,12 @@ describe('styling', () => {
getStylesAndClasses(stylingContext);
expect(stylingContext).toEqual([
element,
null,
null,
[null],
cleanStyle(0, 8), //
0,
element,
{foo: true},
{width: '200px'},
@ -1208,12 +1208,12 @@ describe('styling', () => {
getStylesAndClasses(stylingContext);
expect(stylingContext).toEqual([
element,
null,
null,
[null],
cleanStyle(0, 8), //
0,
element,
{foo: false},
{width: '300px'},
@ -1240,12 +1240,12 @@ describe('styling', () => {
expect(getClasses(stylingContext)).toEqual({apple: true, orange: true, banana: true});
expect(stylingContext).toEqual([
element,
null,
null,
[null],
cleanStyle(0, 8), //
0,
element,
'apple orange banana',
null,