refactor(core): renamed and split out interfaces (#20855)
PR Close #20855
This commit is contained in:
parent
1a9064ba2b
commit
b462f49ce7
@ -9,10 +9,10 @@
|
|||||||
import {ComponentRef, EmbeddedViewRef, Injector} from '../core';
|
import {ComponentRef, EmbeddedViewRef, Injector} from '../core';
|
||||||
import {assertNotNull} from './assert';
|
import {assertNotNull} from './assert';
|
||||||
import {NG_HOST_SYMBOL, createError, createViewState, directiveCreate, elementHost, enterView, leaveView} from './instructions';
|
import {NG_HOST_SYMBOL, createError, createViewState, directiveCreate, elementHost, enterView, leaveView} from './instructions';
|
||||||
import {LElement} from './interfaces';
|
import {LElement} from './l_node';
|
||||||
import {ComponentDef, ComponentType} from './public_interfaces';
|
import {ComponentDef, ComponentType} from './public_interfaces';
|
||||||
import {RElement, Renderer3, RendererFactory3} from './renderer';
|
import {RElement, Renderer3, RendererFactory3} from './renderer';
|
||||||
import {stringify} from './util';
|
import {stringify, notImplemented} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -71,27 +71,28 @@ export function createComponentRef<T>(
|
|||||||
function createViewRef<T>(detectChanges: () => void, context: T): EmbeddedViewRef<T> {
|
function createViewRef<T>(detectChanges: () => void, context: T): EmbeddedViewRef<T> {
|
||||||
return addDestroyable(
|
return addDestroyable(
|
||||||
{
|
{
|
||||||
|
// TODO: rootNodes should be replaced when properly implemented
|
||||||
rootNodes: null !,
|
rootNodes: null !,
|
||||||
// inherited from core/ChangeDetectorRef
|
// inherited from core/ChangeDetectorRef
|
||||||
markForCheck: () => {
|
markForCheck: () => {
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
implement();
|
throw notImplemented();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
detach: () => {
|
detach: () => {
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
implement();
|
throw notImplemented();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
detectChanges: detectChanges,
|
detectChanges: detectChanges,
|
||||||
checkNoChanges: () => {
|
checkNoChanges: () => {
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
implement();
|
throw notImplemented();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reattach: () => {
|
reattach: () => {
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
implement();
|
throw notImplemented();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -105,10 +106,6 @@ interface DestroyRef<T> {
|
|||||||
onDestroy(cb: Function): void;
|
onDestroy(cb: Function): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function implement() {
|
|
||||||
throw new Error('NotImplemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDestroyable<T, C>(obj: any, context: C): T&DestroyRef<C> {
|
function addDestroyable<T, C>(obj: any, context: C): T&DestroyRef<C> {
|
||||||
let destroyFn: Function[]|null = null;
|
let destroyFn: Function[]|null = null;
|
||||||
obj.destroyed = false;
|
obj.destroyed = false;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
// correctly implementing its interfaces for backwards compatibility.
|
// correctly implementing its interfaces for backwards compatibility.
|
||||||
import * as viewEngine from '../core';
|
import * as viewEngine from '../core';
|
||||||
import {BLOOM_SIZE, NG_ELEMENT_ID, getOrCreateNodeInjector} from './instructions';
|
import {BLOOM_SIZE, NG_ELEMENT_ID, getOrCreateNodeInjector} from './instructions';
|
||||||
import {LContainer, LNodeFlags, LNodeInjector} from './interfaces';
|
import {LContainer, LNodeFlags, LNodeInjector} from './l_node';
|
||||||
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
||||||
import {stringify, notImplemented} from './util';
|
import {stringify, notImplemented} from './util';
|
||||||
|
|
||||||
|
@ -10,11 +10,9 @@ import './ng_dev_mode';
|
|||||||
|
|
||||||
import {Type} from '../core';
|
import {Type} from '../core';
|
||||||
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull} from './assert';
|
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull} from './assert';
|
||||||
import {
|
import {CssSelector, ContainerState, ProjectionState, QueryState, ViewState} from './interfaces';
|
||||||
CSSSelector, ContainerState, InitialInputData, InitialInputs, LContainer, LContainerStatic, LElement, LNode,
|
import {LText, LView, LElement, LNode, LNodeFlags, LNodeInjector, LContainer, LProjection} from './l_node';
|
||||||
LNodeFlags, LNodeInjector, LNodeStatic, LProjection, LText, LView, MinificationData, MinificationDataValue,
|
import {NgStaticData, LNodeStatic, LContainerStatic, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue,} from './l_node_static';
|
||||||
ProjectionState, QueryState, ViewState, NgStaticData
|
|
||||||
} from './interfaces';
|
|
||||||
import {assertNodeType} from './node_assert';
|
import {assertNodeType} from './node_assert';
|
||||||
import {appendChild, insertChild, insertView, processProjectedNode, removeView} from './node_manipulation';
|
import {appendChild, insertChild, insertView, processProjectedNode, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelector} from './node_selector_matcher';
|
import {isNodeMatchingSelector} from './node_selector_matcher';
|
||||||
@ -439,7 +437,7 @@ export function listenerCreate(
|
|||||||
// if we create LNodeStatic here, inputs must be undefined so we know they still need to be
|
// if we create LNodeStatic here, inputs must be undefined so we know they still need to be
|
||||||
// checked
|
// checked
|
||||||
mergeData.outputs = null;
|
mergeData.outputs = null;
|
||||||
mergeData = generateMinifiedData(node.flags, mergeData);
|
mergeData = generatePropertyAliases(node.flags, mergeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputs = mergeData.outputs;
|
const outputs = mergeData.outputs;
|
||||||
@ -474,7 +472,7 @@ export function elementEnd() {
|
|||||||
}
|
}
|
||||||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Element);
|
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Element);
|
||||||
const query = previousOrParentNode.query;
|
const query = previousOrParentNode.query;
|
||||||
query && query.add(previousOrParentNode);
|
query && query.addNode(previousOrParentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -525,11 +523,11 @@ export function elementProperty<T>(index: number, propName: string, value: T | N
|
|||||||
if (staticData.inputs === undefined) {
|
if (staticData.inputs === undefined) {
|
||||||
// mark inputs as checked
|
// mark inputs as checked
|
||||||
staticData.inputs = null;
|
staticData.inputs = null;
|
||||||
staticData = generateMinifiedData(node.flags, staticData, true);
|
staticData = generatePropertyAliases(node.flags, staticData, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputData = staticData.inputs;
|
const inputData = staticData.inputs;
|
||||||
let dataValue: MinificationDataValue|null;
|
let dataValue: PropertyAliasValue|null;
|
||||||
if (inputData && (dataValue = inputData[propName])) {
|
if (inputData && (dataValue = inputData[propName])) {
|
||||||
setInputsForProperty(dataValue, value);
|
setInputsForProperty(dataValue, value);
|
||||||
} else {
|
} else {
|
||||||
@ -572,22 +570,22 @@ function setInputsForProperty(inputs: (number | string)[], value: any): void {
|
|||||||
*
|
*
|
||||||
* @param index Index where data should be stored in ngStaticData
|
* @param index Index where data should be stored in ngStaticData
|
||||||
*/
|
*/
|
||||||
function generateMinifiedData(flags: number, data: LNodeStatic, isInputData = false): LNodeStatic {
|
function generatePropertyAliases(flags: number, data: LNodeStatic, isInputData = false): LNodeStatic {
|
||||||
const start = flags >> LNodeFlags.INDX_SHIFT;
|
const start = flags >> LNodeFlags.INDX_SHIFT;
|
||||||
const size = (flags & LNodeFlags.SIZE_MASK) >> LNodeFlags.SIZE_SHIFT;
|
const size = (flags & LNodeFlags.SIZE_MASK) >> LNodeFlags.SIZE_SHIFT;
|
||||||
|
|
||||||
for (let i = start, ii = start + size; i < ii; i++) {
|
for (let i = start, ii = start + size; i < ii; i++) {
|
||||||
const directiveDef: DirectiveDef<any> = ngStaticData ![i] as DirectiveDef<any>;
|
const directiveDef: DirectiveDef<any> = ngStaticData ![i] as DirectiveDef<any>;
|
||||||
const minifiedPropertyMap: {[minifiedKey: string]: string} =
|
const propertyAliasMap: {[publicName: string]: string} =
|
||||||
isInputData ? directiveDef.inputs : directiveDef.outputs;
|
isInputData ? directiveDef.inputs : directiveDef.outputs;
|
||||||
for (let unminifiedKey in minifiedPropertyMap) {
|
for (let publicName in propertyAliasMap) {
|
||||||
if (minifiedPropertyMap.hasOwnProperty(unminifiedKey)) {
|
if (propertyAliasMap.hasOwnProperty(publicName)) {
|
||||||
const minifiedKey = minifiedPropertyMap[unminifiedKey];
|
const internalName = propertyAliasMap[publicName];
|
||||||
const staticDirData: MinificationData = isInputData ? (data.inputs || (data.inputs = {})) :
|
const staticDirData: PropertyAliases = isInputData ? (data.inputs || (data.inputs = {})) :
|
||||||
(data.outputs || (data.outputs = {}));
|
(data.outputs || (data.outputs = {}));
|
||||||
const hasProperty: boolean = staticDirData.hasOwnProperty(unminifiedKey);
|
const hasProperty: boolean = staticDirData.hasOwnProperty(publicName);
|
||||||
hasProperty ? staticDirData[unminifiedKey].push(i, minifiedKey) :
|
hasProperty ? staticDirData[publicName].push(i, internalName) :
|
||||||
(staticDirData[unminifiedKey] = [i, minifiedKey]);
|
(staticDirData[publicName] = [i, internalName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -851,9 +849,9 @@ export function containerCreate(
|
|||||||
index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[]): void {
|
index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[]): void {
|
||||||
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
|
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
|
||||||
|
|
||||||
// If the direct parent of the container is a view, its children (including its comment)
|
// If the direct parent of the container is a view, its views (including its comment)
|
||||||
// will need to be added through insertView() when its parent view is being inserted.
|
// will need to be added through insertView() when its parent view is being inserted.
|
||||||
// For now, it is marked "headless" so we know to append its children later.
|
// For now, it is marked "headless" so we know to append its views later.
|
||||||
let comment = renderer.createComment(ngDevMode ? 'container' : '');
|
let comment = renderer.createComment(ngDevMode ? 'container' : '');
|
||||||
let renderParent: LElement|null = null;
|
let renderParent: LElement|null = null;
|
||||||
const currentParent = isParent ? previousOrParentNode : previousOrParentNode.parent !;
|
const currentParent = isParent ? previousOrParentNode : previousOrParentNode.parent !;
|
||||||
@ -866,7 +864,7 @@ export function containerCreate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const node = createLNode(index, LNodeFlags.Container, comment, <ContainerState>{
|
const node = createLNode(index, LNodeFlags.Container, comment, <ContainerState>{
|
||||||
children: [],
|
views: [],
|
||||||
nextIndex: 0, renderParent,
|
nextIndex: 0, renderParent,
|
||||||
template: template == null ? null : template,
|
template: template == null ? null : template,
|
||||||
next: null,
|
next: null,
|
||||||
@ -922,7 +920,7 @@ export function refreshContainerEnd(): void {
|
|||||||
const container = previousOrParentNode as LContainer;
|
const container = previousOrParentNode as LContainer;
|
||||||
ngDevMode && assertNodeType(container, LNodeFlags.Container);
|
ngDevMode && assertNodeType(container, LNodeFlags.Container);
|
||||||
const nextIndex = container.data.nextIndex;
|
const nextIndex = container.data.nextIndex;
|
||||||
while (nextIndex < container.data.children.length) {
|
while (nextIndex < container.data.views.length) {
|
||||||
// remove extra view.
|
// remove extra view.
|
||||||
removeView(container, nextIndex);
|
removeView(container, nextIndex);
|
||||||
}
|
}
|
||||||
@ -938,14 +936,14 @@ export function viewCreate(viewBlockId: number): boolean {
|
|||||||
const container = (isParent ? previousOrParentNode : previousOrParentNode.parent !) as LContainer;
|
const container = (isParent ? previousOrParentNode : previousOrParentNode.parent !) as LContainer;
|
||||||
ngDevMode && assertNodeType(container, LNodeFlags.Container);
|
ngDevMode && assertNodeType(container, LNodeFlags.Container);
|
||||||
const containerState = container.data;
|
const containerState = container.data;
|
||||||
const children = containerState.children;
|
const views = containerState.views;
|
||||||
|
|
||||||
const existingView: LView|false = !creationMode && containerState.nextIndex < children.length &&
|
const existingView: LView|false = !creationMode && containerState.nextIndex < views.length &&
|
||||||
children[containerState.nextIndex];
|
views[containerState.nextIndex];
|
||||||
let viewUpdateMode = existingView && viewBlockId === (existingView as LView).data.id;
|
let viewUpdateMode = existingView && viewBlockId === (existingView as LView).data.id;
|
||||||
|
|
||||||
if (viewUpdateMode) {
|
if (viewUpdateMode) {
|
||||||
previousOrParentNode = children[containerState.nextIndex++];
|
previousOrParentNode = views[containerState.nextIndex++];
|
||||||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.View);
|
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.View);
|
||||||
isParent = true;
|
isParent = true;
|
||||||
enterView((existingView as LView).data, previousOrParentNode as LView);
|
enterView((existingView as LView).data, previousOrParentNode as LView);
|
||||||
@ -990,8 +988,8 @@ export function viewEnd(): void {
|
|||||||
ngDevMode && assertNodeType(viewNode, LNodeFlags.View);
|
ngDevMode && assertNodeType(viewNode, LNodeFlags.View);
|
||||||
ngDevMode && assertNodeType(container, LNodeFlags.Container);
|
ngDevMode && assertNodeType(container, LNodeFlags.Container);
|
||||||
const containerState = container.data;
|
const containerState = container.data;
|
||||||
const previousView = containerState.nextIndex <= containerState.children.length ?
|
const previousView = containerState.nextIndex <= containerState.views.length ?
|
||||||
containerState.children[containerState.nextIndex - 1] as LView :
|
containerState.views[containerState.nextIndex - 1] as LView :
|
||||||
null;
|
null;
|
||||||
const viewIdChanged = previousView == null ? true : previousView.data.id !== viewNode.data.id;
|
const viewIdChanged = previousView == null ? true : previousView.data.id !== viewNode.data.id;
|
||||||
|
|
||||||
@ -1033,9 +1031,9 @@ export const refreshComponent:
|
|||||||
* each projected node belongs (it re-distributes nodes among "buckets" where each "bucket" is
|
* each projected node belongs (it re-distributes nodes among "buckets" where each "bucket" is
|
||||||
* backed by a selector).
|
* backed by a selector).
|
||||||
*
|
*
|
||||||
* @param {CSSSelector[]} selectors
|
* @param {CssSelector[]} selectors
|
||||||
*/
|
*/
|
||||||
export function distributeProjectedNodes(selectors?: CSSSelector[]): LNode[][] {
|
export function distributeProjectedNodes(selectors?: CssSelector[]): LNode[][] {
|
||||||
const noOfNodeBuckets = selectors ? selectors.length + 1 : 1;
|
const noOfNodeBuckets = selectors ? selectors.length + 1 : 1;
|
||||||
const distributedNodes = new Array<LNode[]>(noOfNodeBuckets);
|
const distributedNodes = new Array<LNode[]>(noOfNodeBuckets);
|
||||||
for (let i = 0; i < noOfNodeBuckets; i++) {
|
for (let i = 0; i < noOfNodeBuckets; i++) {
|
||||||
|
@ -6,47 +6,22 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ElementRef, Injector, QueryList, TemplateRef, Type, ViewContainerRef} from '../core';
|
import {QueryList, Type} from '../core';
|
||||||
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
||||||
import {RComment, RElement, RText, Renderer3} from './renderer';
|
import {Renderer3} from './renderer';
|
||||||
|
import {LNode, LView, LElement, LContainer, LText} from './l_node';
|
||||||
|
import {LNodeStatic} from './l_node_static';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
const ngDevMode: boolean;
|
const ngDevMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum LNodeFlags {
|
|
||||||
Container = 0b00,
|
|
||||||
Projection = 0b01,
|
|
||||||
View = 0b10,
|
|
||||||
Element = 0b11,
|
|
||||||
ViewOrElement = 0b10,
|
|
||||||
SIZE_SKIP = 0b100,
|
|
||||||
SIZE_SHIFT = 2,
|
|
||||||
INDX_SHIFT = 12,
|
|
||||||
TYPE_MASK = 0b00000000000000000000000000000011,
|
|
||||||
SIZE_MASK = 0b00000000000000000000111111111100,
|
|
||||||
INDX_MASK = 0b11111111111111111111000000000000,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTES:
|
|
||||||
*
|
|
||||||
* Each Array costs 70 bytes and is composed of `Array` and `(array)` object
|
|
||||||
* - `Array` javascript visible object: 32 bytes
|
|
||||||
* - `(array)` VM object where the array is actually stored in: 38 bytes
|
|
||||||
*
|
|
||||||
* Each Object cost is 24 bytes plus 8 bytes per property.
|
|
||||||
*
|
|
||||||
* For small arrays, it is more efficient to store the data as a linked list
|
|
||||||
* of items rather than small arrays. However, the array access is faster as
|
|
||||||
* shown here: https://jsperf.com/small-arrays-vs-linked-objects
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `ViewState` stores all of the information needed to process the instructions as
|
* `ViewState` stores all of the information needed to process the instructions as
|
||||||
* they are invoked from the template. `ViewState` is saved when a child `View` is
|
* they are invoked from the template. Each embedded view and component view has its
|
||||||
* being processed and restored when the child `View` is done.
|
* own `ViewState`. When processing a particular view, we set the `currentView` to that
|
||||||
|
* `ViewState`. When that view is done processing, the `currentView` is set back to
|
||||||
|
* whatever the original `currentView` was before(the parent `ViewState`).
|
||||||
*
|
*
|
||||||
* Keeping separate state for each view facilities view insertion / deletion, so we
|
* Keeping separate state for each view facilities view insertion / deletion, so we
|
||||||
* don't have to edit the data array based on which views are present.
|
* don't have to edit the data array based on which views are present.
|
||||||
@ -60,9 +35,12 @@ export interface ViewState {
|
|||||||
readonly parent: ViewState|null;
|
readonly parent: ViewState|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pointer to the `LView` node which represents the root of the view. We
|
* Pointer to the `LView` or `LElement` node which represents the root of the view.
|
||||||
* need this to be able to efficiently find the `LView` when inserting the
|
*
|
||||||
* view into an anchor.
|
* If `LView`, this is an embedded view of a container. We need this to be able to
|
||||||
|
* efficiently find the `LView` when inserting the view into an anchor.
|
||||||
|
*
|
||||||
|
* If `LElement`, this is the ViewState of a component.
|
||||||
*/
|
*/
|
||||||
readonly node: LView|LElement;
|
readonly node: LView|LElement;
|
||||||
|
|
||||||
@ -87,10 +65,11 @@ export interface ViewState {
|
|||||||
bindingStartIndex: number|null;
|
bindingStartIndex: number|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a view is destroyed, listeners need to be released
|
* When a view is destroyed, listeners need to be released and onDestroy callbacks
|
||||||
* and onDestroy callbacks need to be called. This cleanup array
|
* need to be called. This cleanup array stores both listener data (in chunks of 4)
|
||||||
* stores both listener data (in chunks of 4) and onDestroy data
|
* and onDestroy data (in chunks of 2) for a particular view. Combining the arrays
|
||||||
* (in chunks of 2), as they'll be processed at the same time.
|
* saves on memory (70 bytes per array) and on a few bytes of code size (for two
|
||||||
|
* separate for loops).
|
||||||
*
|
*
|
||||||
* If it's a listener being stored:
|
* If it's a listener being stored:
|
||||||
* 1st index is: event name to remove
|
* 1st index is: event name to remove
|
||||||
@ -105,25 +84,31 @@ export interface ViewState {
|
|||||||
cleanup: any[]|null;
|
cleanup: any[]|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Necessary so views can traverse through their nested views
|
* The first ViewState or ContainerState beneath this ViewState in the hierarchy.
|
||||||
|
*
|
||||||
|
* Necessary to store this so views can traverse through their nested views
|
||||||
* to remove listeners and call onDestroy callbacks.
|
* to remove listeners and call onDestroy callbacks.
|
||||||
*
|
*
|
||||||
* For embedded views, we store the container rather than the
|
* For embedded views, we store the ContainerState rather than the first ViewState
|
||||||
* first view to avoid managing splicing when views are added/removed.
|
* to avoid managing splicing when views are added/removed.
|
||||||
*/
|
*/
|
||||||
child: ViewState|ContainerState|null;
|
child: ViewState|ContainerState|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tail allows us to quickly add a new state to the end of the
|
* The last ViewState or ContainerState beneath this ViewState in the hierarchy.
|
||||||
* view list without having to propagate starting from the first child.
|
*
|
||||||
|
* The tail allows us to quickly add a new state to the end of the view list
|
||||||
|
* without having to propagate starting from the first child.
|
||||||
*/
|
*/
|
||||||
tail: ViewState|ContainerState|null;
|
tail: ViewState|ContainerState|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows us to propagate between view states.
|
* The next sibling ViewState or ContainerState.
|
||||||
*
|
*
|
||||||
* Embedded views already have a node.next, but it is only set for views
|
* Allows us to propagate between sibling view states that aren't in the same
|
||||||
* in the same container. We need a way to link component views as well.
|
* container. Embedded views already have a node.next, but it is only set for
|
||||||
|
* views in the same container. We need a way to link component views and views
|
||||||
|
* across containers as well.
|
||||||
*/
|
*/
|
||||||
next: ViewState|ContainerState|null;
|
next: ViewState|ContainerState|null;
|
||||||
|
|
||||||
@ -152,183 +137,12 @@ export interface ViewState {
|
|||||||
ngStaticData: (LNodeStatic|DirectiveDef<any>|null)[];
|
ngStaticData: (LNodeStatic|DirectiveDef<any>|null)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LNodeInjector {
|
|
||||||
/**
|
|
||||||
* We need to store a reference to the injector's parent so DI can keep looking up
|
|
||||||
* the injector tree until it finds the dependency it's looking for.
|
|
||||||
*/
|
|
||||||
readonly parent: LNodeInjector|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows access to the directives array in that node's static data and to
|
|
||||||
* the node's flags (for starting directive index and directive size). Necessary
|
|
||||||
* for DI to retrieve a directive from the data array if injector indicates
|
|
||||||
* it is there.
|
|
||||||
*/
|
|
||||||
readonly node: LElement|LContainer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The following bloom filter determines whether a directive is available
|
|
||||||
* on the associated node or not. This prevents us from searching the directives
|
|
||||||
* array at this level unless it's probable the directive is in it.
|
|
||||||
*
|
|
||||||
* - bf0: Check directive IDs 0-31 (IDs are % 128)
|
|
||||||
* - bf1: Check directive IDs 33-63
|
|
||||||
* - bf2: Check directive IDs 64-95
|
|
||||||
* - bf3: Check directive IDs 96-127
|
|
||||||
*/
|
|
||||||
bf0: number;
|
|
||||||
bf1: number;
|
|
||||||
bf2: number;
|
|
||||||
bf3: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* cbf0 - cbf3 properties determine whether a directive is available through a
|
|
||||||
* parent injector. They refer to the merged values of parent bloom filters. This
|
|
||||||
* allows us to skip looking up the chain unless it's probable that directive exists
|
|
||||||
* up the chain.
|
|
||||||
*/
|
|
||||||
cbf0: number;
|
|
||||||
cbf1: number;
|
|
||||||
cbf2: number;
|
|
||||||
cbf3: number;
|
|
||||||
injector: Injector|null;
|
|
||||||
|
|
||||||
/** Stores the TemplateRef so subsequent injections of the TemplateRef get the same instance. */
|
|
||||||
templateRef: TemplateRef<any>|null;
|
|
||||||
|
|
||||||
/** Stores the ViewContainerRef so subsequent injections of the ViewContainerRef get the same
|
|
||||||
* instance. */
|
|
||||||
viewContainerRef: ViewContainerRef|null;
|
|
||||||
|
|
||||||
/** Stores the ElementRef so subsequent injections of the ElementRef get the same instance. */
|
|
||||||
elementRef: ElementRef|null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LNode is an internal data structure which is used for the incremental DOM algorithm.
|
|
||||||
*
|
|
||||||
* The data structure is optimized for speed and size.
|
|
||||||
*
|
|
||||||
* In order to be fast, all subtypes of `LNode` should have the same shape.
|
|
||||||
* Because size of the `LNode` matters, many fields have multiple roles depending
|
|
||||||
* on the `LNode` subtype.
|
|
||||||
*
|
|
||||||
* NOTE: This is a private data structure and should not be exported by any of the
|
|
||||||
* instructions.
|
|
||||||
*/
|
|
||||||
export interface LNode {
|
|
||||||
/**
|
|
||||||
* This number stores three values using its bits:
|
|
||||||
*
|
|
||||||
* - the type of the node (first 2 bits)
|
|
||||||
* - the number of directives on that node (next 10 bits)
|
|
||||||
* - the starting index of the node's directives in the directives array (last 20 bits).
|
|
||||||
*
|
|
||||||
* The latter two values are necessary so DI can effectively search the directives associated
|
|
||||||
* with a node without searching the whole directives array.
|
|
||||||
*/
|
|
||||||
flags: LNodeFlags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The associated DOM node. Storing this allows us to:
|
|
||||||
* - append children to their element parents in the DOM (e.g. `parent.native.appendChild(...)`)
|
|
||||||
* - retrieve the sibling elements of text nodes whose creation / insertion has been delayed
|
|
||||||
* - mark locations where child views should be inserted (for containers)
|
|
||||||
*/
|
|
||||||
readonly native: RElement|RText|RComment|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We need a reference to a node's parent so we can append the node to its parent's native
|
|
||||||
* element at the appropriate time.
|
|
||||||
*/
|
|
||||||
readonly parent: LNode|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* First child of the current node.
|
|
||||||
*/
|
|
||||||
child: LNode|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The next sibling node. Necessary so we can propagate through the root nodes of a view
|
|
||||||
* to insert them or remove them from the DOM.
|
|
||||||
*/
|
|
||||||
next: LNode|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If ViewState, then `data` contains lightDOM.
|
|
||||||
* If LContainer, then `data` contains ContainerState
|
|
||||||
*/
|
|
||||||
readonly data: ViewState|ContainerState|ProjectionState|null;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Each node belongs to a view.
|
|
||||||
*
|
|
||||||
* When the injector is walking up a tree, it needs access to the `directives` (part of view).
|
|
||||||
*/
|
|
||||||
readonly view: ViewState;
|
|
||||||
|
|
||||||
/** The injector associated with this node. Necessary for DI. */
|
|
||||||
nodeInjector: LNodeInjector|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional `QueryState` used for tracking queries.
|
|
||||||
*
|
|
||||||
* If present the node creation/updates are reported to the `QueryState`.
|
|
||||||
*/
|
|
||||||
query: QueryState|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pointer to the corresponding LNodeStatic object, which stores static
|
|
||||||
* data about this node.
|
|
||||||
*/
|
|
||||||
staticData: LNodeStatic|null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for tracking queries.
|
|
||||||
*/
|
|
||||||
export interface QueryState {
|
|
||||||
/**
|
|
||||||
* Used to ask query if it should be cloned to the child element.
|
|
||||||
*
|
|
||||||
* For example in the case of deep queries the `child()` returns
|
|
||||||
* query for the child node. In case of shallow queries it returns
|
|
||||||
* `null`.
|
|
||||||
*/
|
|
||||||
child(): QueryState|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify `QueryState` that a `LNode` has been created.
|
|
||||||
*/
|
|
||||||
add(node: LNode): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify `QueryState` that a `LView` has been added to `LContainer`.
|
|
||||||
*/
|
|
||||||
insert(container: LContainer, view: LView, insertIndex: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify `QueryState` that a `LView` has been removed from `LContainer`.
|
|
||||||
*/
|
|
||||||
remove(container: LContainer, view: LView, removeIndex: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add additional `QueryList` to track.
|
|
||||||
*
|
|
||||||
* @param queryList `QueryList` to update with changes.
|
|
||||||
* @param predicate Either `Type` or selector array of [key, value] predicates.
|
|
||||||
* @param descend If true the query will recursively apply to the children.
|
|
||||||
*/
|
|
||||||
track<T>(queryList: QueryList<T>, predicate: Type<T>|any[], descend?: boolean): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The state associated with an LContainer */
|
/** The state associated with an LContainer */
|
||||||
export interface ContainerState {
|
export interface ContainerState {
|
||||||
/**
|
/**
|
||||||
* The next active index in the children array to read or write to. This helps us
|
* The next active index in the views array to read or write to. This helps us
|
||||||
* keep track of where we are in the children array.
|
* keep track of where we are in the views array.
|
||||||
*/
|
*/
|
||||||
nextIndex: number;
|
nextIndex: number;
|
||||||
|
|
||||||
@ -351,7 +165,7 @@ export interface ContainerState {
|
|||||||
* (and don't need to be re-added) and so we can remove views from the DOM when they
|
* (and don't need to be re-added) and so we can remove views from the DOM when they
|
||||||
* are no longer required.
|
* are no longer required.
|
||||||
*/
|
*/
|
||||||
readonly children: LView[];
|
readonly views: LView[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -361,16 +175,16 @@ export interface ContainerState {
|
|||||||
* in another `LView` which in turn is contained in another `LContainer` and therefore
|
* in another `LView` which in turn is contained in another `LContainer` and therefore
|
||||||
* it does not yet have its own parent.
|
* it does not yet have its own parent.
|
||||||
*
|
*
|
||||||
* If `renderParent` is not `null` than it may be:
|
* If `renderParent` is not `null` then it may be:
|
||||||
* - same as `LContainer.parent` in which case it is just a normal container.
|
* - same as `LContainer.parent` in which case it is just a normal container.
|
||||||
* - different from `LContainer.parent` in which case it has been re-projected.
|
* - different from `LContainer.parent` in which case it has been re-projected.
|
||||||
* In other words `LContainer.parent` is logical parent where as
|
* In other words `LContainer.parent` is logical parent where as
|
||||||
* `ContainerState.projectedParent` is render parent.
|
* `ContainerState.projectedParent` is render parent.
|
||||||
*
|
*
|
||||||
* When views are inserted into `LContainer` than `renderParent` is:
|
* When views are inserted into `LContainer` then `renderParent` is:
|
||||||
* - `null`, we are in `LView` keep going up a hierarchy until actual
|
* - `null`, we are in `LView` keep going up a hierarchy until actual
|
||||||
* `renderParent` is found.
|
* `renderParent` is found.
|
||||||
* - not `null`, than use the `projectedParent.native` as the `RElement` to insert
|
* - not `null`, then use the `projectedParent.native` as the `RElement` to insert
|
||||||
* `LView`s into.
|
* `LView`s into.
|
||||||
*/
|
*/
|
||||||
renderParent: LElement|null;
|
renderParent: LElement|null;
|
||||||
@ -381,211 +195,61 @@ export interface ContainerState {
|
|||||||
readonly template: ComponentTemplate<any>|null;
|
readonly template: ComponentTemplate<any>|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This mapping is necessary so we can set input properties and output listeners
|
|
||||||
* properly at runtime when property names are minified.
|
|
||||||
*
|
|
||||||
* Key: original unminified input or output name
|
|
||||||
* Value: array containing minified name and related directive index
|
|
||||||
*
|
|
||||||
* The value must be an array to support inputs and outputs with the same name
|
|
||||||
* on the same node.
|
|
||||||
*/
|
|
||||||
export type MinificationData = {
|
|
||||||
[key: string]: MinificationDataValue
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value in MinificationData objects.
|
|
||||||
*
|
|
||||||
* In each array:
|
|
||||||
* Even indices: directive index
|
|
||||||
* Odd indices: minified name
|
|
||||||
*
|
|
||||||
* e.g. [0, 'change-minified']
|
|
||||||
*/
|
|
||||||
export type MinificationDataValue = (number | string)[];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This array contains information about input properties that
|
|
||||||
* need to be set once from attribute data. It's ordered by
|
|
||||||
* directive index (relative to element) so it's simple to
|
|
||||||
* look up a specific directive's initial input data.
|
|
||||||
*
|
|
||||||
* Within each sub-array:
|
|
||||||
*
|
|
||||||
* Even indices: minified input name
|
|
||||||
* Odd indices: initial value
|
|
||||||
*
|
|
||||||
* If a directive on a node does not have any input properties
|
|
||||||
* that should be set from attributes, its index is set to null
|
|
||||||
* to avoid a sparse array.
|
|
||||||
*
|
|
||||||
* e.g. [null, ['role-min', 'button']]
|
|
||||||
*/
|
|
||||||
export type InitialInputData = (InitialInputs | null)[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by InitialInputData to store input properties
|
|
||||||
* that should be set once from attributes.
|
|
||||||
*
|
|
||||||
* Even indices: minified input name
|
|
||||||
* Odd indices: initial value
|
|
||||||
*
|
|
||||||
* e.g. ['role-min', 'button']
|
|
||||||
*/
|
|
||||||
export type InitialInputs = string[];
|
|
||||||
|
|
||||||
/** The type of the global ngStaticData array. */
|
|
||||||
export type NgStaticData = (LNodeStatic | DirectiveDef<any> | null)[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LNode binding data for a particular node that is shared between all templates
|
|
||||||
* of a specific type.
|
|
||||||
*
|
|
||||||
* If a property is:
|
|
||||||
* - Minification Data: that property's data was generated and this is it
|
|
||||||
* - Null: that property's data was already generated and nothing was found.
|
|
||||||
* - Undefined: that property's data has not yet been generated
|
|
||||||
*/
|
|
||||||
export interface LNodeStatic {
|
|
||||||
/** The tag name associated with this node. */
|
|
||||||
tagName: string|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Static attributes associated with an element. We need to store
|
|
||||||
* static attributes to support content projection with selectors.
|
|
||||||
* Attributes are stored statically because reading them from the DOM
|
|
||||||
* would be way too slow for content projection and queries.
|
|
||||||
*
|
|
||||||
* Since attrs will always be calculated first, they will never need
|
|
||||||
* to be marked undefined by other instructions.
|
|
||||||
*/
|
|
||||||
attrs: string[]|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This property contains information about input properties that
|
|
||||||
* need to be set once from attribute data.
|
|
||||||
*/
|
|
||||||
initialInputs: InitialInputData|null|undefined;
|
|
||||||
|
|
||||||
/** Input data for all directives on this node. */
|
|
||||||
inputs: MinificationData|null|undefined;
|
|
||||||
|
|
||||||
/** Output data for all directives on this node. */
|
|
||||||
outputs: MinificationData|null|undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If this LNodeStatic corresponds to an LContainer, the container will
|
|
||||||
* need to have nested static data for each of its embedded views.
|
|
||||||
* Otherwise, nodes in embedded views with the same index as nodes
|
|
||||||
* in their parent views will overwrite each other, as they are in
|
|
||||||
* the same template.
|
|
||||||
*
|
|
||||||
* Each index in this array corresponds to the static data for a certain
|
|
||||||
* view. So if you had V(0) and V(1) in a container, you might have:
|
|
||||||
*
|
|
||||||
* [
|
|
||||||
* [{tagName: 'div', attrs: ...}, null], // V(0) ngData
|
|
||||||
* [{tagName: 'button', attrs ...}, null] // V(1) ngData
|
|
||||||
* ]
|
|
||||||
*/
|
|
||||||
containerStatic: (LNodeStatic|null)[][]|null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Static data for an LElement */
|
|
||||||
export interface LElementStatic extends LNodeStatic { containerStatic: null; }
|
|
||||||
|
|
||||||
/** Static data for an LContainer */
|
|
||||||
export interface LContainerStatic extends LNodeStatic { containerStatic: (LNodeStatic|null)[][]; }
|
|
||||||
|
|
||||||
/** Interface necessary to work with view tree traversal */
|
/** Interface necessary to work with view tree traversal */
|
||||||
export interface ViewOrContainerState {
|
export interface ViewOrContainerState {
|
||||||
next: ViewState|ContainerState|null;
|
next: ViewState|ContainerState|null;
|
||||||
child?: ViewState|ContainerState|null;
|
child?: ViewState|ContainerState|null;
|
||||||
children?: LView[];
|
views?: LView[];
|
||||||
parent: ViewState|null;
|
parent: ViewState|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** LNode representing an element. */
|
|
||||||
export interface LElement extends LNode {
|
|
||||||
/** The DOM element associated with this node. */
|
|
||||||
readonly native: RElement;
|
|
||||||
|
|
||||||
child: LContainer|LElement|LText|LProjection|null;
|
|
||||||
next: LContainer|LElement|LText|LProjection|null;
|
|
||||||
|
|
||||||
/** If Component than data has ViewState (light DOM) */
|
|
||||||
readonly data: ViewState|null;
|
|
||||||
|
|
||||||
/** LElement nodes can be inside other LElement nodes or inside LViews. */
|
|
||||||
readonly parent: LElement|LView;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** LNode representing a #text node. */
|
|
||||||
export interface LText extends LNode {
|
|
||||||
/** The text node associated with this node. */
|
|
||||||
native: RText;
|
|
||||||
child: null;
|
|
||||||
next: LContainer|LElement|LText|LProjection|null;
|
|
||||||
|
|
||||||
/** LText nodes can be inside LElement nodes or inside LViews. */
|
|
||||||
readonly parent: LElement|LView;
|
|
||||||
readonly data: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract node which contains root nodes of a view.
|
|
||||||
*/
|
|
||||||
export interface LView extends LNode {
|
|
||||||
readonly native: null;
|
|
||||||
child: LContainer|LElement|LText|LProjection|null;
|
|
||||||
next: LView|null;
|
|
||||||
|
|
||||||
/** LView nodes can only be added to LContainers. */
|
|
||||||
readonly parent: LContainer|null;
|
|
||||||
readonly data: ViewState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract node container which contains other views.
|
|
||||||
*/
|
|
||||||
export interface LContainer extends LNode {
|
|
||||||
/**
|
|
||||||
* This comment node is appended to the container's parent element to mark where
|
|
||||||
* in the DOM the container's child views should be added.
|
|
||||||
*
|
|
||||||
* If the container is a root node of a view, this comment will not be appended
|
|
||||||
* until the parent view is processed.
|
|
||||||
*/
|
|
||||||
readonly native: RComment;
|
|
||||||
readonly data: ContainerState;
|
|
||||||
child: null;
|
|
||||||
next: LContainer|LElement|LText|LProjection|null;
|
|
||||||
|
|
||||||
/** Containers can be added to elements or views. */
|
|
||||||
readonly parent: LElement|LView|null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A projection state is just an array of projected nodes.
|
* A projection state is just an array of projected nodes.
|
||||||
*
|
*
|
||||||
* It would be nice if we could not need an array, but since a projected note can be
|
* It would be nice if we could not need an array, but since a projected node can be
|
||||||
* re-projected, the same node can be part of more than one LProjection which makes
|
* re-projected, the same node can be part of more than one LProjection which makes
|
||||||
* list approach not possible.
|
* list approach not possible.
|
||||||
*/
|
*/
|
||||||
export type ProjectionState = Array<LElement|LText|LContainer>;
|
export type ProjectionState = Array<LElement|LText|LContainer>;
|
||||||
|
|
||||||
export interface LProjection extends LNode {
|
|
||||||
readonly native: null;
|
|
||||||
child: null;
|
|
||||||
next: LContainer|LElement|LText|LProjection|null;
|
|
||||||
|
|
||||||
readonly data: ProjectionState;
|
/**
|
||||||
|
* Used for tracking queries (e.g. ViewChild, ContentChild).
|
||||||
|
*/
|
||||||
|
export interface QueryState {
|
||||||
|
/**
|
||||||
|
* Used to ask query if it should be cloned to the child element.
|
||||||
|
*
|
||||||
|
* For example in the case of deep queries the `child()` returns
|
||||||
|
* query for the child node. In case of shallow queries it returns
|
||||||
|
* `null`.
|
||||||
|
*/
|
||||||
|
child(): QueryState|null;
|
||||||
|
|
||||||
/** Projections can be added to elements or views. */
|
/**
|
||||||
readonly parent: LElement|LView;
|
* Notify `QueryState` that a `LNode` has been created.
|
||||||
|
*/
|
||||||
|
addNode(node: LNode): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify `QueryState` that a `LView` has been added to `LContainer`.
|
||||||
|
*/
|
||||||
|
insertView(container: LContainer, view: LView, insertIndex: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify `QueryState` that a `LView` has been removed from `LContainer`.
|
||||||
|
*/
|
||||||
|
removeView(container: LContainer, view: LView, removeIndex: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add additional `QueryList` to track.
|
||||||
|
*
|
||||||
|
* @param queryList `QueryList` to update with changes.
|
||||||
|
* @param predicate Either `Type` or selector array of [key, value] predicates.
|
||||||
|
* @param descend If true the query will recursively apply to the children.
|
||||||
|
*/
|
||||||
|
track<T>(queryList: QueryList<T>, predicate: Type<T>|any[], descend?: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -603,16 +267,16 @@ export interface LProjection extends LNode {
|
|||||||
* - class names in a selector are at the end of an array (after the attribute with the name
|
* - class names in a selector are at the end of an array (after the attribute with the name
|
||||||
* 'class').
|
* 'class').
|
||||||
*/
|
*/
|
||||||
export type SimpleCSSSelector = string[];
|
export type SimpleCssSelector = string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A complex selector expressed as an Array where:
|
* A complex selector expressed as an Array where:
|
||||||
* - element at index 0 is a selector (SimpleCSSSelector) to match
|
* - element at index 0 is a selector (SimpleCSSSelector) to match
|
||||||
* - elements at index 1..n is a selector (SimpleCSSSelector) that should NOT match
|
* - elements at index 1..n is a selector (SimpleCSSSelector) that should NOT match
|
||||||
*/
|
*/
|
||||||
export type CSSSelectorWithNegations = [SimpleCSSSelector | null, SimpleCSSSelector[] | null];
|
export type CssSelectorWithNegations = [SimpleCssSelector | null, SimpleCssSelector[] | null];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of complex selectors (CSSSelectorWithNegations) in a parsed form
|
* A collection of complex selectors (CSSSelectorWithNegations) in a parsed form
|
||||||
*/
|
*/
|
||||||
export type CSSSelector = CSSSelectorWithNegations[];
|
export type CssSelector = CssSelectorWithNegations[];
|
||||||
|
268
packages/core/src/render3/l_node.ts
Normal file
268
packages/core/src/render3/l_node.ts
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
/**
|
||||||
|
* @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 {ElementRef, Injector, TemplateRef, ViewContainerRef} from '../core';
|
||||||
|
import {RComment, RElement, RText} from './renderer';
|
||||||
|
import {ViewState, ContainerState, ProjectionState, QueryState} from './interfaces';
|
||||||
|
import {LNodeStatic} from './l_node_static';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LNodeFlags corresponds to the LNode.flags property. It contains information
|
||||||
|
* on how to map a particular set of bits in LNode.flags to the node type, directive
|
||||||
|
* count, or directive starting index.
|
||||||
|
*
|
||||||
|
* For example, if you wanted to check the type of a certain node, you would mask
|
||||||
|
* node.flags with TYPE_MASK and compare it to the value for a certain node type. e.g:
|
||||||
|
*
|
||||||
|
*```ts
|
||||||
|
* if ((node.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Element) {...}
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
export const enum LNodeFlags {
|
||||||
|
Container = 0b00,
|
||||||
|
Projection = 0b01,
|
||||||
|
View = 0b10,
|
||||||
|
Element = 0b11,
|
||||||
|
ViewOrElement = 0b10,
|
||||||
|
SIZE_SKIP = 0b100,
|
||||||
|
SIZE_SHIFT = 2,
|
||||||
|
INDX_SHIFT = 12,
|
||||||
|
TYPE_MASK = 0b00000000000000000000000000000011,
|
||||||
|
SIZE_MASK = 0b00000000000000000000111111111100,
|
||||||
|
INDX_MASK = 0b11111111111111111111000000000000
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LNode is an internal data structure which is used for the incremental DOM algorithm.
|
||||||
|
* The "L" stands for "Logical" to differentiate between `RNodes` (actual rendered DOM
|
||||||
|
* node) and our logical representation of DOM nodes, `LNodes`.
|
||||||
|
*
|
||||||
|
* The data structure is optimized for speed and size.
|
||||||
|
*
|
||||||
|
* In order to be fast, all subtypes of `LNode` should have the same shape.
|
||||||
|
* Because size of the `LNode` matters, many fields have multiple roles depending
|
||||||
|
* on the `LNode` subtype.
|
||||||
|
*
|
||||||
|
* See: https://en.wikipedia.org/wiki/Inline_caching#Monomorphic_inline_caching
|
||||||
|
*
|
||||||
|
* NOTE: This is a private data structure and should not be exported by any of the
|
||||||
|
* instructions.
|
||||||
|
*/
|
||||||
|
export interface LNode {
|
||||||
|
/**
|
||||||
|
* This number stores three values using its bits:
|
||||||
|
*
|
||||||
|
* - the type of the node (first 2 bits)
|
||||||
|
* - the number of directives on that node (next 10 bits)
|
||||||
|
* - the starting index of the node's directives in the directives array (last 20 bits).
|
||||||
|
*
|
||||||
|
* The latter two values are necessary so DI can effectively search the directives associated
|
||||||
|
* with a node without searching the whole directives array.
|
||||||
|
*/
|
||||||
|
flags: LNodeFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The associated DOM node. Storing this allows us to:
|
||||||
|
* - append children to their element parents in the DOM (e.g. `parent.native.appendChild(...)`)
|
||||||
|
* - retrieve the sibling elements of text nodes whose creation / insertion has been delayed
|
||||||
|
* - mark locations where child views should be inserted (for containers)
|
||||||
|
*/
|
||||||
|
readonly native: RElement|RText|RComment|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need a reference to a node's parent so we can append the node to its parent's native
|
||||||
|
* element at the appropriate time.
|
||||||
|
*/
|
||||||
|
readonly parent: LNode|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First child of the current node.
|
||||||
|
*/
|
||||||
|
child: LNode|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The next sibling node. Necessary so we can propagate through the root nodes of a view
|
||||||
|
* to insert them or remove them from the DOM.
|
||||||
|
*/
|
||||||
|
next: LNode|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If regular LElement, then `data` will be null.
|
||||||
|
* If LElement with component, then `data` contains ViewState.
|
||||||
|
* If LView, then `data` contains the ViewState.
|
||||||
|
* If LContainer, then `data` contains ContainerState.
|
||||||
|
* If LProjection, then `data` contains ProjectionState.
|
||||||
|
*/
|
||||||
|
readonly data: ViewState|ContainerState|ProjectionState|null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each node belongs to a view.
|
||||||
|
*
|
||||||
|
* When the injector is walking up a tree, it needs access to the `directives` (part of view).
|
||||||
|
*/
|
||||||
|
readonly view: ViewState;
|
||||||
|
|
||||||
|
/** The injector associated with this node. Necessary for DI. */
|
||||||
|
nodeInjector: LNodeInjector|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional `QueryState` used for tracking queries.
|
||||||
|
*
|
||||||
|
* If present the node creation/updates are reported to the `QueryState`.
|
||||||
|
*/
|
||||||
|
query: QueryState|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to the corresponding LNodeStatic object, which stores static
|
||||||
|
* data about this node.
|
||||||
|
*/
|
||||||
|
staticData: LNodeStatic|null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** LNode representing an element. */
|
||||||
|
export interface LElement extends LNode {
|
||||||
|
/** The DOM element associated with this node. */
|
||||||
|
readonly native: RElement;
|
||||||
|
|
||||||
|
child: LContainer|LElement|LText|LProjection|null;
|
||||||
|
next: LContainer|LElement|LText|LProjection|null;
|
||||||
|
|
||||||
|
/** If Component than data has ViewState (light DOM) */
|
||||||
|
readonly data: ViewState|null;
|
||||||
|
|
||||||
|
/** LElement nodes can be inside other LElement nodes or inside LViews. */
|
||||||
|
readonly parent: LElement|LView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** LNode representing a #text node. */
|
||||||
|
export interface LText extends LNode {
|
||||||
|
/** The text node associated with this node. */
|
||||||
|
native: RText;
|
||||||
|
child: null;
|
||||||
|
next: LContainer|LElement|LText|LProjection|null;
|
||||||
|
|
||||||
|
/** LText nodes can be inside LElement nodes or inside LViews. */
|
||||||
|
readonly parent: LElement|LView;
|
||||||
|
readonly data: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract node which contains root nodes of a view.
|
||||||
|
*/
|
||||||
|
export interface LView extends LNode {
|
||||||
|
readonly native: null;
|
||||||
|
child: LContainer|LElement|LText|LProjection|null;
|
||||||
|
next: LView|null;
|
||||||
|
|
||||||
|
/** LView nodes can only be added to LContainers. */
|
||||||
|
readonly parent: LContainer|null;
|
||||||
|
readonly data: ViewState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract node container which contains other views.
|
||||||
|
*/
|
||||||
|
export interface LContainer extends LNode {
|
||||||
|
/**
|
||||||
|
* This comment node is appended to the container's parent element to mark where
|
||||||
|
* in the DOM the container's child views should be added.
|
||||||
|
*
|
||||||
|
* If the container is a root node of a view, this comment will not be appended
|
||||||
|
* until the parent view is processed.
|
||||||
|
*/
|
||||||
|
readonly native: RComment;
|
||||||
|
readonly data: ContainerState;
|
||||||
|
child: null;
|
||||||
|
next: LContainer|LElement|LText|LProjection|null;
|
||||||
|
|
||||||
|
/** Containers can be added to elements or views. */
|
||||||
|
readonly parent: LElement|LView|null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface LProjection extends LNode {
|
||||||
|
readonly native: null;
|
||||||
|
child: null;
|
||||||
|
next: LContainer|LElement|LText|LProjection|null;
|
||||||
|
|
||||||
|
readonly data: ProjectionState;
|
||||||
|
|
||||||
|
/** Projections can be added to elements or views. */
|
||||||
|
readonly parent: LElement|LView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTES:
|
||||||
|
*
|
||||||
|
* Each Array costs 70 bytes and is composed of `Array` and `(array)` object
|
||||||
|
* - `Array` javascript visible object: 32 bytes
|
||||||
|
* - `(array)` VM object where the array is actually stored in: 38 bytes
|
||||||
|
*
|
||||||
|
* Each Object cost is 24 bytes plus 8 bytes per property.
|
||||||
|
*
|
||||||
|
* For small arrays, it is more efficient to store the data as a linked list
|
||||||
|
* of items rather than small arrays. However, the array access is faster as
|
||||||
|
* shown here: https://jsperf.com/small-arrays-vs-linked-objects
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface LNodeInjector {
|
||||||
|
/**
|
||||||
|
* We need to store a reference to the injector's parent so DI can keep looking up
|
||||||
|
* the injector tree until it finds the dependency it's looking for.
|
||||||
|
*/
|
||||||
|
readonly parent: LNodeInjector|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows access to the directives array in that node's static data and to
|
||||||
|
* the node's flags (for starting directive index and directive size). Necessary
|
||||||
|
* for DI to retrieve a directive from the data array if injector indicates
|
||||||
|
* it is there.
|
||||||
|
*/
|
||||||
|
readonly node: LElement|LContainer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following bloom filter determines whether a directive is available
|
||||||
|
* on the associated node or not. This prevents us from searching the directives
|
||||||
|
* array at this level unless it's probable the directive is in it.
|
||||||
|
*
|
||||||
|
* - bf0: Check directive IDs 0-31 (IDs are % 128)
|
||||||
|
* - bf1: Check directive IDs 33-63
|
||||||
|
* - bf2: Check directive IDs 64-95
|
||||||
|
* - bf3: Check directive IDs 96-127
|
||||||
|
*
|
||||||
|
* See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
|
||||||
|
*/
|
||||||
|
bf0: number;
|
||||||
|
bf1: number;
|
||||||
|
bf2: number;
|
||||||
|
bf3: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cbf0 - cbf3 properties determine whether a directive is available through a
|
||||||
|
* parent injector. They refer to the merged values of parent bloom filters. This
|
||||||
|
* allows us to skip looking up the chain unless it's probable that directive exists
|
||||||
|
* up the chain.
|
||||||
|
*/
|
||||||
|
cbf0: number;
|
||||||
|
cbf1: number;
|
||||||
|
cbf2: number;
|
||||||
|
cbf3: number;
|
||||||
|
injector: Injector|null;
|
||||||
|
|
||||||
|
/** Stores the TemplateRef so subsequent injections of the TemplateRef get the same instance. */
|
||||||
|
templateRef: TemplateRef<any>|null;
|
||||||
|
|
||||||
|
/** Stores the ViewContainerRef so subsequent injections of the ViewContainerRef get the same
|
||||||
|
* instance. */
|
||||||
|
viewContainerRef: ViewContainerRef|null;
|
||||||
|
|
||||||
|
/** Stores the ElementRef so subsequent injections of the ElementRef get the same instance. */
|
||||||
|
elementRef: ElementRef|null;
|
||||||
|
}
|
132
packages/core/src/render3/l_node_static.ts
Normal file
132
packages/core/src/render3/l_node_static.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* @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 {DirectiveDef} from './public_interfaces';
|
||||||
|
|
||||||
|
/** The type of the global ngStaticData array. */
|
||||||
|
export type NgStaticData = (LNodeStatic | DirectiveDef<any> | null)[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LNode binding data (flywiehgt) for a particular node that is shared between all templates
|
||||||
|
* of a specific type.
|
||||||
|
*
|
||||||
|
* If a property is:
|
||||||
|
* - Minification Data: that property's data was generated and this is it
|
||||||
|
* - Null: that property's data was already generated and nothing was found.
|
||||||
|
* - Undefined: that property's data has not yet been generated
|
||||||
|
*/
|
||||||
|
export interface LNodeStatic {
|
||||||
|
/** The tag name associated with this node. */
|
||||||
|
tagName: string|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static attributes associated with an element. We need to store
|
||||||
|
* static attributes to support content projection with selectors.
|
||||||
|
* Attributes are stored statically because reading them from the DOM
|
||||||
|
* would be way too slow for content projection and queries.
|
||||||
|
*
|
||||||
|
* Since attrs will always be calculated first, they will never need
|
||||||
|
* to be marked undefined by other instructions.
|
||||||
|
*
|
||||||
|
* The name of the attribute and its value alternate in the array.
|
||||||
|
* e.g. ['role', 'checkbox']
|
||||||
|
*/
|
||||||
|
attrs: string[]|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This property contains information about input properties that
|
||||||
|
* need to be set once from attribute data.
|
||||||
|
*/
|
||||||
|
initialInputs: InitialInputData|null|undefined;
|
||||||
|
|
||||||
|
/** Input data for all directives on this node. */
|
||||||
|
inputs: PropertyAliases|null|undefined;
|
||||||
|
|
||||||
|
/** Output data for all directives on this node. */
|
||||||
|
outputs: PropertyAliases|null|undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this LNodeStatic corresponds to an LContainer, the container will
|
||||||
|
* need to have nested static data for each of its embedded views.
|
||||||
|
* Otherwise, nodes in embedded views with the same index as nodes
|
||||||
|
* in their parent views will overwrite each other, as they are in
|
||||||
|
* the same template.
|
||||||
|
*
|
||||||
|
* Each index in this array corresponds to the static data for a certain
|
||||||
|
* view. So if you had V(0) and V(1) in a container, you might have:
|
||||||
|
*
|
||||||
|
* [
|
||||||
|
* [{tagName: 'div', attrs: ...}, null], // V(0) ngData
|
||||||
|
* [{tagName: 'button', attrs ...}, null] // V(1) ngData
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
containerStatic: (LNodeStatic|null)[][]|null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Static data for an LElement */
|
||||||
|
export interface LElementStatic extends LNodeStatic { containerStatic: null; }
|
||||||
|
|
||||||
|
/** Static data for an LContainer */
|
||||||
|
export interface LContainerStatic extends LNodeStatic { containerStatic: (LNodeStatic|null)[][]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This mapping is necessary so we can set input properties and output listeners
|
||||||
|
* properly at runtime when property names are minified or aliased.
|
||||||
|
*
|
||||||
|
* Key: unminified / public input or output name
|
||||||
|
* Value: array containing minified / internal name and related directive index
|
||||||
|
*
|
||||||
|
* The value must be an array to support inputs and outputs with the same name
|
||||||
|
* on the same node.
|
||||||
|
*/
|
||||||
|
export type PropertyAliases = {
|
||||||
|
// This uses an object map because using the Map type would be too slow
|
||||||
|
[key: string]: PropertyAliasValue
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value in PropertyAliases.
|
||||||
|
*
|
||||||
|
* In each array:
|
||||||
|
* Even indices: directive index
|
||||||
|
* Odd indices: minified / internal name
|
||||||
|
*
|
||||||
|
* e.g. [0, 'change-minified']
|
||||||
|
*/
|
||||||
|
export type PropertyAliasValue = (number | string)[];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This array contains information about input properties that
|
||||||
|
* need to be set once from attribute data. It's ordered by
|
||||||
|
* directive index (relative to element) so it's simple to
|
||||||
|
* look up a specific directive's initial input data.
|
||||||
|
*
|
||||||
|
* Within each sub-array:
|
||||||
|
*
|
||||||
|
* Even indices: minified/internal input name
|
||||||
|
* Odd indices: initial value
|
||||||
|
*
|
||||||
|
* If a directive on a node does not have any input properties
|
||||||
|
* that should be set from attributes, its index is set to null
|
||||||
|
* to avoid a sparse array.
|
||||||
|
*
|
||||||
|
* e.g. [null, ['role-min', 'button']]
|
||||||
|
*/
|
||||||
|
export type InitialInputData = (InitialInputs | null)[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by InitialInputData to store input properties
|
||||||
|
* that should be set once from attributes.
|
||||||
|
*
|
||||||
|
* Even indices: minified/internal input name
|
||||||
|
* Odd indices: initial value
|
||||||
|
*
|
||||||
|
* e.g. ['role-min', 'button']
|
||||||
|
*/
|
||||||
|
export type InitialInputs = string[];
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {assertEqual, assertNotEqual} from './assert';
|
import {assertEqual, assertNotEqual} from './assert';
|
||||||
import {LNode, LNodeFlags} from './interfaces';
|
import {LNode, LNodeFlags} from './l_node';
|
||||||
|
|
||||||
export function assertNodeType(node: LNode, type: LNodeFlags) {
|
export function assertNodeType(node: LNode, type: LNodeFlags) {
|
||||||
assertNotEqual(node, null, 'node');
|
assertNotEqual(node, null, 'node');
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {assertNotNull} from './assert';
|
import {assertNotNull} from './assert';
|
||||||
import {ContainerState, LContainer, LElement, LNode, LNodeFlags, LProjection, LText, LView, ProjectionState, ViewOrContainerState, ViewState} from './interfaces';
|
import {ContainerState, ProjectionState, ViewOrContainerState, ViewState} from './interfaces';
|
||||||
|
import {LContainer, LElement, LNode, LNodeFlags, LProjection, LText, LView} from './l_node';
|
||||||
import {assertNodeType} from './node_assert';
|
import {assertNodeType} from './node_assert';
|
||||||
import {RComment, RElement, RNode, RText, Renderer3Fn} from './renderer';
|
import {RComment, RElement, RNode, RText, Renderer3Fn} from './renderer';
|
||||||
|
|
||||||
@ -56,10 +57,10 @@ export function findNativeParent(containerNode: LContainer): RNode|null {
|
|||||||
*/
|
*/
|
||||||
export function findBeforeNode(index: number, state: ContainerState, native: RComment): RElement|
|
export function findBeforeNode(index: number, state: ContainerState, native: RComment): RElement|
|
||||||
RText|RComment {
|
RText|RComment {
|
||||||
const children = state.children;
|
const views = state.views;
|
||||||
// Find the node to insert in front of
|
// Find the node to insert in front of
|
||||||
return index + 1 < children.length ?
|
return index + 1 < views.length ?
|
||||||
(children[index + 1].child as LText | LElement | LContainer).native :
|
(views[index + 1].child as LText | LElement | LContainer).native :
|
||||||
native;
|
native;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +113,7 @@ export function addRemoveViewFromContainer(
|
|||||||
(isFnRenderer ?
|
(isFnRenderer ?
|
||||||
(renderer as Renderer3Fn).removeChild !(parent as RElement, node.native !) :
|
(renderer as Renderer3Fn).removeChild !(parent as RElement, node.native !) :
|
||||||
parent.removeChild(node.native !));
|
parent.removeChild(node.native !));
|
||||||
nextNode = childContainerData.children.length ? childContainerData.children[0].child : null;
|
nextNode = childContainerData.views.length ? childContainerData.views[0].child : null;
|
||||||
} else if (type === LNodeFlags.Projection) {
|
} else if (type === LNodeFlags.Projection) {
|
||||||
nextNode = (node as LProjection).data[0];
|
nextNode = (node as LProjection).data[0];
|
||||||
} else {
|
} else {
|
||||||
@ -150,8 +151,8 @@ export function destroyViewTree(rootView: ViewState): void {
|
|||||||
while (viewOrContainerState) {
|
while (viewOrContainerState) {
|
||||||
let next: ViewOrContainerState|null = null;
|
let next: ViewOrContainerState|null = null;
|
||||||
|
|
||||||
if (viewOrContainerState.children && viewOrContainerState.children.length) {
|
if (viewOrContainerState.views && viewOrContainerState.views.length) {
|
||||||
next = viewOrContainerState.children[0].data;
|
next = viewOrContainerState.views[0].data;
|
||||||
} else if (viewOrContainerState.child) {
|
} else if (viewOrContainerState.child) {
|
||||||
next = viewOrContainerState.child;
|
next = viewOrContainerState.child;
|
||||||
} else if (viewOrContainerState.next) {
|
} else if (viewOrContainerState.next) {
|
||||||
@ -175,7 +176,7 @@ export function destroyViewTree(rootView: ViewState): void {
|
|||||||
/**
|
/**
|
||||||
* Inserts a view into a container.
|
* Inserts a view into a container.
|
||||||
*
|
*
|
||||||
* This adds the view to the container's array of active children in the correct
|
* This adds the view to the container's array of active views in the correct
|
||||||
* position. It also adds the view's elements to the DOM if the container isn't a
|
* position. It also adds the view's elements to the DOM if the container isn't a
|
||||||
* root node of another view (in that case, the view's elements will be added when
|
* root node of another view (in that case, the view's elements will be added when
|
||||||
* the container's parent view is added later).
|
* the container's parent view is added later).
|
||||||
@ -187,19 +188,19 @@ export function destroyViewTree(rootView: ViewState): void {
|
|||||||
*/
|
*/
|
||||||
export function insertView(container: LContainer, newView: LView, index: number): LView {
|
export function insertView(container: LContainer, newView: LView, index: number): LView {
|
||||||
const state = container.data;
|
const state = container.data;
|
||||||
const children = state.children;
|
const views = state.views;
|
||||||
|
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
// This is a new view, we need to add it to the children.
|
// This is a new view, we need to add it to the children.
|
||||||
setViewNext(children[index - 1], newView);
|
setViewNext(views[index - 1], newView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < children.length && children[index].data.id !== newView.data.id) {
|
if (index < views.length && views[index].data.id !== newView.data.id) {
|
||||||
// View ID change replace the view.
|
// View ID change replace the view.
|
||||||
setViewNext(newView, children[index]);
|
setViewNext(newView, views[index]);
|
||||||
children.splice(index, 0, newView);
|
views.splice(index, 0, newView);
|
||||||
} else if (index >= children.length) {
|
} else if (index >= views.length) {
|
||||||
children.push(newView);
|
views.push(newView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.nextIndex <= index) {
|
if (state.nextIndex <= index) {
|
||||||
@ -215,14 +216,14 @@ export function insertView(container: LContainer, newView: LView, index: number)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Notify query that view has been inserted
|
// Notify query that view has been inserted
|
||||||
container.query && container.query.insert(container, newView, index);
|
container.query && container.query.insertView(container, newView, index);
|
||||||
return newView;
|
return newView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a view from a container.
|
* Removes a view from a container.
|
||||||
*
|
*
|
||||||
* This method splices the view from the container's array of active children. It also
|
* This method splices the view from the container's array of active views. It also
|
||||||
* removes the view's elements from the DOM and conducts cleanup (e.g. removing
|
* removes the view's elements from the DOM and conducts cleanup (e.g. removing
|
||||||
* listeners, calling onDestroys).
|
* listeners, calling onDestroys).
|
||||||
*
|
*
|
||||||
@ -231,16 +232,16 @@ export function insertView(container: LContainer, newView: LView, index: number)
|
|||||||
* @returns The removed view
|
* @returns The removed view
|
||||||
*/
|
*/
|
||||||
export function removeView(container: LContainer, removeIndex: number): LView {
|
export function removeView(container: LContainer, removeIndex: number): LView {
|
||||||
const children = container.data.children;
|
const views = container.data.views;
|
||||||
const viewNode = children[removeIndex];
|
const viewNode = views[removeIndex];
|
||||||
if (removeIndex > 0) {
|
if (removeIndex > 0) {
|
||||||
setViewNext(children[removeIndex - 1], viewNode.next);
|
setViewNext(views[removeIndex - 1], viewNode.next);
|
||||||
}
|
}
|
||||||
children.splice(removeIndex, 1);
|
views.splice(removeIndex, 1);
|
||||||
destroyViewTree(viewNode.data);
|
destroyViewTree(viewNode.data);
|
||||||
addRemoveViewFromContainer(container, viewNode, false);
|
addRemoveViewFromContainer(container, viewNode, false);
|
||||||
// Notify query that view has been removed
|
// Notify query that view has been removed
|
||||||
container.query && container.query.remove(container, viewNode, removeIndex);
|
container.query && container.query.removeView(container, viewNode, removeIndex);
|
||||||
return viewNode;
|
return viewNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +402,7 @@ export function processProjectedNode(
|
|||||||
// Assignee the final projection location in those cases.
|
// Assignee the final projection location in those cases.
|
||||||
const containerState = (node as LContainer).data;
|
const containerState = (node as LContainer).data;
|
||||||
containerState.renderParent = currentParent as LElement;
|
containerState.renderParent = currentParent as LElement;
|
||||||
const views = containerState.children;
|
const views = containerState.views;
|
||||||
for (let i = 0; i < views.length; i++) {
|
for (let i = 0; i < views.length; i++) {
|
||||||
addRemoveViewFromContainer(node as LContainer, views[i], true, null);
|
addRemoveViewFromContainer(node as LContainer, views[i], true, null);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
import './ng_dev_mode';
|
import './ng_dev_mode';
|
||||||
|
|
||||||
import {assertNotNull} from './assert';
|
import {assertNotNull} from './assert';
|
||||||
import {CSSSelector, CSSSelectorWithNegations, LNodeStatic, SimpleCSSSelector} from './interfaces';
|
import {CssSelector, CssSelectorWithNegations, SimpleCssSelector} from './interfaces';
|
||||||
|
import {LNodeStatic} from './l_node_static';
|
||||||
|
|
||||||
function isCssClassMatching(nodeClassAttrVal: string, cssClassToMatch: string): boolean {
|
function isCssClassMatching(nodeClassAttrVal: string, cssClassToMatch: string): boolean {
|
||||||
const nodeClassesLen = nodeClassAttrVal.length;
|
const nodeClassesLen = nodeClassAttrVal.length;
|
||||||
@ -29,11 +30,11 @@ function isCssClassMatching(nodeClassAttrVal: string, cssClassToMatch: string):
|
|||||||
* A utility function to match an Ivy node static data against a simple CSS selector
|
* A utility function to match an Ivy node static data against a simple CSS selector
|
||||||
*
|
*
|
||||||
* @param {LNodeStatic} node static data to match
|
* @param {LNodeStatic} node static data to match
|
||||||
* @param {SimpleCSSSelector} selector
|
* @param {SimpleCssSelector} selector
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
export function isNodeMatchingSimpleSelector(
|
export function isNodeMatchingSimpleSelector(
|
||||||
lNodeStaticData: LNodeStatic, selector: SimpleCSSSelector): boolean {
|
lNodeStaticData: LNodeStatic, selector: SimpleCssSelector): boolean {
|
||||||
const noOfSelectorParts = selector.length;
|
const noOfSelectorParts = selector.length;
|
||||||
ngDevMode && assertNotNull(selector[0], 'selector[0]');
|
ngDevMode && assertNotNull(selector[0], 'selector[0]');
|
||||||
const tagNameInSelector = selector[0];
|
const tagNameInSelector = selector[0];
|
||||||
@ -83,7 +84,7 @@ export function isNodeMatchingSimpleSelector(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isNodeMatchingSelectorWithNegations(
|
export function isNodeMatchingSelectorWithNegations(
|
||||||
lNodeStaticData: LNodeStatic, selector: CSSSelectorWithNegations): boolean {
|
lNodeStaticData: LNodeStatic, selector: CssSelectorWithNegations): boolean {
|
||||||
const positiveSelector = selector[0];
|
const positiveSelector = selector[0];
|
||||||
if (positiveSelector != null &&
|
if (positiveSelector != null &&
|
||||||
!isNodeMatchingSimpleSelector(lNodeStaticData, positiveSelector)) {
|
!isNodeMatchingSimpleSelector(lNodeStaticData, positiveSelector)) {
|
||||||
@ -105,7 +106,7 @@ export function isNodeMatchingSelectorWithNegations(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isNodeMatchingSelector(
|
export function isNodeMatchingSelector(
|
||||||
lNodeStaticData: LNodeStatic, selector: CSSSelector): boolean {
|
lNodeStaticData: LNodeStatic, selector: CssSelector): boolean {
|
||||||
for (let i = 0; i < selector.length; i++) {
|
for (let i = 0; i < selector.length; i++) {
|
||||||
if (isNodeMatchingSelectorWithNegations(lNodeStaticData, selector[i])) {
|
if (isNodeMatchingSelectorWithNegations(lNodeStaticData, selector[i])) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -6,11 +6,12 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as viewEngine from '../core';
|
||||||
import {Observable} from 'rxjs/Observable';
|
import {Observable} from 'rxjs/Observable';
|
||||||
import {QueryList as IQueryList, Type} from '../core';
|
|
||||||
import {assertNotNull} from './assert';
|
import {assertNotNull} from './assert';
|
||||||
import {LContainer, LNode, LNodeFlags, LView, QueryState} from './interfaces';
|
import {QueryState} from './interfaces';
|
||||||
import {DirectiveDef} from '@angular/core/src/render3/public_interfaces';
|
import {DirectiveDef} from '@angular/core/src/render3/public_interfaces';
|
||||||
|
import {LContainer, LNode, LNodeFlags, LView} from './l_node';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ export interface QueryPredicate<T> {
|
|||||||
/**
|
/**
|
||||||
* If looking for directives than it contains the directive type.
|
* If looking for directives than it contains the directive type.
|
||||||
*/
|
*/
|
||||||
type: Type<T>|null;
|
type: viewEngine.Type<T>|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If selector then contains the selector parts where:
|
* If selector then contains the selector parts where:
|
||||||
@ -59,7 +60,7 @@ export class QueryState_ implements QueryState {
|
|||||||
|
|
||||||
constructor(deep?: QueryPredicate<any>) { this.deep = deep == null ? null : deep; }
|
constructor(deep?: QueryPredicate<any>) { this.deep = deep == null ? null : deep; }
|
||||||
|
|
||||||
track<T>(queryList: IQueryList<T>, predicate: Type<T>|any[], descend?: boolean): void {
|
track<T>(queryList: viewEngine.QueryList<T>, predicate: viewEngine.Type<T>|any[], descend?: boolean): void {
|
||||||
// TODO(misko): This is not right. In case of inherited state, a calling track will incorrectly
|
// TODO(misko): This is not right. In case of inherited state, a calling track will incorrectly
|
||||||
// mutate parent.
|
// mutate parent.
|
||||||
if (descend) {
|
if (descend) {
|
||||||
@ -84,16 +85,16 @@ export class QueryState_ implements QueryState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add(node: LNode): void {
|
addNode(node: LNode): void {
|
||||||
add(this.shallow, node);
|
add(this.shallow, node);
|
||||||
add(this.deep, node);
|
add(this.deep, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(container: LContainer, view: LView, index: number): void {
|
insertView(container: LContainer, view: LView, index: number): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(container: LContainer, view: LView, index: number): void {
|
removeView(container: LContainer, view: LView, index: number): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,7 +120,7 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
|||||||
|
|
||||||
function createPredicate<T>(
|
function createPredicate<T>(
|
||||||
previous: QueryPredicate<any>| null, queryList: QueryList<T>,
|
previous: QueryPredicate<any>| null, queryList: QueryList<T>,
|
||||||
predicate: Type<T>| any[]): QueryPredicate<T> {
|
predicate: viewEngine.Type<T>| any[]): QueryPredicate<T> {
|
||||||
const isArray = Array.isArray(predicate);
|
const isArray = Array.isArray(predicate);
|
||||||
const values = <any>[];
|
const values = <any>[];
|
||||||
if ((queryList as any as QueryList_<T>)._valuesTree === null) {
|
if ((queryList as any as QueryList_<T>)._valuesTree === null) {
|
||||||
@ -128,13 +129,13 @@ function createPredicate<T>(
|
|||||||
return {
|
return {
|
||||||
next: previous,
|
next: previous,
|
||||||
list: queryList,
|
list: queryList,
|
||||||
type: isArray ? null : predicate as Type<T>,
|
type: isArray ? null : predicate as viewEngine.Type<T>,
|
||||||
selector: isArray ? predicate as any[] : null,
|
selector: isArray ? predicate as any[] : null,
|
||||||
values: values
|
values: values
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryList_<T>/* implements IQueryList<T> */ {
|
class QueryList_<T>/* implements viewEngine.QueryList<T> */ {
|
||||||
dirty: boolean = false;
|
dirty: boolean = false;
|
||||||
changes: Observable<T>;
|
changes: Observable<T>;
|
||||||
|
|
||||||
@ -201,8 +202,8 @@ class QueryList_<T>/* implements IQueryList<T> */ {
|
|||||||
|
|
||||||
// NOTE: this hack is here because IQueryList has private members and therefore
|
// NOTE: this hack is here because IQueryList has private members and therefore
|
||||||
// it can't be implemented only extended.
|
// it can't be implemented only extended.
|
||||||
export type QueryList<T> = IQueryList<T>;
|
export type QueryList<T> = viewEngine.QueryList<T>;
|
||||||
export const QueryList: typeof IQueryList = QueryList_ as any;
|
export const QueryList: typeof viewEngine.QueryList = QueryList_ as any;
|
||||||
|
|
||||||
export function refreshQuery(query: QueryList<any>): boolean {
|
export function refreshQuery(query: QueryList<any>): boolean {
|
||||||
return (query as any as QueryList_<any>)._refresh();
|
return (query as any as QueryList_<any>)._refresh();
|
||||||
|
@ -11,7 +11,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
|||||||
import {bloomFindPossibleInjector} from '../../src/render3/di';
|
import {bloomFindPossibleInjector} from '../../src/render3/di';
|
||||||
import {C, D, E, PublicFeature, T, V, b, b2, c, defineDirective, e, inject, injectElementRef, injectTemplateRef, injectViewContainerRef, rC, rc, t, v} from '../../src/render3/index';
|
import {C, D, E, PublicFeature, T, V, b, b2, c, defineDirective, e, inject, injectElementRef, injectTemplateRef, injectViewContainerRef, rC, rc, t, v} from '../../src/render3/index';
|
||||||
import {bloomAdd, createLNode, createViewState, enterView, getOrCreateNodeInjector, leaveView} from '../../src/render3/instructions';
|
import {bloomAdd, createLNode, createViewState, enterView, getOrCreateNodeInjector, leaveView} from '../../src/render3/instructions';
|
||||||
import {LNodeFlags, LNodeInjector} from '../../src/render3/interfaces';
|
import {LNodeFlags, LNodeInjector} from '../../src/render3/l_node';
|
||||||
|
|
||||||
import {renderToHtml} from './render_util';
|
import {renderToHtml} from './render_util';
|
||||||
|
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CSSSelector, CSSSelectorWithNegations, LNodeStatic, SimpleCSSSelector} from '../../src/render3/interfaces';
|
import {CssSelector, CssSelectorWithNegations, SimpleCssSelector} from '../../src/render3/interfaces';
|
||||||
|
import {LNodeStatic} from '../../src/render3/l_node_static';
|
||||||
import {isNodeMatchingSelector, isNodeMatchingSelectorWithNegations, isNodeMatchingSimpleSelector} from '../../src/render3/node_selector_matcher';
|
import {isNodeMatchingSelector, isNodeMatchingSelectorWithNegations, isNodeMatchingSimpleSelector} from '../../src/render3/node_selector_matcher';
|
||||||
|
|
||||||
function testLStaticData(tagName: string, attrs: string[] | null): LNodeStatic {
|
function testLStaticData(tagName: string, attrs: string[] | null): LNodeStatic {
|
||||||
@ -25,7 +26,7 @@ describe('css selector matching', () => {
|
|||||||
describe('isNodeMatchingSimpleSelector', () => {
|
describe('isNodeMatchingSimpleSelector', () => {
|
||||||
|
|
||||||
function isMatching(
|
function isMatching(
|
||||||
tagName: string, attrs: string[] | null, selector: SimpleCSSSelector): boolean {
|
tagName: string, attrs: string[] | null, selector: SimpleCssSelector): boolean {
|
||||||
return isNodeMatchingSimpleSelector(testLStaticData(tagName, attrs), selector);
|
return isNodeMatchingSimpleSelector(testLStaticData(tagName, attrs), selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +140,7 @@ describe('css selector matching', () => {
|
|||||||
|
|
||||||
describe('isNodeMatchingSelectorWithNegations', () => {
|
describe('isNodeMatchingSelectorWithNegations', () => {
|
||||||
function isMatching(
|
function isMatching(
|
||||||
tagName: string, attrs: string[] | null, selector: CSSSelectorWithNegations): boolean {
|
tagName: string, attrs: string[] | null, selector: CssSelectorWithNegations): boolean {
|
||||||
return isNodeMatchingSelectorWithNegations(testLStaticData(tagName, attrs), selector);
|
return isNodeMatchingSelectorWithNegations(testLStaticData(tagName, attrs), selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ describe('css selector matching', () => {
|
|||||||
|
|
||||||
describe('isNodeMatchingSelector', () => {
|
describe('isNodeMatchingSelector', () => {
|
||||||
|
|
||||||
function isMatching(tagName: string, attrs: string[] | null, selector: CSSSelector): boolean {
|
function isMatching(tagName: string, attrs: string[] | null, selector: CssSelector): boolean {
|
||||||
return isNodeMatchingSelector(testLStaticData(tagName, attrs), selector);
|
return isNodeMatchingSelector(testLStaticData(tagName, attrs), selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {ComponentTemplate, ComponentType, PublicFeature, defineComponent, renderComponent as _renderComponent} from '../../src/render3/index';
|
import {ComponentTemplate, ComponentType, PublicFeature, defineComponent, renderComponent as _renderComponent} from '../../src/render3/index';
|
||||||
import {NG_HOST_SYMBOL, createLNode, createViewState, renderTemplate} from '../../src/render3/instructions';
|
import {NG_HOST_SYMBOL, createLNode, createViewState, renderTemplate} from '../../src/render3/instructions';
|
||||||
import {LElement, LNodeFlags} from '../../src/render3/interfaces';
|
import {LElement, LNodeFlags} from '../../src/render3/l_node';
|
||||||
import {RElement, RText, Renderer3} from '../../src/render3/renderer';
|
import {RElement, RText, Renderer3} from '../../src/render3/renderer';
|
||||||
import {getRenderer2} from './imported_renderer2';
|
import {getRenderer2} from './imported_renderer2';
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user