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

View File

@ -13,19 +13,22 @@ import {Injector} from '../di/injector';
import {Sanitizer} from '../sanitization/security'; import {Sanitizer} from '../sanitization/security';
import {assertComponentType, assertDefined} from './assert'; import {assertComponentType, assertDefined} from './assert';
import {getLElementFromComponent, readPatchedLViewData} from './context_discovery'; import {getComponentViewByInstance} from './context_discovery';
import {getComponentDef} from './definition'; import {getComponentDef} from './definition';
import {queueInitHooks, queueLifecycleHooks} from './hooks'; 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 {ComponentDef, ComponentType} from './interfaces/definition';
import {LElementNode, TNodeFlags} from './interfaces/node'; import {TNodeFlags, TNodeType} from './interfaces/node';
import {PlayerHandler} from './interfaces/player'; import {PlayerHandler} from './interfaces/player';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {RElement, RNode, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view'; import {CONTEXT, HEADER_OFFSET, HOST, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
import {getRootView, stringify} from './util'; 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. */ /** Options that control how the component should be bootstrapped. */
export interface CreateComponentOptions { export interface CreateComponentOptions {
/** Which renderer factory to use. */ /** Which renderer factory to use. */
@ -111,30 +114,29 @@ export function renderComponent<T>(
// The first index of the first selector is the tag name. // The first index of the first selector is the tag name.
const componentTag = componentDef.selectors ![0] ![0] as string; 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 : const rootFlags = componentDef.onPush ? LViewFlags.Dirty | LViewFlags.IsRoot :
LViewFlags.CheckAlways | LViewFlags.IsRoot; LViewFlags.CheckAlways | LViewFlags.IsRoot;
const rootContext = createRootContext( const rootContext = createRootContext(
opts.scheduler || requestAnimationFrame.bind(window), opts.playerHandler || null); opts.scheduler || requestAnimationFrame.bind(window), opts.playerHandler || null);
const renderer = rendererFactory.createRenderer(hostRNode, componentDef);
const rootView: LViewData = createLViewData( const rootView: LViewData = createLViewData(
rendererFactory.createRenderer(hostNode, componentDef), renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
rootView[INJECTOR] = opts.injector || null; rootView[INJECTOR] = opts.injector || null;
const oldView = enterView(rootView, null); const oldView = enterView(rootView, null);
let elementNode: LElementNode;
let component: T; let component: T;
try { try {
if (rendererFactory.begin) rendererFactory.begin(); if (rendererFactory.begin) rendererFactory.begin();
// Create element node at index 0 in data array const componentView =
elementNode = hostElement(componentTag, hostNode, componentDef, sanitizer); createRootComponentView(hostRNode, componentDef, rootView, renderer, sanitizer);
component = createRootComponent( component = createRootComponent(
elementNode, componentDef, rootView, rootContext, opts.hostFeatures || null); hostRNode, componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
executeInitAndContentHooks(); executeInitAndContentHooks();
detectChangesInternal(elementNode.data as LViewData, component); detectChangesInternal(componentView, component);
} finally { } finally {
leaveView(oldView); leaveView(oldView);
if (rendererFactory.end) rendererFactory.end(); if (rendererFactory.end) rendererFactory.end();
@ -143,19 +145,55 @@ export function renderComponent<T>(
return component; 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 * Creates a root component and sets it up with features and host bindings. Shared by
* renderComponent() and ViewContainerRef.createComponent(). * renderComponent() and ViewContainerRef.createComponent().
*/ */
export function createRootComponent<T>( export function createRootComponent<T>(
elementNode: LElementNode, componentDef: ComponentDef<T>, rootView: LViewData, hostRNode: RNode | null, componentView: LViewData, componentDef: ComponentDef<T>,
rootContext: RootContext, hostFeatures: HostFeature[] | null): any { rootView: LViewData, rootContext: RootContext, hostFeatures: HostFeature[] | null): any {
// Create directive instance with factory() and store at next index in viewData // Create directive instance with factory() and store at next index in viewData
const component = const component =
baseDirectiveCreate(rootView.length, componentDef.factory() as T, componentDef, elementNode); baseDirectiveCreate(rootView.length, componentDef.factory() as T, componentDef, hostRNode);
rootContext.components.push(component); rootContext.components.push(component);
(elementNode.data as LViewData)[CONTEXT] = component; componentView[CONTEXT] = component;
hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef)); hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef));
if (rootView[TVIEW].firstTemplatePass) prefillHostVars(componentDef.hostVars); 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. * @param component Component for which the host element should be retrieved.
*/ */
export function getHostElement<T>(component: T): HTMLElement { 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 {Type} from '../type';
import {assertComponentType, assertDefined} from './assert'; import {assertComponentType, assertDefined} from './assert';
import {LifecycleHooksFeature, createRootComponent, createRootContext} from './component'; import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
import {getComponentDef} from './definition'; 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 {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 {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view'; import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {getTNode} from './util';
import {createElementRef} from './view_engine_compatibility'; import {createElementRef} from './view_engine_compatibility';
import {RootViewRef, ViewRef} from './view_ref'; import {RootViewRef, ViewRef} from './view_ref';
@ -109,7 +110,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
rendererFactory = domRendererFactory3; rendererFactory = domRendererFactory3;
} }
const hostNode = isInternalRootView ? const hostRNode = isInternalRootView ?
elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef)) : elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef)) :
locateHostElement(rendererFactory, rootSelectorOrNode); locateHostElement(rendererFactory, rootSelectorOrNode);
@ -122,24 +123,23 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
ngModule.injector.get(ROOT_CONTEXT) : ngModule.injector.get(ROOT_CONTEXT) :
createRootContext(requestAnimationFrame.bind(window)); createRootContext(requestAnimationFrame.bind(window));
const renderer = rendererFactory.createRenderer(hostRNode, this.componentDef);
// Create the root view. Uses empty TView and ContentTemplate. // Create the root view. Uses empty TView and ContentTemplate.
const rootView: LViewData = createLViewData( const rootView: LViewData = createLViewData(
rendererFactory.createRenderer(hostNode, this.componentDef), renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
rootView[INJECTOR] = ngModule && ngModule.injector || null; rootView[INJECTOR] = ngModule && ngModule.injector || null;
// rootView is the parent when bootstrapping // rootView is the parent when bootstrapping
const oldView = enterView(rootView, null); const oldView = enterView(rootView, null);
let component: T; let component: T;
let elementNode: LElementNode;
let tElementNode: TElementNode; let tElementNode: TElementNode;
try { try {
if (rendererFactory.begin) rendererFactory.begin(); if (rendererFactory.begin) rendererFactory.begin();
// Create element node at index 0 in data array const componentView =
elementNode = hostElement(componentTag, hostNode, this.componentDef); createRootComponentView(hostRNode, this.componentDef, rootView, renderer);
tElementNode = getTNode(0) as TElementNode; tElementNode = getTNode(0, rootView) as TElementNode;
// Transform the arrays of native nodes into a LNode structure that can be consumed by the // 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. // 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? // executed here?
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
component = createRootComponent( 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 // 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); renderEmbeddedTemplate(componentView, componentView[TVIEW], component, RenderFlags.Create);
componentView[FLAGS] &= ~LViewFlags.CreationMode; componentView[FLAGS] &= ~LViewFlags.CreationMode;
} finally { } finally {

View File

@ -8,59 +8,12 @@
import './ng_dev_mode'; import './ng_dev_mode';
import {assertEqual} from './assert'; 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 {LElementNode, TNode, TNodeFlags} from './interfaces/node';
import {RElement} from './interfaces/renderer'; import {RElement} from './interfaces/renderer';
import {StylingContext, StylingIndex} from './interfaces/styling'; import {CONTEXT, HEADER_OFFSET, HOST, LViewData, TVIEW} from './interfaces/view';
import {CONTEXT, HEADER_OFFSET, 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. /** 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 * @param componentInstance
* that `getContext` has in the event that an Angular application doesn't need to have * @returns The component's view
* any programmatic access to an element's context (only change detection uses this function).
*/ */
export function getLElementFromComponent(componentInstance: {}): LElementNode { export function getComponentViewByInstance(componentInstance: {}): LViewData {
let lViewData = readPatchedData(componentInstance); let lViewData = readPatchedData(componentInstance);
let lNode: LElementNode; let view: LViewData;
if (Array.isArray(lViewData)) { if (Array.isArray(lViewData)) {
const lNodeIndex = findViaComponent(lViewData, componentInstance); const lNodeIndex = findViaComponent(lViewData, componentInstance);
lNode = readElementValue(lViewData[lNodeIndex]); view = getComponentViewByIndex(lNodeIndex, lViewData);
const context = createLContext(lViewData, lNodeIndex, lNode.native); const context = createLContext(lViewData, lNodeIndex, (view[HOST] as LElementNode).native);
context.component = componentInstance; context.component = componentInstance;
attachPatchData(componentInstance, context); attachPatchData(componentInstance, context);
attachPatchData(context.native, context); attachPatchData(context.native, context);
} else { } else {
const context = lViewData as any as LContext; const context = lViewData as any as LContext;
lNode = readElementValue(context.lViewData[context.nodeIndex]); view = getComponentViewByIndex(context.nodeIndex, context.lViewData);
} }
return view;
return lNode;
} }
/** /**
@ -220,22 +171,6 @@ export function attachPatchData(target: any, data: LViewData | LContext) {
target[MONKEY_PATCH_KEY_NAME] = data; 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 { export function isComponentInstance(instance: any): boolean {
return instance && instance.constructor && instance.constructor.ngComponentDef; return instance && instance.constructor && instance.constructor.ngComponentDef;
} }
@ -282,14 +217,14 @@ function findViaComponent(lViewData: LViewData, componentInstance: {}): number {
if (componentIndices) { if (componentIndices) {
for (let i = 0; i < componentIndices.length; i++) { for (let i = 0; i < componentIndices.length; i++) {
const elementComponentIndex = componentIndices[i]; const elementComponentIndex = componentIndices[i];
const lNodeData = readElementValue(lViewData[elementComponentIndex] !).data !; const componentView = getComponentViewByIndex(elementComponentIndex, lViewData);
if (lNodeData[CONTEXT] === componentInstance) { if (componentView[CONTEXT] === componentInstance) {
return elementComponentIndex; return elementComponentIndex;
} }
} }
} else { } else {
const rootNode = lViewData[HEADER_OFFSET]; const rootComponentView = getComponentViewByIndex(HEADER_OFFSET, lViewData);
const rootComponent = rootNode.data[CONTEXT]; const rootComponent = rootComponentView[CONTEXT];
if (rootComponent === componentInstance) { if (rootComponent === componentInstance) {
// we are dealing with the root element here therefore we know that the // 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 // 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; const count = tNode.flags & TNodeFlags.DirectiveCountMask;
return count ? (startIndex + count) : -1; 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 {Injector} from '../di/injector';
import {assertDefined} from './assert'; 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 {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 {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... * 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 context = loadContext(target);
const tNode = context.lViewData[TVIEW].data[context.nodeIndex] as TNode; const tNode = context.lViewData[TVIEW].data[context.nodeIndex] as TNode;
if (tNode.flags & TNodeFlags.isComponent) { if (tNode.flags & TNodeFlags.isComponent) {
const lNode = context.lViewData[context.nodeIndex] as LElementNode; const componentView = getComponentViewByIndex(context.nodeIndex, context.lViewData);
return lNode.data ![CONTEXT] as any as T; return componentView[CONTEXT] as any as T;
} }
return null; return null;
} }

View File

@ -7,13 +7,14 @@
*/ */
import {assertEqual, assertLessThan} from './assert'; 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 {LContainer, NATIVE, RENDER_PARENT} from './interfaces/container';
import {LContainerNode, LNode, TElementNode, TNode, TNodeType} from './interfaces/node'; import {LContainerNode, LNode, TElementNode, TNode, TNodeType} from './interfaces/node';
import {StylingContext} from './interfaces/styling'; import {StylingContext} from './interfaces/styling';
import {BINDING_INDEX, HEADER_OFFSET, HOST_NODE, TVIEW} from './interfaces/view'; import {BINDING_INDEX, HEADER_OFFSET, HOST_NODE, TVIEW} from './interfaces/view';
import {appendChild, createTextNode, removeChild} from './node_manipulation'; 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(getNative(tNode, viewData), tNode, viewData);
appendChild(native, tNode, viewData);
const slotValue = viewData[tNode.index]; const slotValue = viewData[tNode.index];
if (tNode.type !== TNodeType.Container && isLContainer(slotValue)) { if (tNode.type !== TNodeType.Container && isLContainer(slotValue)) {
@ -320,7 +320,7 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
} }
const renderer = getRenderer(); const renderer = getRenderer();
const startTNode = getTNode(startIndex); const startTNode = getTNode(startIndex, viewData);
let localParentTNode: TNode = startTNode.parent || viewData[HOST_NODE] !; let localParentTNode: TNode = startTNode.parent || viewData[HOST_NODE] !;
let localPreviousTNode: TNode = localParentTNode; let localPreviousTNode: TNode = localParentTNode;
resetComponentState(); // We don't want to add to the tree with the wrong previous node 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; const instruction = instructions[i] as number;
switch (instruction & I18nInstructions.InstructionMask) { switch (instruction & I18nInstructions.InstructionMask) {
case I18nInstructions.Element: case I18nInstructions.Element:
const elementTNode = getTNode(instruction & I18nInstructions.IndexMask); const elementTNode = getTNode(instruction & I18nInstructions.IndexMask, viewData);
localPreviousTNode = appendI18nNode(elementTNode, localParentTNode, localPreviousTNode); localPreviousTNode = appendI18nNode(elementTNode, localParentTNode, localPreviousTNode);
localParentTNode = elementTNode; localParentTNode = elementTNode;
break; break;
@ -338,7 +338,7 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
case I18nInstructions.Any: case I18nInstructions.Any:
const nodeIndex = instruction & I18nInstructions.IndexMask; const nodeIndex = instruction & I18nInstructions.IndexMask;
localPreviousTNode = localPreviousTNode =
appendI18nNode(getTNode(nodeIndex), localParentTNode, localPreviousTNode); appendI18nNode(getTNode(nodeIndex, viewData), localParentTNode, localPreviousTNode);
break; break;
case I18nInstructions.Text: case I18nInstructions.Text:
if (ngDevMode) { if (ngDevMode) {
@ -365,7 +365,7 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
} }
const removeIndex = instruction & I18nInstructions.IndexMask; const removeIndex = instruction & I18nInstructions.IndexMask;
const removedNode: LNode|LContainerNode = loadElement(removeIndex); const removedNode: LNode|LContainerNode = loadElement(removeIndex);
const removedTNode = getTNode(removeIndex); const removedTNode = getTNode(removeIndex, viewData);
removeChild(removedTNode, removedNode.native || null, viewData); removeChild(removedTNode, removedNode.native || null, viewData);
const slotValue = load(removeIndex) as LNode | LContainer | StylingContext; 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 {StyleSanitizeFn} from '../sanitization/style_sanitizer';
import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './assert'; 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 {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks'; 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 {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {INJECTOR_SIZE} from './interfaces/injector'; 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 {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
import {LQueries} from './interfaces/query'; import {LQueries} from './interfaces/query';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer'; 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, 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_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 {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 {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 {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 {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. */ /** Used to set the parent property when nodes are created and track query results. */
let previousOrParentTNode: TNode; let previousOrParentTNode: TNode;
export function getPreviousOrParentNode(): LNode|null {
return previousOrParentTNode == null || previousOrParentTNode === viewData[HOST_NODE] ?
getHostElementNode(viewData) :
getLNode(previousOrParentTNode, viewData);
}
export function getPreviousOrParentTNode(): TNode { export function getPreviousOrParentTNode(): TNode {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) // top level variables should not be exported for performance reasons (PERF_NOTES.md)
return previousOrParentTNode; return previousOrParentTNode;
@ -252,9 +247,6 @@ export function getBindingRoot() {
return bindingRootIndex; 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 { const enum BindingDirection {
Input, Input,
Output, Output,
@ -413,8 +405,8 @@ export function createLViewData<T>(
renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags, renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags,
sanitizer?: Sanitizer | null): LViewData { sanitizer?: Sanitizer | null): LViewData {
const instance = tView.blueprint.slice() as LViewData; const instance = tView.blueprint.slice() as LViewData;
instance[PARENT] = instance[DECLARATION_VIEW] = viewData;
instance[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit; instance[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit;
instance[PARENT] = instance[DECLARATION_VIEW] = viewData;
instance[CONTEXT] = context; instance[CONTEXT] = context;
instance[INJECTOR] = viewData ? viewData[INJECTOR] : null; instance[INJECTOR] = viewData ? viewData[INJECTOR] : null;
instance[RENDERER] = renderer; instance[RENDERER] = renderer;
@ -422,17 +414,6 @@ export function createLViewData<T>(
return instance; 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 * 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. * keep the execution code monomorphic and fast.
@ -456,14 +437,14 @@ export function createNodeAtIndex(
attrs: TAttributes | null, data: null): TContainerNode; attrs: TAttributes | null, data: null): TContainerNode;
export function createNodeAtIndex( export function createNodeAtIndex(
index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null, index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null,
lProjection: null): TProjectionNode; data: null): TProjectionNode;
export function createNodeAtIndex( export function createNodeAtIndex(
index: number, type: TNodeType.ElementContainer, native: RComment, name: null, index: number, type: TNodeType.ElementContainer, native: RComment, name: null,
attrs: TAttributes | null, data: null): TElementContainerNode; attrs: TAttributes | null, data: null): TElementContainerNode;
export function createNodeAtIndex( export function createNodeAtIndex(
index: number, type: TNodeType, native: RText | RElement | RComment | null, name: string | null, index: number, type: TNodeType, native: RText | RElement | RComment | null, name: string | null,
attrs: TAttributes | null, state?: null | LViewData | LContainer): TElementNode&TViewNode& attrs: TAttributes | null, state?: null | LViewData): TElementNode&TViewNode&TContainerNode&
TContainerNode&TElementContainerNode&TProjectionNode { TElementContainerNode&TProjectionNode {
const parent = const parent =
isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent; isParent ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent;
@ -473,7 +454,7 @@ export function createNodeAtIndex(
const tParent = parentInSameView ? parent as TElementNode | TContainerNode : null; const tParent = parentInSameView ? parent as TElementNode | TContainerNode : null;
const isState = state != null; const isState = state != null;
const node = createLNodeObject(type, native, isState ? state as any : null); const node = {native: native as any};
let tNode: TNode; let tNode: TNode;
if (index === -1 || type === TNodeType.View) { if (index === -1 || type === TNodeType.View) {
@ -574,10 +555,10 @@ export function resetComponentState() {
*/ */
export function renderTemplate<T>( export function renderTemplate<T>(
hostNode: RElement, templateFn: ComponentTemplate<T>, consts: number, vars: number, context: 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, directives?: DirectiveDefListOrFactory | null, pipes?: PipeDefListOrFactory | null,
sanitizer?: Sanitizer | null): LElementNode { sanitizer?: Sanitizer | null): LViewData {
if (host == null) { if (hostView == null) {
resetComponentState(); resetComponentState();
rendererFactory = providedRendererFactory; rendererFactory = providedRendererFactory;
renderer = providedRendererFactory.createRenderer(null, null); renderer = providedRendererFactory.createRenderer(null, null);
@ -588,16 +569,13 @@ export function renderTemplate<T>(
const componentTView = const componentTView =
getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null); getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null);
const componentLView = hostView =
createLViewData(renderer, componentTView, context, LViewFlags.CheckAlways, sanitizer); createLViewData(renderer, componentTView, context, LViewFlags.CheckAlways, sanitizer);
createNodeAtIndex(0, TNodeType.Element, hostNode, null, null, componentLView); createNodeAtIndex(0, TNodeType.Element, hostNode, null, null, hostView);
host = loadElement(0);
} }
const hostView = host.data !;
ngDevMode && assertDefined(hostView, 'Host node should have an LView defined in host.data.');
renderComponentOrTemplate(hostView, context, templateFn); renderComponentOrTemplate(hostView, context, templateFn);
return host; return hostView;
} }
/** /**
@ -877,10 +855,6 @@ export function elementCreate(name: string, overriddenRenderer?: Renderer3): REl
return native; return native;
} }
function nativeNodeLocalRefExtractor(tNode: TNode, currentView: LViewData): RNode {
return getLNode(tNode, currentView).native;
}
/** /**
* Creates directive instances and populates local refs. * 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 * @param localRefExtractor mapping function that extracts local ref value from LNode
*/ */
function createDirectivesAndLocals( function createDirectivesAndLocals(
localRefs: string[] | null | undefined, localRefs: string[] | null | undefined, localRefExtractor: LocalRefExtractor = getNative) {
localRefExtractor: LocalRefExtractor = nativeNodeLocalRefExtractor) {
if (!bindingsEnabled) return; if (!bindingsEnabled) return;
if (firstTemplatePass) { if (firstTemplatePass) {
ngDevMode && ngDevMode.firstTemplatePass++; ngDevMode && ngDevMode.firstTemplatePass++;
@ -1096,7 +1069,7 @@ function saveResolvedLocalsInData(localRefExtractor: LocalRefExtractor): void {
* @param pipes Pipe defs that should be saved on TView * @param pipes Pipe defs that should be saved on TView
* @returns TView * @returns TView
*/ */
function getOrCreateTView( export function getOrCreateTView(
templateFn: ComponentTemplate<any>, consts: number, vars: number, templateFn: ComponentTemplate<any>, consts: number, vars: number,
directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null, directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null,
viewQuery: ComponentQuery<any>| null): TView { viewQuery: ComponentQuery<any>| null): TView {
@ -1235,35 +1208,6 @@ export function locateHostElement(
return rNode; 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. * Adds an event listener to the current node.
* *
@ -1282,17 +1226,17 @@ export function listener(
// add native event listener - applicable to elements only // add native event listener - applicable to elements only
if (tNode.type === TNodeType.Element) { if (tNode.type === TNodeType.Element) {
const node = getPreviousOrParentNode() as LElementNode; const native = getNative(previousOrParentTNode, viewData) as RElement;
ngDevMode && ngDevMode.rendererAddEventListener++; ngDevMode && ngDevMode.rendererAddEventListener++;
// In order to match current behavior, native DOM event listeners must be added for all // In order to match current behavior, native DOM event listeners must be added for all
// events (including outputs). // events (including outputs).
if (isProceduralRenderer(renderer)) { if (isProceduralRenderer(renderer)) {
const cleanupFn = renderer.listen(node.native, eventName, listenerFn); const cleanupFn = renderer.listen(native, eventName, listenerFn);
storeCleanupFn(viewData, cleanupFn); storeCleanupFn(viewData, cleanupFn);
} else { } else {
const wrappedListener = wrapListenerWithPreventDefault(listenerFn); const wrappedListener = wrapListenerWithPreventDefault(listenerFn);
node.native.addEventListener(eventName, wrappedListener, useCapture); native.addEventListener(eventName, wrappedListener, useCapture);
const cleanupInstances = getCleanup(viewData); const cleanupInstances = getCleanup(viewData);
cleanupInstances.push(wrappedListener); cleanupInstances.push(wrappedListener);
if (firstTemplatePass) { if (firstTemplatePass) {
@ -1421,7 +1365,7 @@ export function elementProperty<T>(
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void { index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void {
if (value === NO_CHANGE) return; if (value === NO_CHANGE) return;
const node = loadElement(index) as LElementNode | LContainerNode | LElementContainerNode; 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 // if tNode.inputs is undefined, a listener has created outputs, but inputs haven't
// yet been checked // yet been checked
if (tNode && tNode.inputs === undefined) { if (tNode && tNode.inputs === undefined) {
@ -1433,7 +1377,7 @@ export function elementProperty<T>(
let dataValue: PropertyAliasValue|undefined; let dataValue: PropertyAliasValue|undefined;
if (inputData && (dataValue = inputData[propName])) { if (inputData && (dataValue = inputData[propName])) {
setInputsForProperty(dataValue, value); 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) { } else if (tNode.type === TNodeType.Element) {
// It is assumed that the sanitizer is only added when the compiler determines that the property // 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. // is risky, so sanitization can be done without further checks.
@ -1583,7 +1527,7 @@ function generatePropertyAliases(
*/ */
export function elementClassProp<T>( export function elementClassProp<T>(
index: number, stylingIndex: number, value: T | NO_CHANGE): void { 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. * 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.) * index.)
*/ */
export function elementStylingApply<T>(index: number): void { 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; 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>( export function elementStylingMap<T>(
index: number, classes: {[key: string]: any} | string | null, index: number, classes: {[key: string]: any} | string | null,
styles?: {[styleName: string]: any} | null): void { 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>( export function directiveCreate<T>(
directiveDefIdx: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>): T { directiveDefIdx: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>): T {
const hostNode = getLNode(previousOrParentTNode, viewData); const native = getNative(previousOrParentTNode, viewData);
const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, hostNode); const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef, native);
if ((directiveDef as ComponentDef<T>).template) { if ((directiveDef as ComponentDef<T>).template) {
hostNode.data ![CONTEXT] = directive; const componentView = getComponentViewByIndex(previousOrParentTNode.index, viewData);
componentView[CONTEXT] = directive;
} }
if (firstTemplatePass) { if (firstTemplatePass) {
@ -1826,7 +1743,7 @@ export function directiveCreate<T>(
} }
function addComponentLogic<T>(def: ComponentDef<T>): void { function addComponentLogic<T>(def: ComponentDef<T>): void {
const hostNode = getLNode(previousOrParentTNode, viewData); const native = getNative(previousOrParentTNode, viewData);
const tView = getOrCreateTView( const tView = getOrCreateTView(
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery); 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( const componentView = addToViewTree(
viewData, previousOrParentTNode.index as number, viewData, previousOrParentTNode.index as number,
createLViewData( 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())); def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer()));
// We need to set the host node/data here because when the component LNode was created, componentView[HOST_NODE] = previousOrParentTNode as TElementNode;
// we didn't yet know it was a component (just an element).
(hostNode as{data: LViewData}).data = componentView; // Component view will always be created before any injected LContainers,
(componentView as LViewData)[HOST_NODE] = previousOrParentTNode as TElementNode; // so this is a regular LNode, wrap it with the component view
componentView[HOST] = viewData[previousOrParentTNode.index];
viewData[previousOrParentTNode.index] = componentView;
if (firstTemplatePass) { if (firstTemplatePass) {
queueComponentIndexForCheck(); queueComponentIndexForCheck();
@ -1859,15 +1778,15 @@ function addComponentLogic<T>(def: ComponentDef<T>): void {
*/ */
export function baseDirectiveCreate<T>( export function baseDirectiveCreate<T>(
index: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>, index: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>,
hostNode: LNode): T { native: RNode | null): T {
ngDevMode && assertEqual( ngDevMode && assertEqual(
viewData[BINDING_INDEX], tView.bindingStartIndex, viewData[BINDING_INDEX], tView.bindingStartIndex,
'directives should be created before any bindings'); 'directives should be created before any bindings');
ngDevMode && assertPreviousIsParent(); ngDevMode && assertPreviousIsParent();
attachPatchData(directive, viewData); attachPatchData(directive, viewData);
if (hostNode) { if (native) {
attachPatchData(hostNode.native, viewData); attachPatchData(native, viewData);
} }
viewData[index] = directive; viewData[index] = directive;
@ -1897,7 +1816,7 @@ export function baseDirectiveCreate<T>(
} }
if (directiveDef !.attributes != null && previousOrParentTNode.type == TNodeType.Element) { 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; return directive;
@ -1990,12 +1909,12 @@ export function createLContainer(
native: RComment, isForViewContainerRef?: boolean): LContainer { native: RComment, isForViewContainerRef?: boolean): LContainer {
return [ return [
isForViewContainerRef ? -1 : 0, // active index isForViewContainerRef ? -1 : 0, // active index
[], // views
currentView, // parent currentView, // parent
null, // next null, // next
null, // queries null, // queries
hostLNode, // host native hostLNode, // host native
native, // native native, // native
[], // views
getRenderParent(hostTNode, currentView) // renderParent getRenderParent(hostTNode, currentView) // renderParent
]; ];
} }
@ -2269,11 +2188,8 @@ export function embeddedViewEnd(): void {
export function componentRefresh<T>( export function componentRefresh<T>(
adjustedElementIndex: number, parentFirstTemplatePass: boolean): void { adjustedElementIndex: number, parentFirstTemplatePass: boolean): void {
ngDevMode && assertDataInRange(adjustedElementIndex); 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 && 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 // Only attached CheckAlways components or attached, dirty OnPush components should be checked
if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) { 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. */ /** If node is an OnPush component, marks its LViewData dirty. */
export function markDirtyIfOnPush(node: LElementNode): void { export function markDirtyIfOnPush(viewIndex: number): void {
// Because data flows down the component tree, ancestors do not need to be marked dirty const view = getComponentViewByIndex(viewIndex, viewData);
if (node.data && !(node.data[FLAGS] & LViewFlags.CheckAlways)) { if (!(view[FLAGS] & LViewFlags.CheckAlways)) {
node.data[FLAGS] |= LViewFlags.Dirty; view[FLAGS] |= LViewFlags.Dirty;
} }
} }
@ -2576,10 +2492,7 @@ function tickRootContext(rootContext: RootContext) {
* @param component The component which the change detection should be performed on. * @param component The component which the change detection should be performed on.
*/ */
export function detectChanges<T>(component: T): void { export function detectChanges<T>(component: T): void {
const hostNode = getLElementFromComponent(component) !; detectChangesInternal(getComponentViewByInstance(component) !, component);
ngDevMode &&
assertDefined(hostNode, 'Component host node should be attached to an LViewData instance.');
detectChangesInternal(hostNode.data !, component);
} }
/** /**
@ -2673,8 +2586,7 @@ function updateViewQuery<T>(viewQuery: ComponentQuery<{}>| null, component: T):
*/ */
export function markDirty<T>(component: T) { export function markDirty<T>(component: T) {
ngDevMode && assertDefined(component, 'component'); ngDevMode && assertDefined(component, 'component');
const elementNode = getLElementFromComponent(component) !; markViewDirty(getComponentViewByInstance(component));
markViewDirty(elementNode.data as LViewData);
} }
/////////////////////////////// ///////////////////////////////
@ -2888,10 +2800,6 @@ export function loadElement(index: number): LElementNode {
return loadElementInternal(index, viewData); return loadElementInternal(index, viewData);
} }
export function getTNode(index: number): TNode {
return tView.data[index + HEADER_OFFSET] as TNode;
}
/** Gets the current binding value. */ /** Gets the current binding value. */
export function getBinding(bindingIndex: number): any { export function getBinding(bindingIndex: number): any {
ngDevMode && assertDataInRange(viewData[bindingIndex]); ngDevMode && assertDataInRange(viewData[bindingIndex]);

View File

@ -10,7 +10,8 @@ import {LContainerNode, LElementContainerNode, LElementNode} from './node';
import {LQueries} from './query'; import {LQueries} from './query';
import {RComment} from './renderer'; import {RComment} from './renderer';
import {StylingContext} from './styling'; 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 * 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. * Uglify will inline these when minifying so there shouldn't be a cost.
*/ */
export const ACTIVE_INDEX = 0; 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. // As we already have these constants in LViewData, we don't need to re-create them.
export const HOST_NATIVE = 4; export const NATIVE = 6;
export const NATIVE = 5;
export const VIEWS = 6;
export const RENDER_PARENT = 7; export const RENDER_PARENT = 7;
/** /**
@ -43,6 +43,15 @@ export interface LContainer extends Array<any> {
*/ */
[ACTIVE_INDEX]: number; [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 * Access to the parent view is necessary so we can propagate back
* up from inside a container to parent[NEXT]. * up from inside a container to parent[NEXT].
@ -63,20 +72,11 @@ export interface LContainer extends Array<any> {
/** The host node of this LContainer. */ /** The host node of this LContainer. */
// TODO: Should contain just the native element once LNode is removed. // 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. */ /** The comment element that serves as an anchor for this LContainer. */
[NATIVE]: RComment; [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 * Parent Element which will contain the location where all of the Views will be
* inserted into to. * 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 * - retrieve the sibling elements of text nodes whose creation / insertion has been delayed
*/ */
readonly native: RComment|RElement|RText|null; 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 { export interface LElementNode extends LNode {
/** The DOM element associated with this node. */ /** The DOM element associated with this node. */
readonly native: RElement; readonly native: RElement;
/** If Component then data has LView (light DOM) */
readonly data: LViewData|null;
} }
/** LNode representing <ng-container>. */ /** LNode representing <ng-container>. */
export interface LElementContainerNode extends LNode { export interface LElementContainerNode extends LNode {
/** The DOM comment associated with this node. */ /** The DOM comment associated with this node. */
readonly native: RComment; readonly native: RComment;
readonly data: null;
} }
/** LNode representing a #text node. */ /** LNode representing a #text node. */
export interface LTextNode extends LNode { export interface LTextNode extends LNode {
/** The text node associated with this node. */ /** The text node associated with this node. */
native: RText; native: RText;
readonly data: null;
} }
/** Abstract node which contains root nodes of a view. */ /** Abstract node which contains root nodes of a view. */
export interface LViewNode extends LNode { export interface LViewNode extends LNode { readonly native: null; }
readonly native: null;
readonly data: LViewData;
}
/** Abstract node container which contains other views. */ /** Abstract node container which contains other views. */
export interface LContainerNode extends LNode { export interface LContainerNode extends LNode {
@ -119,14 +103,10 @@ export interface LContainerNode extends LNode {
* until the parent view is processed. * until the parent view is processed.
*/ */
native: RComment; native: RComment;
readonly data: null;
} }
export interface LProjectionNode extends LNode { export interface LProjectionNode extends LNode { readonly native: null; }
readonly native: null;
readonly data: null;
}
/** /**
* A set of marker values to be used in the attributes arrays. Those markers indicate that some * 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 export interface StylingContext extends
Array<InitialStyles|{[key: string]: any}|number|string|boolean|LElementNode|StyleSanitizeFn| Array<InitialStyles|{[key: string]: any}|number|string|boolean|LElementNode|StyleSanitizeFn|
PlayerContext|null> { 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 * Location of animation context (which contains the active players) for this element styling
* context. * context.
@ -156,6 +151,11 @@ export interface StylingContext extends
*/ */
[StylingIndex.ClassOffsetPosition]: number; [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 * 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. * 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` */ /** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
export const enum StylingIndex { export const enum StylingIndex {
// Position of where the initial styles are stored in the styling context // Position of where the initial styles are stored in the styling context
ElementPosition = 0, PlayerContext = 0,
// Position of where the initial styles are stored in the styling context
PlayerContext = 1,
// Position of where the style sanitizer is stored within the styling context // 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 // 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`) // 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 // 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 // Position of where the last string-based CSS class value was stored
PreviousMultiClassValue = 6, PreviousMultiClassValue = 6,
// Position of where the last string-based CSS class value was stored // 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 {LContainer} from './container';
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList} from './definition'; 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 {LQueries} from './query';
import {Renderer3} from './renderer'; import {Renderer3} from './renderer';
import {StylingContext} from './styling';
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */ /** 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 // Below are constants for LViewData indices to help us look up LViewData members
// without having to remember the specific indices. // without having to remember the specific indices.
// Uglify will inline these when minifying so there shouldn't be a cost. // Uglify will inline these when minifying so there shouldn't be a cost.
export const TVIEW = 0; export const TVIEW = 0;
export const PARENT = 1; export const FLAGS = 1;
export const NEXT = 2; export const PARENT = 2;
export const QUERIES = 3; export const NEXT = 3;
export const FLAGS = 4; export const QUERIES = 4;
export const HOST_NODE = 5; export const HOST = 5;
export const BINDING_INDEX = 6; export const HOST_NODE = 6;
export const CLEANUP = 7; export const BINDING_INDEX = 7;
export const CONTEXT = 8; export const CLEANUP = 8;
export const INJECTOR = 9; export const CONTEXT = 9;
export const RENDERER = 10; export const INJECTOR = 10;
export const SANITIZER = 11; export const RENDERER = 11;
export const TAIL = 12; export const SANITIZER = 12;
export const CONTAINER_INDEX = 13; export const TAIL = 13;
export const CONTENT_QUERIES = 14; export const CONTAINER_INDEX = 14;
export const DECLARATION_VIEW = 15; export const CONTENT_QUERIES = 15;
export const DECLARATION_VIEW = 16;
// This interface replaces the real LViewData interface if it is an arg or a // 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 // 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; [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 * 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 * `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 active for this view - nodes from a view are reported to those queries. */
[QUERIES]: LQueries|null; [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. * Pointer to the `TViewNode` or `TElementNode` which represents the root of the view.

View File

@ -7,16 +7,15 @@
*/ */
import {assertDefined} from './assert'; import {assertDefined} from './assert';
import {attachPatchData, readElementValue} from './context_discovery'; import {attachPatchData} from './context_discovery';
import {callHooks} from './hooks'; import {callHooks} from './hooks';
import {HOST_NATIVE, LContainer, NATIVE, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container'; import {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 {LContainerNode, LElementContainerNode, LElementNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; 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 {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 {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; const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
@ -104,8 +103,7 @@ function walkTNodeTree(
let nextTNode: TNode|null = null; let nextTNode: TNode|null = null;
const parent = renderParentNode ? renderParentNode.native : null; const parent = renderParentNode ? renderParentNode.native : null;
if (tNode.type === TNodeType.Element) { if (tNode.type === TNodeType.Element) {
const elementNode = getLNode(tNode, currentView); executeNodeAction(action, renderer, parent, getNative(tNode, currentView), beforeNode);
executeNodeAction(action, renderer, parent, elementNode.native !, beforeNode);
const nodeOrContainer = currentView[tNode.index]; const nodeOrContainer = currentView[tNode.index];
if (isLContainer(nodeOrContainer)) { if (isLContainer(nodeOrContainer)) {
// This element has an LContainer, and its comment needs to be handled // 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 */ /** Gets the child of the given LViewData */
export function getLViewChild(viewData: LViewData): LViewData|LContainer|null { export function getLViewChild(viewData: LViewData): LViewData|LContainer|null {
const childIndex = viewData[TVIEW].childIndex; const childIndex = viewData[TVIEW].childIndex;
if (childIndex === -1) return null; return childIndex === -1 ? null : viewData[childIndex];
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;
} }
/** /**
@ -668,7 +660,7 @@ export function getBeforeNodeForView(index: number, views: LViewData[], containe
if (index + 1 < views.length) { if (index + 1 < views.length) {
const view = views[index + 1] as LViewData; const view = views[index + 1] as LViewData;
const viewTNode = view[HOST_NODE] as TViewNode; 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 { } else {
return containerNative; return containerNative;
} }
@ -706,7 +698,7 @@ export function removeChild(tNode: TNode, child: RNode | null, currentView: LVie
export function appendProjectedNode( export function appendProjectedNode(
projectedTNode: TNode, tProjectionNode: TNode, currentView: LViewData, projectedTNode: TNode, tProjectionNode: TNode, currentView: LViewData,
projectionView: LViewData): void { projectionView: LViewData): void {
const native = getLNode(projectedTNode, projectionView).native; const native = getNative(projectedTNode, projectionView);
appendChild(native, tProjectionNode, currentView); appendChild(native, tProjectionNode, currentView);
// the projected contents are processed while in the shadow view (which is the 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'; 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. * Creates a styling context template where styling information is stored.
* Any styles that are later referenced using `updateStyleProp` must be * 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 * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer'; 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 {LElementNode} from '../interfaces/node';
import {PlayerContext} from '../interfaces/player'; import {PlayerContext} from '../interfaces/player';
import {InitialStyles, StylingContext, StylingIndex} from '../interfaces/styling'; 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_ARR: any[] = [];
export const EMPTY_OBJ: {[key: string]: any} = {}; export const EMPTY_OBJ: {[key: string]: any} = {};
@ -18,10 +23,70 @@ export function createEmptyStylingContext(
element?: LElementNode | null, sanitizer?: StyleSanitizeFn | null, element?: LElementNode | null, sanitizer?: StyleSanitizeFn | null,
initialStylingValues?: InitialStyles): StylingContext { initialStylingValues?: InitialStyles): StylingContext {
return [ 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 { export function getOrCreatePlayerContext(target: {}, context?: LContext | null): PlayerContext {
context = context || getContext(target) !; context = context || getContext(target) !;
if (ngDevMode && !context) { if (ngDevMode && !context) {
@ -30,11 +95,7 @@ export function getOrCreatePlayerContext(target: {}, context?: LContext | null):
} }
const {lViewData, nodeIndex} = context; const {lViewData, nodeIndex} = context;
const value = lViewData[nodeIndex]; const stylingContext = getStylingContext(nodeIndex - HEADER_OFFSET, lViewData);
let stylingContext = value as StylingContext;
if (!Array.isArray(value)) {
stylingContext = lViewData[nodeIndex] = createEmptyStylingContext(value as LElementNode);
}
return stylingContext[StylingIndex.PlayerContext] || allocPlayerContext(stylingContext); return stylingContext[StylingIndex.PlayerContext] || allocPlayerContext(stylingContext);
} }

View File

@ -9,11 +9,12 @@
import {devModeEqual} from '../change_detection/change_detection_util'; import {devModeEqual} from '../change_detection/change_detection_util';
import {assertDefined, assertLessThan} from './assert'; import {assertDefined, assertLessThan} from './assert';
import {readElementValue, readPatchedLViewData} from './context_discovery';
import {ACTIVE_INDEX, LContainer} from './interfaces/container'; 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 {LContainerNode, LElementContainerNode, LElementNode, LNode, TNode, TNodeFlags} from './interfaces/node';
import {RComment, RElement, RText} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling'; 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); 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| export function getLNode(tNode: TNode, hostView: LViewData): LElementNode|LContainerNode|
LElementContainerNode { LElementContainerNode {
return readElementValue(hostView[tNode.index]); 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 { export function isContentQueryHost(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.hasContentQuery) !== 0; return (tNode.flags & TNodeFlags.hasContentQuery) !== 0;
} }
@ -128,3 +163,19 @@ export function getRootView(target: LViewData | {}): LViewData {
export function getRootContext(viewOrComponent: LViewData | {}): RootContext { export function getRootContext(viewOrComponent: LViewData | {}): RootContext {
return getRootView(viewOrComponent)[CONTEXT] as 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 {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getRenderParent, insertView, removeView} from './node_manipulation'; 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'; 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 // TODO: Fix class name, should be ElementRef, but there appears to be a rollup bug
R3ElementRef = class ElementRef_ extends ElementRefToken {}; R3ElementRef = class ElementRef_ extends ElementRefToken {};
} }
return new R3ElementRef(getLNode(tNode, view).native); return new R3ElementRef(getNative(tNode, view));
} }
let R3TemplateRef: { let R3TemplateRef: {
@ -325,7 +325,7 @@ export function createViewRef(
hostTNode: TNode, hostView: LViewData, context: any): ViewEngine_ChangeDetectorRef { hostTNode: TNode, hostView: LViewData, context: any): ViewEngine_ChangeDetectorRef {
if (isComponent(hostTNode)) { if (isComponent(hostTNode)) {
const componentIndex = hostTNode.flags >> TNodeFlags.DirectiveStartingIndexShift; 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); return new ViewRef(componentView, context, componentIndex);
} else if (hostTNode.type === TNodeType.Element) { } else if (hostTNode.type === TNodeType.Element) {
const hostComponentView = findComponentView(hostView); const hostComponentView = findComponentView(hostView);
@ -346,4 +346,4 @@ function getOrCreateRenderer2(view: LViewData): Renderer2 {
/** Returns a Renderer2 (or throws when application was bootstrapped with Renderer3) */ /** Returns a Renderer2 (or throws when application was bootstrapped with Renderer3) */
export function injectRenderer2(): Renderer2 { export function injectRenderer2(): Renderer2 {
return getOrCreateRenderer2(_getViewData()); return getOrCreateRenderer2(_getViewData());
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -6,17 +6,18 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {TemplateRef, ViewContainerRef} from '@angular/core';
import {withBody} from '@angular/private/testing'; import {withBody} from '@angular/private/testing';
import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core'; import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core';
import {getRenderedText, whenRendered} from '../../src/render3/component'; import {getRenderedText, whenRendered} from '../../src/render3/component';
import {directiveInject} from '../../src/render3/di'; import {directiveInject} from '../../src/render3/di';
import {LifecycleHooksFeature, defineComponent, defineDirective} from '../../src/render3/index'; 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, text, textBinding, tick} from '../../src/render3/instructions'; 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 {RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer'; 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('change detection', () => {
describe('markDirty, detectChanges, whenRendered, getRenderedText', () => { describe('markDirty, detectChanges, whenRendered, getRenderedText', () => {
@ -86,6 +87,73 @@ describe('change detection', () => {
await whenRendered(myComp); await whenRendered(myComp);
expect(getRenderedText(myComp)).toEqual('updated'); 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', () => { describe('onPush', () => {

View File

@ -81,7 +81,7 @@ describe('instructions', () => {
element(0, 'div', ['id', 'test', 'title', 'Hello']); element(0, 'div', ['id', 'test', 'title', 'Hello']);
}, () => {}, 1); }, () => {}, 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.id).toEqual('test');
expect(div.title).toEqual('Hello'); expect(div.title).toEqual('Hello');
expect(ngDevMode).toHaveProperties({ expect(ngDevMode).toHaveProperties({
@ -109,7 +109,7 @@ describe('instructions', () => {
]); ]);
}, () => {}, 1); }, () => {}, 1);
const div = (t.hostNode.native as HTMLElement).querySelector('div') !; const div = (t.hostElement as HTMLElement).querySelector('div') !;
const attrs: any = div.attributes; const attrs: any = div.attributes;
expect(attrs['id'].name).toEqual('id'); expect(attrs['id'].name).toEqual('id');
@ -179,7 +179,7 @@ describe('instructions', () => {
t.update(() => elementProperty(0, 'hidden', false)); t.update(() => elementProperty(0, 'hidden', false));
// The hidden property would be true if `false` was stringified into `"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({ expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1, firstTemplatePass: 1,
tNode: 2, // 1 for div, 1 for host element 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 {NgIf} from './common_with_def';
import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util'; 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 {StylingIndex} from '../../src/render3/interfaces/styling';
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
import {directiveInject} from '../../src/render3/di'; import {directiveInject} from '../../src/render3/di';
describe('render3 integration test', () => { describe('render3 integration test', () => {
@ -1414,6 +1415,39 @@ describe('render3 integration test', () => {
expect(fixture.html).toEqual('<span class="existing"></span>'); 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', () => { it('should apply classes properly when nodes have LContainers', () => {
let structuralComp !: StructuralComp; let structuralComp !: StructuralComp;
@ -1448,7 +1482,7 @@ describe('render3 integration test', () => {
/** /**
* <ng-template #foo> * <ng-template #foo>
* Content * Temp Content
* </ng-template> * </ng-template>
* <structural-comp [class.active]="class" [tmp]="foo"></structural-comp> * <structural-comp [class.active]="class" [tmp]="foo"></structural-comp>
*/ */
@ -1477,8 +1511,12 @@ describe('render3 integration test', () => {
fixture.update(); fixture.update();
expect(fixture.html) expect(fixture.html)
.toEqual('<structural-comp class="active">Comp Content</structural-comp>Temp Content'); .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 div1 = hostElm.querySelector('div:first-child') !as any;
const div2 = hostElm.querySelector('div:last-child') !as any; const div2 = hostElm.querySelector('div:last-child') !as any;
const context = getContext(hostElm) !; const context = getContext(hostElm) !;
const elementNode = context.lViewData[context.nodeIndex]; const componentView = context.lViewData[context.nodeIndex];
const elmData = elementNode.data !;
expect(elmData).toContain(myDir1Instance); expect(componentView).toContain(myDir1Instance);
expect(elmData).toContain(myDir2Instance); expect(componentView).toContain(myDir2Instance);
expect(elmData).toContain(myDir3Instance); expect(componentView).toContain(myDir3Instance);
expect(Array.isArray((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy(); expect(Array.isArray((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();
expect(Array.isArray((myDir2Instance 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 d2Context = getContext(myDir2Instance) !;
const d3Context = getContext(myDir3Instance) !; const d3Context = getContext(myDir3Instance) !;
expect(d1Context.lViewData).toEqual(elmData); expect(d1Context.lViewData).toEqual(componentView);
expect(d2Context.lViewData).toEqual(elmData); expect(d2Context.lViewData).toEqual(componentView);
expect(d3Context.lViewData).toEqual(elmData); expect(d3Context.lViewData).toEqual(componentView);
expect((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d1Context); expect((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d1Context);
expect((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d2Context); expect((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d2Context);
@ -2349,7 +2386,7 @@ describe('render3 integration test', () => {
const context = getContext(child) !; const context = getContext(child) !;
expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy();
const componentData = context.lViewData[context.nodeIndex].data; const componentData = context.lViewData[context.nodeIndex];
const component = componentData[CONTEXT]; const component = componentData[CONTEXT];
expect(component instanceof ChildComp).toBeTruthy(); expect(component instanceof ChildComp).toBeTruthy();
expect(component[MONKEY_PATCH_KEY_NAME]).toBe(context.lViewData); 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 {LElementNode} from '../../src/render3/interfaces/node';
import {PlayerHandler} from '../../src/render3/interfaces/player'; import {PlayerHandler} from '../../src/render3/interfaces/player';
import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; 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 {Sanitizer} from '../../src/sanitization/security';
import {Type} from '../../src/type'; import {Type} from '../../src/type';
@ -55,7 +55,7 @@ function noop() {}
* - access to the render `html`. * - access to the render `html`.
*/ */
export class TemplateFixture extends BaseFixture { export class TemplateFixture extends BaseFixture {
hostNode: LElementNode; hostView: LViewData;
private _directiveDefs: DirectiveDefList|null; private _directiveDefs: DirectiveDefList|null;
private _pipeDefs: PipeDefList|null; private _pipeDefs: PipeDefList|null;
private _sanitizer: Sanitizer|null; private _sanitizer: Sanitizer|null;
@ -78,7 +78,7 @@ export class TemplateFixture extends BaseFixture {
this._pipeDefs = toDefs(pipes, extractPipeDef); this._pipeDefs = toDefs(pipes, extractPipeDef);
this._sanitizer = sanitizer || null; this._sanitizer = sanitizer || null;
this._rendererFactory = rendererFactory || domRendererFactory3; this._rendererFactory = rendererFactory || domRendererFactory3;
this.hostNode = renderTemplate( this.hostView = renderTemplate(
this.hostElement, this.hostElement,
(rf: RenderFlags, ctx: any) => { (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
@ -99,8 +99,8 @@ export class TemplateFixture extends BaseFixture {
*/ */
update(updateBlock?: () => void): void { update(updateBlock?: () => void): void {
renderTemplate( renderTemplate(
this.hostNode.native, updateBlock || this.updateBlock, 0, this.vars, null !, this.hostElement, updateBlock || this.updateBlock, 0, this.vars, null !,
this._rendererFactory, this.hostNode, this._directiveDefs, this._pipeDefs, this._sanitizer); 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 const document = ((typeof global == 'object' && global || window) as any).document;
export let containerEl: HTMLElement = null !; export let containerEl: HTMLElement = null !;
let host: LElementNode|null; let hostView: LViewData|null;
const isRenderer2 = const isRenderer2 =
typeof process == 'object' && process.argv[3] && process.argv[3] === '--r=renderer2'; typeof process == 'object' && process.argv[3] && process.argv[3] === '--r=renderer2';
// tslint:disable-next-line:no-console // tslint:disable-next-line:no-console
@ -181,7 +181,7 @@ export function resetDOM() {
containerEl = document.createElement('div'); containerEl = document.createElement('div');
containerEl.setAttribute('host', ''); containerEl.setAttribute('host', '');
document.body.appendChild(containerEl); document.body.appendChild(containerEl);
host = null; hostView = null;
// TODO: assert that the global state is clean (e.g. ngData, previousOrParentNode, etc) // 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, template: ComponentTemplate<any>, ctx: any, consts: number = 0, vars: number = 0,
directives?: DirectiveTypesOrFactory | null, pipes?: PipeTypesOrFactory | null, directives?: DirectiveTypesOrFactory | null, pipes?: PipeTypesOrFactory | null,
providedRendererFactory?: RendererFactory3 | null) { providedRendererFactory?: RendererFactory3 | null) {
host = renderTemplate( hostView = renderTemplate(
containerEl, template, consts, vars, ctx, providedRendererFactory || testRendererFactory, containerEl, template, consts, vars, ctx, providedRendererFactory || testRendererFactory,
host, toDefs(directives, extractDirectiveDef), toDefs(pipes, extractPipeDef)); hostView, toDefs(directives, extractDirectiveDef), toDefs(pipes, extractPipeDef));
return toHtml(containerEl); return toHtml(containerEl);
} }

View File

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