refactor(core): have different data types for each node. (#14120)
Also have a new node type for queries. This leads to less memory usage and better performance. Deep Tree Benchmark results (depth 11): - createAndDestroy (view engine vs current codegen): * pureScriptTime: 78.80+-4% vs 72.34+-4% * scriptTime: 78.80+-4% vs 90.71+-9% * gc: 5371.66+-108% vs 9717.53+-174% * i.e. faster when gc is also considered and about 2x less memory usage! - update unchanged Part of #14013 PR Close #14120
This commit is contained in:
parent
7ad616a177
commit
f802194c18
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {SecurityContext} from '../security';
|
||||
|
||||
import {BindingDef, BindingType, DisposableFn, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, ViewData, ViewDefinition, ViewFlags} from './types';
|
||||
import {BindingDef, BindingType, DisposableFn, ElementData, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, ViewData, ViewDefinition, ViewFlags, asElementData} from './types';
|
||||
import {checkAndUpdateBinding, setBindingDebugInfo} from './util';
|
||||
|
||||
export function anchorDef(
|
||||
|
@ -28,16 +28,22 @@ export function anchorDef(
|
|||
childMatchedQueries: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
providerIndices: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
matchedQueries: matchedQueryDefs, childCount,
|
||||
bindings: [],
|
||||
disposableCount: 0,
|
||||
element: {name: undefined, attrs: undefined, outputs: [], template},
|
||||
element: {
|
||||
name: undefined,
|
||||
attrs: undefined,
|
||||
outputs: [], template,
|
||||
// will bet set by the view definition
|
||||
providerIndices: undefined,
|
||||
},
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
pureExpression: undefined
|
||||
pureExpression: undefined,
|
||||
query: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -95,21 +101,29 @@ export function elementDef(
|
|||
childMatchedQueries: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
providerIndices: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
matchedQueries: matchedQueryDefs, childCount,
|
||||
bindings: bindingDefs,
|
||||
disposableCount: outputDefs.length,
|
||||
element: {name, attrs: fixedAttrs, outputs: outputDefs, template: undefined},
|
||||
element: {
|
||||
name,
|
||||
attrs: fixedAttrs,
|
||||
outputs: outputDefs,
|
||||
template: undefined,
|
||||
// will bet set by the view definition
|
||||
providerIndices: undefined,
|
||||
},
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
pureExpression: undefined
|
||||
pureExpression: undefined,
|
||||
query: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function createElement(view: ViewData, renderHost: any, def: NodeDef): NodeData {
|
||||
const parentNode = def.parent != null ? view.nodes[def.parent].elementOrText.node : renderHost;
|
||||
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
|
||||
const parentNode =
|
||||
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
|
||||
const elDef = def.element;
|
||||
let el: any;
|
||||
if (view.renderer) {
|
||||
|
@ -162,13 +176,9 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): No
|
|||
}
|
||||
}
|
||||
return {
|
||||
elementOrText: {
|
||||
node: el,
|
||||
renderElement: el,
|
||||
embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined,
|
||||
projectedViews: undefined
|
||||
},
|
||||
provider: undefined,
|
||||
pureExpression: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -228,7 +238,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
|||
|
||||
const binding = def.bindings[bindingIdx];
|
||||
const name = binding.name;
|
||||
const renderNode = view.nodes[def.index].elementOrText.node;
|
||||
const renderNode = asElementData(view, def.index).renderElement;
|
||||
switch (binding.type) {
|
||||
case BindingType.ElementAttribute:
|
||||
setElementAttribute(view, binding, renderNode, name, value);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
export {anchorDef, elementDef} from './element';
|
||||
export {providerDef} from './provider';
|
||||
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
||||
export {queryDef} from './query';
|
||||
export {textDef} from './text';
|
||||
export {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, viewDef} from './view';
|
||||
export {attachEmbeddedView, detachEmbeddedView, rootRenderNodes} from './view_attach';
|
||||
|
|
|
@ -10,14 +10,13 @@ import {SimpleChange, SimpleChanges} from '../change_detection/change_detection'
|
|||
import {Injector} from '../di';
|
||||
import {stringify} from '../facade/lang';
|
||||
import {ElementRef} from '../linker/element_ref';
|
||||
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
|
||||
import {QueryList} from '../linker/query_list';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {Renderer} from '../render/api';
|
||||
import {queryDef} from './query';
|
||||
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderOutputDef, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags} from './types';
|
||||
import {checkAndUpdateBinding, checkAndUpdateBindingWithChange, declaredViewContainer, setBindingDebugInfo} from './util';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderOutputDef, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, asElementData, asProviderData} from './types';
|
||||
import {checkAndUpdateBinding, checkAndUpdateBindingWithChange, setBindingDebugInfo} from './util';
|
||||
|
||||
const _tokenKeyCache = new Map<any, string>();
|
||||
|
||||
|
@ -27,11 +26,9 @@ const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
|
|||
const TemplateRefTokenKey = tokenKey(TemplateRef);
|
||||
|
||||
export function providerDef(
|
||||
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ctor: any,
|
||||
flags: NodeFlags, matchedQueries: [string, QueryValueType][], childCount: number, ctor: any,
|
||||
deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
||||
outputs?: {[name: string]: string},
|
||||
contentQueries?: {[name: string]: [string, QueryBindingType]}, component?: () => ViewDefinition,
|
||||
viewQueries?: {[name: string]: [string, QueryBindingType]}, ): NodeDef {
|
||||
outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef {
|
||||
const matchedQueryDefs: {[queryId: string]: QueryValueType} = {};
|
||||
if (matchedQueries) {
|
||||
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
|
||||
|
@ -66,26 +63,9 @@ export function providerDef(
|
|||
}
|
||||
return {flags, token, tokenKey: tokenKey(token)};
|
||||
});
|
||||
const contentQueryDefs: QueryDef[] = [];
|
||||
for (let propName in contentQueries) {
|
||||
const [id, bindingType] = contentQueries[propName];
|
||||
contentQueryDefs.push({id, propName, bindingType});
|
||||
}
|
||||
const viewQueryDefs: QueryDef[] = [];
|
||||
for (let propName in viewQueries) {
|
||||
const [id, bindingType] = viewQueries[propName];
|
||||
viewQueryDefs.push({id, propName, bindingType});
|
||||
}
|
||||
|
||||
if (component) {
|
||||
flags = flags | NodeFlags.HasComponent;
|
||||
}
|
||||
if (contentQueryDefs.length) {
|
||||
flags = flags | NodeFlags.HasContentQuery;
|
||||
}
|
||||
if (viewQueryDefs.length) {
|
||||
flags = flags | NodeFlags.HasViewQuery;
|
||||
}
|
||||
|
||||
return {
|
||||
type: NodeType.Provider,
|
||||
|
@ -97,23 +77,15 @@ export function providerDef(
|
|||
childMatchedQueries: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
providerIndices: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
matchedQueries: matchedQueryDefs,
|
||||
childCount: 0, bindings,
|
||||
matchedQueries: matchedQueryDefs, childCount, bindings,
|
||||
disposableCount: outputDefs.length,
|
||||
element: undefined,
|
||||
provider: {
|
||||
tokenKey: tokenKey(ctor),
|
||||
ctor,
|
||||
deps: depDefs,
|
||||
outputs: outputDefs,
|
||||
contentQueries: contentQueryDefs,
|
||||
viewQueries: viewQueryDefs, component
|
||||
},
|
||||
provider: {tokenKey: tokenKey(ctor), ctor, deps: depDefs, outputs: outputDefs, component},
|
||||
text: undefined,
|
||||
pureExpression: undefined,
|
||||
query: undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -126,7 +98,8 @@ export function tokenKey(token: any): string {
|
|||
return key;
|
||||
}
|
||||
|
||||
export function createProvider(view: ViewData, def: NodeDef, componentView: ViewData): NodeData {
|
||||
export function createProvider(
|
||||
view: ViewData, def: NodeDef, componentView: ViewData): ProviderData {
|
||||
const providerDef = def.provider;
|
||||
const provider = createInstance(view, def.parent, providerDef.ctor, providerDef.deps);
|
||||
if (providerDef.outputs.length) {
|
||||
|
@ -137,29 +110,13 @@ export function createProvider(view: ViewData, def: NodeDef, componentView: View
|
|||
view.disposables[def.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
|
||||
}
|
||||
}
|
||||
let queries: {[queryId: string]: QueryList<any>};
|
||||
if (providerDef.contentQueries.length || providerDef.viewQueries.length) {
|
||||
queries = {};
|
||||
for (let i = 0; i < providerDef.contentQueries.length; i++) {
|
||||
const def = providerDef.contentQueries[i];
|
||||
queries[def.id] = new QueryList<any>();
|
||||
}
|
||||
for (let i = 0; i < providerDef.viewQueries.length; i++) {
|
||||
const def = providerDef.viewQueries[i];
|
||||
queries[def.id] = new QueryList<any>();
|
||||
}
|
||||
}
|
||||
return {
|
||||
elementOrText: undefined,
|
||||
provider: {instance: provider, componentView: componentView, queries},
|
||||
pureExpression: undefined,
|
||||
};
|
||||
return {instance: provider, componentView: componentView};
|
||||
}
|
||||
|
||||
export function checkAndUpdateProviderInline(
|
||||
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
|
||||
v7: any, v8: any, v9: any) {
|
||||
const provider = view.nodes[def.index].provider.instance;
|
||||
const provider = asProviderData(view, def.index).instance;
|
||||
let changes: SimpleChanges;
|
||||
// Note: fallthrough is intended!
|
||||
switch (def.bindings.length) {
|
||||
|
@ -196,7 +153,7 @@ export function checkAndUpdateProviderInline(
|
|||
}
|
||||
|
||||
export function checkAndUpdateProviderDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
||||
const provider = view.nodes[def.index].provider.instance;
|
||||
const provider = asProviderData(view, def.index).instance;
|
||||
let changes: SimpleChanges;
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
changes = checkAndUpdateProp(view, provider, def, i, values[i], changes);
|
||||
|
@ -265,15 +222,15 @@ export function resolveDep(
|
|||
return Injector.NULL.get(depDef.token, notFoundValue);
|
||||
}
|
||||
case ElementRefTokenKey:
|
||||
return new ElementRef(view.nodes[elIndex].elementOrText.node);
|
||||
return new ElementRef(asElementData(view, elIndex).renderElement);
|
||||
case ViewContainerRefTokenKey:
|
||||
return view.services.createViewContainerRef(view.nodes[elIndex]);
|
||||
return view.services.createViewContainerRef(asElementData(view, elIndex));
|
||||
case TemplateRefTokenKey:
|
||||
return view.services.createTemplateRef(view, elDef);
|
||||
default:
|
||||
const providerIndex = elDef.providerIndices[tokenKey];
|
||||
const providerIndex = elDef.element.providerIndices[tokenKey];
|
||||
if (providerIndex != null) {
|
||||
return view.nodes[providerIndex].provider.instance;
|
||||
return asProviderData(view, providerIndex).instance;
|
||||
}
|
||||
}
|
||||
elIndex = view.parentDiIndex;
|
||||
|
@ -303,7 +260,8 @@ function checkAndUpdateProp(
|
|||
|
||||
if (view.def.flags & ViewFlags.LogBindingUpdate) {
|
||||
setBindingDebugInfo(
|
||||
view.renderer, view.nodes[def.parent].elementOrText.node, binding.nonMinifiedName, value);
|
||||
view.renderer, asElementData(view, def.parent).renderElement, binding.nonMinifiedName,
|
||||
value);
|
||||
}
|
||||
if (change) {
|
||||
changes = changes || {};
|
||||
|
@ -313,153 +271,6 @@ function checkAndUpdateProp(
|
|||
return changes;
|
||||
}
|
||||
|
||||
export enum QueryAction {
|
||||
CheckNoChanges,
|
||||
CheckAndUpdate,
|
||||
}
|
||||
|
||||
export function execContentQueriesAction(view: ViewData, action: QueryAction) {
|
||||
if (!(view.def.nodeFlags & NodeFlags.HasContentQuery)) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < view.nodes.length; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if (nodeDef.flags & NodeFlags.HasContentQuery) {
|
||||
execContentQuery(view, nodeDef, action);
|
||||
} else if ((nodeDef.childFlags & NodeFlags.HasContentQuery) === 0) {
|
||||
// no child has a content query
|
||||
// then skip the children
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function updateViewQueries(view: ViewData, action: QueryAction) {
|
||||
if (!(view.def.nodeFlags & NodeFlags.HasViewQuery)) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < view.nodes.length; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if (nodeDef.flags & NodeFlags.HasViewQuery) {
|
||||
updateViewQuery(view, nodeDef, action);
|
||||
} else if ((nodeDef.childFlags & NodeFlags.HasViewQuery) === 0) {
|
||||
// no child has a view query
|
||||
// then skip the children
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function execContentQuery(view: ViewData, nodeDef: NodeDef, action: QueryAction) {
|
||||
const providerData = view.nodes[nodeDef.index].provider;
|
||||
for (let i = 0; i < nodeDef.provider.contentQueries.length; i++) {
|
||||
const queryDef = nodeDef.provider.contentQueries[i];
|
||||
const queryId = queryDef.id;
|
||||
const queryList = providerData.queries[queryId];
|
||||
if (queryList.dirty) {
|
||||
const elementDef = view.def.nodes[nodeDef.parent];
|
||||
const newValues = calcQueryValues(
|
||||
view, elementDef.index, elementDef.index + elementDef.childCount, queryId, []);
|
||||
execQueryAction(view, providerData.instance, queryList, queryDef, newValues, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateViewQuery(view: ViewData, nodeDef: NodeDef, action: QueryAction) {
|
||||
for (let i = 0; i < nodeDef.provider.viewQueries.length; i++) {
|
||||
const queryDef = nodeDef.provider.viewQueries[i];
|
||||
const queryId = queryDef.id;
|
||||
const providerData = view.nodes[nodeDef.index].provider;
|
||||
const queryList = providerData.queries[queryId];
|
||||
if (queryList.dirty) {
|
||||
const componentView = providerData.componentView;
|
||||
const newValues =
|
||||
calcQueryValues(componentView, 0, componentView.nodes.length - 1, queryId, []);
|
||||
execQueryAction(view, providerData.instance, queryList, queryDef, newValues, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function execQueryAction(
|
||||
view: ViewData, provider: any, queryList: QueryList<any>, queryDef: QueryDef, newValues: any[],
|
||||
action: QueryAction) {
|
||||
switch (action) {
|
||||
case QueryAction.CheckAndUpdate:
|
||||
queryList.reset(newValues);
|
||||
let boundValue: any;
|
||||
switch (queryDef.bindingType) {
|
||||
case QueryBindingType.First:
|
||||
boundValue = queryList.first;
|
||||
break;
|
||||
case QueryBindingType.All:
|
||||
boundValue = queryList;
|
||||
break;
|
||||
}
|
||||
provider[queryDef.propName] = boundValue;
|
||||
break;
|
||||
case QueryAction.CheckNoChanges:
|
||||
// queries should always be non dirty when we go into checkNoChanges!
|
||||
const oldValuesStr = queryList.toArray().map(v => stringify(v));
|
||||
const newValuesStr = newValues.map(v => stringify(v));
|
||||
throw new ExpressionChangedAfterItHasBeenCheckedError(
|
||||
oldValuesStr, newValuesStr, view.firstChange);
|
||||
}
|
||||
}
|
||||
|
||||
function calcQueryValues(
|
||||
view: ViewData, startIndex: number, endIndex: number, queryId: string, values: any[]): any[] {
|
||||
const len = view.def.nodes.length;
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
const queryValueType = <QueryValueType>nodeDef.matchedQueries[queryId];
|
||||
if (queryValueType != null) {
|
||||
// a match
|
||||
let value: any;
|
||||
switch (queryValueType) {
|
||||
case QueryValueType.ElementRef:
|
||||
value = new ElementRef(view.nodes[i].elementOrText.node);
|
||||
break;
|
||||
case QueryValueType.TemplateRef:
|
||||
value = view.services.createTemplateRef(view, nodeDef);
|
||||
break;
|
||||
case QueryValueType.ViewContainerRef:
|
||||
value = view.services.createViewContainerRef(view.nodes[i]);
|
||||
break;
|
||||
case QueryValueType.Provider:
|
||||
value = view.nodes[i].provider.instance;
|
||||
break;
|
||||
}
|
||||
values.push(value);
|
||||
}
|
||||
if (nodeDef.flags & NodeFlags.HasEmbeddedViews &&
|
||||
queryId in nodeDef.element.template.nodeMatchedQueries) {
|
||||
// check embedded views that were attached at the place of their template.
|
||||
const nodeData = view.nodes[i];
|
||||
const embeddedViews = nodeData.elementOrText.embeddedViews;
|
||||
for (let k = 0; k < embeddedViews.length; k++) {
|
||||
const embeddedView = embeddedViews[k];
|
||||
const dvc = declaredViewContainer(embeddedView);
|
||||
if (dvc && dvc === nodeData) {
|
||||
calcQueryValues(embeddedView, 0, embeddedView.nodes.length - 1, queryId, values);
|
||||
}
|
||||
}
|
||||
const projectedViews = nodeData.elementOrText.projectedViews;
|
||||
if (projectedViews) {
|
||||
for (let k = 0; k < projectedViews.length; k++) {
|
||||
const projectedView = projectedViews[k];
|
||||
calcQueryValues(projectedView, 0, projectedView.nodes.length - 1, queryId, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(queryId in nodeDef.childMatchedQueries)) {
|
||||
// If don't check descendants, skip the children.
|
||||
// Or: no child matches the query, then skip the children as well.
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: NodeFlags) {
|
||||
if (!(view.def.nodeFlags & lifecycles)) {
|
||||
return;
|
||||
|
@ -471,7 +282,7 @@ export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: Node
|
|||
const nodeIndex = nodeDef.index;
|
||||
if (nodeDef.flags & lifecycles) {
|
||||
// a leaf
|
||||
callProviderLifecycles(view.nodes[nodeIndex].provider.instance, nodeDef.flags & lifecycles);
|
||||
callProviderLifecycles(asProviderData(view, nodeIndex).instance, nodeDef.flags & lifecycles);
|
||||
} else if ((nodeDef.childFlags & lifecycles) === 0) {
|
||||
// a parent with leafs
|
||||
// no child matches one of the lifecycles,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {resolveDep, tokenKey} from './provider';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, ProviderData, PureExpressionType, ViewData} from './types';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, ProviderData, PureExpressionData, PureExpressionType, ViewData, asPureExpressionData} from './types';
|
||||
import {checkAndUpdateBinding} from './util';
|
||||
|
||||
export function purePipeDef(pipeToken: any, argCount: number): NodeDef {
|
||||
|
@ -47,7 +47,6 @@ function _pureExpressionDef(
|
|||
childMatchedQueries: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
providerIndices: undefined,
|
||||
// regular values
|
||||
flags: 0,
|
||||
matchedQueries: {},
|
||||
|
@ -56,15 +55,16 @@ function _pureExpressionDef(
|
|||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
pureExpression: {type, pipeDep}
|
||||
pureExpression: {type, pipeDep},
|
||||
query: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function createPureExpression(view: ViewData, def: NodeDef): NodeData {
|
||||
export function createPureExpression(view: ViewData, def: NodeDef): PureExpressionData {
|
||||
const pipe = def.pureExpression.pipeDep ?
|
||||
resolveDep(view, def.parent, def.pureExpression.pipeDep) :
|
||||
undefined;
|
||||
return {elementOrText: undefined, provider: undefined, pureExpression: {value: undefined, pipe}};
|
||||
return {value: undefined, pipe};
|
||||
}
|
||||
|
||||
export function checkAndUpdatePureExpressionInline(
|
||||
|
@ -97,7 +97,7 @@ export function checkAndUpdatePureExpressionInline(
|
|||
}
|
||||
|
||||
if (changed) {
|
||||
const data = view.nodes[def.index].pureExpression;
|
||||
const data = asPureExpressionData(view, def.index);
|
||||
let value: any;
|
||||
switch (def.pureExpression.type) {
|
||||
case PureExpressionType.Array:
|
||||
|
@ -202,7 +202,7 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef
|
|||
}
|
||||
}
|
||||
if (changed) {
|
||||
const data = view.nodes[def.index].pureExpression;
|
||||
const data = asPureExpressionData(view, def.index);
|
||||
let value: any;
|
||||
switch (def.pureExpression.type) {
|
||||
case PureExpressionType.Array:
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/**
|
||||
* @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} from '../linker/element_ref';
|
||||
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
|
||||
import {QueryList} from '../linker/query_list';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
|
||||
import {NodeDef, NodeFlags, NodeType, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, ViewData, asElementData, asProviderData, asQueryList} from './types';
|
||||
import {declaredViewContainer} from './util';
|
||||
|
||||
export function queryDef(
|
||||
flags: NodeFlags, id: string, bindings: {[propName: string]: QueryBindingType}): NodeDef {
|
||||
let bindingDefs: QueryBindingDef[] = [];
|
||||
for (let propName in bindings) {
|
||||
const bindingType = bindings[propName];
|
||||
bindingDefs.push({propName, bindingType});
|
||||
}
|
||||
|
||||
return {
|
||||
type: NodeType.Query,
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
reverseChildIndex: undefined,
|
||||
parent: undefined,
|
||||
childFlags: undefined,
|
||||
childMatchedQueries: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
matchedQueries: {},
|
||||
childCount: 0,
|
||||
bindings: [],
|
||||
disposableCount: 0,
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
pureExpression: undefined,
|
||||
query: {id, bindings: bindingDefs}
|
||||
};
|
||||
}
|
||||
|
||||
export function createQuery(): QueryList<any> {
|
||||
return new QueryList();
|
||||
}
|
||||
|
||||
export function dirtyParentQuery(queryId: string, view: ViewData) {
|
||||
let nodeIndex = view.parentIndex;
|
||||
view = view.parent;
|
||||
let queryIdx: number;
|
||||
while (view) {
|
||||
const elementDef = view.def.nodes[nodeIndex];
|
||||
queryIdx = elementDef.element.providerIndices[queryId];
|
||||
if (queryIdx != null) {
|
||||
break;
|
||||
}
|
||||
nodeIndex = view.parentIndex;
|
||||
view = view.parent;
|
||||
}
|
||||
if (!view) {
|
||||
throw new Error(
|
||||
`Illegal State: Tried to dirty parent query ${queryId} but the query could not be found!`);
|
||||
}
|
||||
asQueryList(view, queryIdx).setDirty();
|
||||
}
|
||||
|
||||
export function checkAndUpdateQuery(view: ViewData, nodeDef: NodeDef) {
|
||||
const queryList = asQueryList(view, nodeDef.index);
|
||||
if (!queryList.dirty) {
|
||||
return;
|
||||
}
|
||||
const queryId = nodeDef.query.id;
|
||||
const providerDef = view.def.nodes[nodeDef.parent];
|
||||
const providerData = asProviderData(view, providerDef.index);
|
||||
let newValues: any[];
|
||||
if (nodeDef.flags & NodeFlags.HasContentQuery) {
|
||||
const elementDef = view.def.nodes[providerDef.parent];
|
||||
newValues = calcQueryValues(
|
||||
view, elementDef.index, elementDef.index + elementDef.childCount, queryId, []);
|
||||
} else if (nodeDef.flags & NodeFlags.HasViewQuery) {
|
||||
const compView = providerData.componentView;
|
||||
newValues = calcQueryValues(compView, 0, compView.def.nodes.length - 1, queryId, []);
|
||||
}
|
||||
queryList.reset(newValues);
|
||||
let boundValue: any;
|
||||
const bindings = nodeDef.query.bindings;
|
||||
for (let i = 0; i < bindings.length; i++) {
|
||||
const binding = bindings[i];
|
||||
switch (binding.bindingType) {
|
||||
case QueryBindingType.First:
|
||||
boundValue = queryList.first;
|
||||
break;
|
||||
case QueryBindingType.All:
|
||||
boundValue = queryList;
|
||||
break;
|
||||
}
|
||||
providerData.instance[binding.propName] = boundValue;
|
||||
}
|
||||
}
|
||||
|
||||
function calcQueryValues(
|
||||
view: ViewData, startIndex: number, endIndex: number, queryId: string, values: any[]): any[] {
|
||||
const len = view.def.nodes.length;
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
const queryValueType = <QueryValueType>nodeDef.matchedQueries[queryId];
|
||||
if (queryValueType != null) {
|
||||
// a match
|
||||
let value: any;
|
||||
switch (queryValueType) {
|
||||
case QueryValueType.ElementRef:
|
||||
value = new ElementRef(asElementData(view, i).renderElement);
|
||||
break;
|
||||
case QueryValueType.TemplateRef:
|
||||
value = view.services.createTemplateRef(view, nodeDef);
|
||||
break;
|
||||
case QueryValueType.ViewContainerRef:
|
||||
value = view.services.createViewContainerRef(asElementData(view, i));
|
||||
break;
|
||||
case QueryValueType.Provider:
|
||||
value = asProviderData(view, i).instance;
|
||||
break;
|
||||
}
|
||||
values.push(value);
|
||||
}
|
||||
if (nodeDef.flags & NodeFlags.HasEmbeddedViews &&
|
||||
queryId in nodeDef.element.template.nodeMatchedQueries) {
|
||||
// check embedded views that were attached at the place of their template.
|
||||
const elementData = asElementData(view, i);
|
||||
const embeddedViews = elementData.embeddedViews;
|
||||
for (let k = 0; k < embeddedViews.length; k++) {
|
||||
const embeddedView = embeddedViews[k];
|
||||
const dvc = declaredViewContainer(embeddedView);
|
||||
if (dvc && dvc === elementData) {
|
||||
calcQueryValues(embeddedView, 0, embeddedView.def.nodes.length - 1, queryId, values);
|
||||
}
|
||||
}
|
||||
const projectedViews = elementData.projectedViews;
|
||||
if (projectedViews) {
|
||||
for (let k = 0; k < projectedViews.length; k++) {
|
||||
const projectedView = projectedViews[k];
|
||||
calcQueryValues(projectedView, 0, projectedView.def.nodes.length - 1, queryId, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(queryId in nodeDef.childMatchedQueries)) {
|
||||
// If don't check descendants, skip the children.
|
||||
// Or: no child matches the query, then skip the children as well.
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
|
@ -16,7 +16,7 @@ import {EmbeddedViewRef, ViewRef} from '../linker/view_ref';
|
|||
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
|
||||
import {Sanitizer, SecurityContext} from '../security';
|
||||
|
||||
import {NodeData, NodeDef, Services, ViewData, ViewDefinition} from './types';
|
||||
import {ElementData, NodeData, NodeDef, Services, ViewData, ViewDefinition, asElementData} from './types';
|
||||
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, destroyView} from './view';
|
||||
import {attachEmbeddedView, detachEmbeddedView, rootRenderNodes} from './view_attach';
|
||||
|
||||
|
@ -31,7 +31,10 @@ export class DefaultServices implements Services {
|
|||
return this._sanitizer.sanitize(context, value);
|
||||
}
|
||||
// Note: This needs to be here to prevent a cycle in source files.
|
||||
createViewContainerRef(data: NodeData): ViewContainerRef { return new ViewContainerRef_(data); }
|
||||
createViewContainerRef(data: ElementData): ViewContainerRef {
|
||||
return new ViewContainerRef_(data);
|
||||
}
|
||||
|
||||
// Note: This needs to be here to prevent a cycle in source files.
|
||||
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> {
|
||||
return new TemplateRef_(parentView, def);
|
||||
|
@ -39,7 +42,7 @@ export class DefaultServices implements Services {
|
|||
}
|
||||
|
||||
class ViewContainerRef_ implements ViewContainerRef {
|
||||
constructor(private _data: NodeData) {}
|
||||
constructor(private _data: ElementData) {}
|
||||
|
||||
get element(): ElementRef { return <ElementRef>unimplemented(); }
|
||||
|
||||
|
@ -48,18 +51,16 @@ class ViewContainerRef_ implements ViewContainerRef {
|
|||
get parentInjector(): Injector { return <Injector>unimplemented(); }
|
||||
|
||||
clear(): void {
|
||||
const len = this._data.elementOrText.embeddedViews.length;
|
||||
const len = this._data.embeddedViews.length;
|
||||
for (let i = len - 1; i >= 0; i--) {
|
||||
const view = detachEmbeddedView(this._data, i);
|
||||
destroyView(view);
|
||||
}
|
||||
}
|
||||
|
||||
get(index: number): ViewRef {
|
||||
return new ViewRef_(this._data.elementOrText.embeddedViews[index]);
|
||||
}
|
||||
get(index: number): ViewRef { return new ViewRef_(this._data.embeddedViews[index]); }
|
||||
|
||||
get length(): number { return this._data.elementOrText.embeddedViews.length; };
|
||||
get length(): number { return this._data.embeddedViews.length; };
|
||||
|
||||
createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number):
|
||||
EmbeddedViewRef<C> {
|
||||
|
@ -83,7 +84,7 @@ class ViewContainerRef_ implements ViewContainerRef {
|
|||
move(viewRef: ViewRef, currentIndex: number): ViewRef { return unimplemented(); }
|
||||
|
||||
indexOf(viewRef: ViewRef): number {
|
||||
return this._data.elementOrText.embeddedViews.indexOf((<ViewRef_>viewRef)._view);
|
||||
return this._data.embeddedViews.indexOf((<ViewRef_>viewRef)._view);
|
||||
}
|
||||
|
||||
remove(index?: number): void {
|
||||
|
@ -128,6 +129,6 @@ class TemplateRef_ implements TemplateRef<any> {
|
|||
}
|
||||
|
||||
get elementRef(): ElementRef {
|
||||
return new ElementRef(this._parentView.nodes[this._def.index].elementOrText.node);
|
||||
return new ElementRef(asElementData(this._parentView, this._def.index).renderElement);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {looseIdentical} from '../facade/lang';
|
||||
|
||||
import {BindingDef, BindingType, NodeData, NodeDef, NodeFlags, NodeType, Services, ViewData} from './types';
|
||||
import {BindingDef, BindingType, NodeData, NodeDef, NodeFlags, NodeType, Services, TextData, ViewData, asElementData, asTextData} from './types';
|
||||
import {checkAndUpdateBinding} from './util';
|
||||
|
||||
export function textDef(constants: string[]): NodeDef {
|
||||
|
@ -32,7 +32,6 @@ export function textDef(constants: string[]): NodeDef {
|
|||
childMatchedQueries: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
providerIndices: undefined,
|
||||
// regular values
|
||||
flags: 0,
|
||||
matchedQueries: {},
|
||||
|
@ -41,12 +40,14 @@ export function textDef(constants: string[]): NodeDef {
|
|||
element: undefined,
|
||||
provider: undefined,
|
||||
text: {prefix: constants[0]},
|
||||
pureExpression: undefined
|
||||
pureExpression: undefined,
|
||||
query: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function createText(view: ViewData, renderHost: any, def: NodeDef): NodeData {
|
||||
const parentNode = def.parent != null ? view.nodes[def.parent].elementOrText.node : renderHost;
|
||||
export function createText(view: ViewData, renderHost: any, def: NodeDef): TextData {
|
||||
const parentNode =
|
||||
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
|
||||
let renderNode: any;
|
||||
if (view.renderer) {
|
||||
renderNode = view.renderer.createText(parentNode, def.text.prefix);
|
||||
|
@ -56,11 +57,7 @@ export function createText(view: ViewData, renderHost: any, def: NodeDef): NodeD
|
|||
parentNode.appendChild(renderNode);
|
||||
}
|
||||
}
|
||||
return {
|
||||
elementOrText: {node: renderNode, embeddedViews: undefined, projectedViews: undefined},
|
||||
provider: undefined,
|
||||
pureExpression: undefined
|
||||
};
|
||||
return {renderText: renderNode};
|
||||
}
|
||||
|
||||
export function checkAndUpdateTextInline(
|
||||
|
@ -118,7 +115,7 @@ export function checkAndUpdateTextInline(
|
|||
value = _addInterpolationPart(v0, bindings[0]) + value;
|
||||
}
|
||||
value = def.text.prefix + value;
|
||||
const renderNode = view.nodes[def.index].elementOrText.node;
|
||||
const renderNode = asTextData(view, def.index).renderText;
|
||||
if (view.renderer) {
|
||||
view.renderer.setText(renderNode, value);
|
||||
} else {
|
||||
|
@ -143,7 +140,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
|
|||
value = value + _addInterpolationPart(values[i], bindings[i]);
|
||||
}
|
||||
value = def.text.prefix + value;
|
||||
const renderNode = view.nodes[def.index].elementOrText.node;
|
||||
const renderNode = asTextData(view, def.index).renderText;
|
||||
if (view.renderer) {
|
||||
view.renderer.setText(renderNode, value);
|
||||
} else {
|
||||
|
|
|
@ -34,7 +34,7 @@ export interface ViewDefinition {
|
|||
* Especially providers are after elements / anchros.
|
||||
*/
|
||||
reverseChildNodes: NodeDef[];
|
||||
lastRootNode: number;
|
||||
lastRootNode: NodeDef;
|
||||
bindingCount: number;
|
||||
disposableCount: number;
|
||||
/**
|
||||
|
@ -65,6 +65,12 @@ export enum ViewFlags {
|
|||
DirectDom = 1 << 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A node definition in the view.
|
||||
*
|
||||
* Note: We use one type for all nodes so that loops that loop over all nodes
|
||||
* of a ViewDefinition stay monomorphic!
|
||||
*/
|
||||
export interface NodeDef {
|
||||
type: NodeType;
|
||||
index: number;
|
||||
|
@ -76,7 +82,6 @@ export interface NodeDef {
|
|||
/** aggregated NodeFlags for all children **/
|
||||
childFlags: NodeFlags;
|
||||
|
||||
providerIndices: {[tokenKey: string]: number};
|
||||
bindingIndex: number;
|
||||
bindings: BindingDef[];
|
||||
disposableIndex: number;
|
||||
|
@ -94,6 +99,7 @@ export interface NodeDef {
|
|||
provider: ProviderDef;
|
||||
text: TextDef;
|
||||
pureExpression: PureExpressionDef;
|
||||
query: QueryDef;
|
||||
}
|
||||
|
||||
export enum NodeType {
|
||||
|
@ -101,6 +107,7 @@ export enum NodeType {
|
|||
Text,
|
||||
Provider,
|
||||
PureExpression,
|
||||
Query,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,11 +129,43 @@ export enum NodeFlags {
|
|||
HasViewQuery = 1 << 11,
|
||||
}
|
||||
|
||||
export interface BindingDef {
|
||||
type: BindingType;
|
||||
name: string;
|
||||
nonMinifiedName: string;
|
||||
securityContext: SecurityContext;
|
||||
suffix: string;
|
||||
}
|
||||
|
||||
export enum BindingType {
|
||||
ElementAttribute,
|
||||
ElementClass,
|
||||
ElementStyle,
|
||||
ElementProperty,
|
||||
ProviderProperty,
|
||||
Interpolation,
|
||||
PureExpressionProperty
|
||||
}
|
||||
|
||||
export enum QueryValueType {
|
||||
ElementRef,
|
||||
TemplateRef,
|
||||
ViewContainerRef,
|
||||
Provider
|
||||
}
|
||||
|
||||
export interface ElementDef {
|
||||
name: string;
|
||||
attrs: {[name: string]: string};
|
||||
outputs: ElementOutputDef[];
|
||||
template: ViewDefinition;
|
||||
/**
|
||||
* visible providers for DI in the view,
|
||||
* as see from this element.
|
||||
* Note: We use protoypical inheritance
|
||||
* to indices in parent ElementDefs.
|
||||
*/
|
||||
providerIndices: {[tokenKey: string]: number};
|
||||
}
|
||||
|
||||
export interface ElementOutputDef {
|
||||
|
@ -134,6 +173,21 @@ export interface ElementOutputDef {
|
|||
eventName: string;
|
||||
}
|
||||
|
||||
export interface ProviderDef {
|
||||
tokenKey: string;
|
||||
ctor: any;
|
||||
deps: DepDef[];
|
||||
outputs: ProviderOutputDef[];
|
||||
// closure to allow recursive components
|
||||
component: () => ViewDefinition;
|
||||
}
|
||||
|
||||
export interface DepDef {
|
||||
flags: DepFlags;
|
||||
token: any;
|
||||
tokenKey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitmask for DI flags
|
||||
*/
|
||||
|
@ -142,46 +196,11 @@ export enum DepFlags {
|
|||
SkipSelf = 1 << 0
|
||||
}
|
||||
|
||||
export interface DepDef {
|
||||
flags: DepFlags;
|
||||
token: any;
|
||||
tokenKey: string;
|
||||
}
|
||||
|
||||
export interface ProviderOutputDef {
|
||||
propName: string;
|
||||
eventName: string;
|
||||
}
|
||||
|
||||
export interface ProviderDef {
|
||||
tokenKey: string;
|
||||
ctor: any;
|
||||
deps: DepDef[];
|
||||
outputs: ProviderOutputDef[];
|
||||
contentQueries: QueryDef[];
|
||||
viewQueries: QueryDef[];
|
||||
// closure to allow recursive components
|
||||
component: () => ViewDefinition;
|
||||
}
|
||||
|
||||
export interface QueryDef {
|
||||
id: string;
|
||||
propName: string;
|
||||
bindingType: QueryBindingType;
|
||||
}
|
||||
|
||||
export enum QueryBindingType {
|
||||
First,
|
||||
All
|
||||
}
|
||||
|
||||
export enum QueryValueType {
|
||||
ElementRef,
|
||||
TemplateRef,
|
||||
ViewContainerRef,
|
||||
Provider
|
||||
}
|
||||
|
||||
export interface TextDef { prefix: string; }
|
||||
|
||||
export interface PureExpressionDef {
|
||||
|
@ -195,22 +214,19 @@ export enum PureExpressionType {
|
|||
Pipe
|
||||
}
|
||||
|
||||
export enum BindingType {
|
||||
ElementAttribute,
|
||||
ElementClass,
|
||||
ElementStyle,
|
||||
ElementProperty,
|
||||
ProviderProperty,
|
||||
Interpolation,
|
||||
PureExpressionProperty
|
||||
export interface QueryDef {
|
||||
id: string;
|
||||
bindings: QueryBindingDef[];
|
||||
}
|
||||
|
||||
export interface BindingDef {
|
||||
type: BindingType;
|
||||
name: string;
|
||||
nonMinifiedName: string;
|
||||
securityContext: SecurityContext;
|
||||
suffix: string;
|
||||
export interface QueryBindingDef {
|
||||
propName: string;
|
||||
bindingType: QueryBindingType;
|
||||
}
|
||||
|
||||
export enum QueryBindingType {
|
||||
First,
|
||||
All
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
@ -235,7 +251,12 @@ export interface ViewData {
|
|||
parent: ViewData;
|
||||
component: any;
|
||||
context: any;
|
||||
nodes: NodeData[];
|
||||
// Attention: Never loop over this, as this will
|
||||
// create a polymorphic usage site.
|
||||
// Instead: Always loop over ViewDefinition.nodes,
|
||||
// and call the right accessor (e.g. `elementData`) based on
|
||||
// the NodeType.
|
||||
nodes: {[key: number]: NodeData};
|
||||
firstChange: boolean;
|
||||
oldValues: any[];
|
||||
disposables: DisposableFn[];
|
||||
|
@ -245,16 +266,38 @@ export type DisposableFn = () => void;
|
|||
|
||||
/**
|
||||
* Node instance data.
|
||||
*
|
||||
* We have a separate type per NodeType to save memory
|
||||
* (TextData | ElementData | ProviderData | PureExpressionData | QueryList<any>)
|
||||
*
|
||||
* To keep our code monomorphic,
|
||||
* we prohibit using `NodeData` directly but enforce the use of accessors (`asElementData`, ...).
|
||||
* This way, no usage site can get a `NodeData` from view.nodes and then use it for different
|
||||
* purposes.
|
||||
*/
|
||||
export class NodeData { private __brand: any; }
|
||||
|
||||
/**
|
||||
* Data for an instantiated NodeType.Text.
|
||||
*
|
||||
* Attention: Adding fields to this is performance sensitive!
|
||||
*/
|
||||
export interface NodeData {
|
||||
elementOrText: ElementOrTextData;
|
||||
provider: ProviderData;
|
||||
pureExpression: PureExpressionData;
|
||||
export interface TextData { renderText: any; }
|
||||
|
||||
/**
|
||||
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
|
||||
*/
|
||||
export function asTextData(view: ViewData, index: number): TextData {
|
||||
return <any>view.nodes[index];
|
||||
}
|
||||
|
||||
export interface ElementOrTextData {
|
||||
node: any;
|
||||
/**
|
||||
* Data for an instantiated NodeType.Element.
|
||||
*
|
||||
* Attention: Adding fields to this is performance sensitive!
|
||||
*/
|
||||
export interface ElementData {
|
||||
renderElement: any;
|
||||
embeddedViews: ViewData[];
|
||||
// views that have been created from the template
|
||||
// of this element,
|
||||
|
@ -263,22 +306,59 @@ export interface ElementOrTextData {
|
|||
projectedViews: ViewData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
|
||||
*/
|
||||
export function asElementData(view: ViewData, index: number): ElementData {
|
||||
return <any>view.nodes[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for an instantiated NodeType.Provider.
|
||||
*
|
||||
* Attention: Adding fields to this is performance sensitive!
|
||||
*/
|
||||
export interface ProviderData {
|
||||
instance: any;
|
||||
componentView: ViewData;
|
||||
queries: {[queryId: string]: QueryList<any>};
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
|
||||
*/
|
||||
export function asProviderData(view: ViewData, index: number): ProviderData {
|
||||
return <any>view.nodes[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for an instantiated NodeType.PureExpression.
|
||||
*
|
||||
* Attention: Adding fields to this is performance sensitive!
|
||||
*/
|
||||
export interface PureExpressionData {
|
||||
value: any;
|
||||
pipe: PipeTransform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
|
||||
*/
|
||||
export function asPureExpressionData(view: ViewData, index: number): PureExpressionData {
|
||||
return <any>view.nodes[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
|
||||
*/
|
||||
export function asQueryList(view: ViewData, index: number): QueryList<any> {
|
||||
return <any>view.nodes[index];
|
||||
}
|
||||
|
||||
export interface Services {
|
||||
renderComponent(rcp: RenderComponentType): Renderer;
|
||||
sanitize(context: SecurityContext, value: string): string;
|
||||
// Note: This needs to be here to prevent a cycle in source files.
|
||||
createViewContainerRef(data: NodeData): ViewContainerRef;
|
||||
createViewContainerRef(data: ElementData): ViewContainerRef;
|
||||
// Note: This needs to be here to prevent a cycle in source files.
|
||||
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any>;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import {looseIdentical} from '../facade/lang';
|
|||
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
|
||||
import {Renderer} from '../render/api';
|
||||
|
||||
import {NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewDefinition} from './types';
|
||||
import {ElementData, NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewDefinition, asElementData, asTextData} from './types';
|
||||
|
||||
export function setBindingDebugInfo(
|
||||
renderer: Renderer, renderNode: any, propName: string, value: any) {
|
||||
|
@ -61,10 +61,19 @@ export function checkAndUpdateBindingWithChange(
|
|||
return null;
|
||||
}
|
||||
|
||||
export function declaredViewContainer(view: ViewData) {
|
||||
export function declaredViewContainer(view: ViewData): ElementData {
|
||||
if (view.parent) {
|
||||
const parentView = view.parent;
|
||||
return parentView.nodes[view.parentIndex];
|
||||
return asElementData(parentView, view.parentIndex);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function renderNode(view: ViewData, def: NodeDef): any {
|
||||
switch (def.type) {
|
||||
case NodeType.Element:
|
||||
return asElementData(view, def.index).renderElement;
|
||||
case NodeType.Text:
|
||||
return asTextData(view, def.index).renderText;
|
||||
}
|
||||
}
|
|
@ -10,10 +10,11 @@ import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
|
|||
import {RenderComponentType, Renderer} from '../render/api';
|
||||
|
||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
||||
import {QueryAction, callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAndUpdateProviderInline, createProvider, execContentQueriesAction, updateViewQueries} from './provider';
|
||||
import {callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAndUpdateProviderInline, createProvider} from './provider';
|
||||
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
||||
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
|
||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||
import {ElementDef, NodeData, NodeDef, NodeFlags, NodeType, NodeUpdater, ProviderData, ProviderDef, Services, TextDef, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn} from './types';
|
||||
import {ElementDef, NodeData, NodeDef, NodeFlags, NodeType, NodeUpdater, ProviderData, ProviderDef, Services, TextDef, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList} from './types';
|
||||
import {checkBindingNoChanges} from './util';
|
||||
|
||||
const NOOP = (): any => undefined;
|
||||
|
@ -25,6 +26,7 @@ export function viewDef(
|
|||
if (nodesWithoutIndices.length === 0) {
|
||||
throw new Error(`Illegal State: Views without nodes are not allowed!`);
|
||||
}
|
||||
|
||||
const nodes: NodeDef[] = new Array(nodesWithoutIndices.length);
|
||||
const reverseChildNodes: NodeDef[] = new Array(nodesWithoutIndices.length);
|
||||
let viewBindingCount = 0;
|
||||
|
@ -42,15 +44,21 @@ export function viewDef(
|
|||
}
|
||||
currentParent = newParent;
|
||||
}
|
||||
const nodeWithoutIndices = nodesWithoutIndices[i];
|
||||
const reverseChildIndex = calculateReverseChildIndex(
|
||||
currentParent, i, nodesWithoutIndices[i].childCount, nodesWithoutIndices.length);
|
||||
const node = cloneAndModifyNode(nodesWithoutIndices[i], {
|
||||
currentParent, i, nodeWithoutIndices.childCount, nodesWithoutIndices.length);
|
||||
|
||||
const node = cloneAndModifyNode(nodeWithoutIndices, {
|
||||
index: i,
|
||||
parent: currentParent ? currentParent.index : undefined,
|
||||
bindingIndex: viewBindingCount,
|
||||
disposableIndex: viewDisposableCount, reverseChildIndex,
|
||||
providerIndices: Object.create(currentParent ? currentParent.providerIndices : null)
|
||||
});
|
||||
if (node.element) {
|
||||
node.element = cloneAndModifyElement(node.element, {
|
||||
providerIndices: Object.create(currentParent ? currentParent.element.providerIndices : null)
|
||||
});
|
||||
}
|
||||
nodes[i] = node;
|
||||
reverseChildNodes[reverseChildIndex] = node;
|
||||
validateNode(currentParent, node);
|
||||
|
@ -71,13 +79,11 @@ export function viewDef(
|
|||
lastRootNode = node;
|
||||
}
|
||||
if (node.provider) {
|
||||
currentParent.providerIndices[node.provider.tokenKey] = i;
|
||||
for (let k = 0; k < node.provider.contentQueries.length; k++) {
|
||||
currentParent.providerIndices[node.provider.contentQueries[k].id] = i;
|
||||
}
|
||||
for (let k = 0; k < node.provider.viewQueries.length; k++) {
|
||||
currentParent.providerIndices[node.provider.viewQueries[k].id] = i;
|
||||
currentParent.element.providerIndices[node.provider.tokenKey] = i;
|
||||
}
|
||||
if (node.query) {
|
||||
const elementDef = nodes[currentParent.parent];
|
||||
elementDef.element.providerIndices[node.query.id] = i;
|
||||
}
|
||||
if (node.childCount) {
|
||||
currentParent = node;
|
||||
|
@ -99,8 +105,7 @@ export function viewDef(
|
|||
update: update || NOOP,
|
||||
handleEvent: handleEvent || NOOP, componentType,
|
||||
bindingCount: viewBindingCount,
|
||||
disposableCount: viewDisposableCount,
|
||||
lastRootNode: lastRootNode.index
|
||||
disposableCount: viewDisposableCount, lastRootNode
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -152,8 +157,7 @@ function calculateReverseChildIndex(
|
|||
function validateNode(parent: NodeDef, node: NodeDef) {
|
||||
const template = node.element && node.element.template;
|
||||
if (template) {
|
||||
if (template.lastRootNode != null &&
|
||||
template.nodes[template.lastRootNode].flags & NodeFlags.HasEmbeddedViews) {
|
||||
if (template.lastRootNode && template.lastRootNode.flags & NodeFlags.HasEmbeddedViews) {
|
||||
throw new Error(
|
||||
`Illegal State: Last root node of a template can't have embedded views, at index ${node.index}!`);
|
||||
}
|
||||
|
@ -165,6 +169,13 @@ function validateNode(parent: NodeDef, node: NodeDef) {
|
|||
`Illegal State: Provider nodes need to be children of elements or anchors, at index ${node.index}!`);
|
||||
}
|
||||
}
|
||||
if (node.query) {
|
||||
const parentType = parent ? parent.type : null;
|
||||
if (parentType !== NodeType.Provider) {
|
||||
throw new Error(
|
||||
`Illegal State: Query nodes need to be children of providers, at index ${node.index}!`);
|
||||
}
|
||||
}
|
||||
if (node.childCount) {
|
||||
if (parent) {
|
||||
const parentEnd = parent.index + parent.childCount;
|
||||
|
@ -182,7 +193,6 @@ function cloneAndModifyNode(nodeDef: NodeDef, values: {
|
|||
parent: number,
|
||||
bindingIndex: number,
|
||||
disposableIndex: number,
|
||||
providerIndices: {[tokenKey: string]: number}
|
||||
}): NodeDef {
|
||||
const clonedNode: NodeDef = <any>{};
|
||||
copyInto(nodeDef, clonedNode);
|
||||
|
@ -192,13 +202,21 @@ function cloneAndModifyNode(nodeDef: NodeDef, values: {
|
|||
clonedNode.disposableIndex = values.disposableIndex;
|
||||
clonedNode.parent = values.parent;
|
||||
clonedNode.reverseChildIndex = values.reverseChildIndex;
|
||||
clonedNode.providerIndices = values.providerIndices;
|
||||
|
||||
// Note: We can't set the value immediately, as we need to walk the children first.
|
||||
clonedNode.childFlags = 0;
|
||||
clonedNode.childMatchedQueries = {};
|
||||
return clonedNode;
|
||||
}
|
||||
|
||||
function cloneAndModifyElement(
|
||||
elementDef: ElementDef, values: {providerIndices: {[tokenKey: string]: number}}): ElementDef {
|
||||
const clonedElement: ElementDef = <any>{};
|
||||
copyInto(elementDef, clonedElement);
|
||||
clonedElement.providerIndices = values.providerIndices;
|
||||
return clonedElement;
|
||||
}
|
||||
|
||||
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||
// embedded views are seen as siblings to the anchor, so we need
|
||||
// to get the parent of the anchor and use it as parentIndex.
|
||||
|
@ -256,13 +274,18 @@ function initView(view: ViewData, renderHost: any, component: any, context: any)
|
|||
case NodeType.Provider:
|
||||
let componentView: ViewData;
|
||||
if (nodeDef.provider.component) {
|
||||
componentView = createView(view.services, view, i, i, nodeDef.provider.component());
|
||||
const hostElIndex = nodeDef.parent;
|
||||
componentView = createView(
|
||||
view.services, view, hostElIndex, hostElIndex, nodeDef.provider.component());
|
||||
}
|
||||
nodeData = createProvider(view, nodeDef, componentView);
|
||||
break;
|
||||
case NodeType.PureExpression:
|
||||
nodeData = createPureExpression(view, nodeDef);
|
||||
break;
|
||||
case NodeType.Query:
|
||||
nodeData = createQuery();
|
||||
break;
|
||||
}
|
||||
nodes[i] = nodeData;
|
||||
}
|
||||
|
@ -272,9 +295,9 @@ function initView(view: ViewData, renderHost: any, component: any, context: any)
|
|||
export function checkNoChangesView(view: ViewData) {
|
||||
view.def.update(CheckNoChanges, view);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
||||
execContentQueriesAction(view, QueryAction.CheckNoChanges);
|
||||
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckNoChanges);
|
||||
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
||||
updateViewQueries(view, QueryAction.CheckNoChanges);
|
||||
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckNoChanges);
|
||||
}
|
||||
|
||||
const CheckNoChanges: NodeUpdater = {
|
||||
|
@ -305,7 +328,7 @@ const CheckNoChanges: NodeUpdater = {
|
|||
checkBindingNoChanges(view, nodeDef, 0, v0);
|
||||
}
|
||||
if (nodeDef.type === NodeType.PureExpression) {
|
||||
return view.nodes[index].pureExpression.value;
|
||||
return asPureExpressionData(view, index).value;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
@ -315,7 +338,7 @@ const CheckNoChanges: NodeUpdater = {
|
|||
checkBindingNoChanges(view, nodeDef, i, values[i]);
|
||||
}
|
||||
if (nodeDef.type === NodeType.PureExpression) {
|
||||
return view.nodes[index].pureExpression.value;
|
||||
return asPureExpressionData(view, index).value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
@ -324,12 +347,12 @@ const CheckNoChanges: NodeUpdater = {
|
|||
export function checkAndUpdateView(view: ViewData) {
|
||||
view.def.update(CheckAndUpdate, view);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execContentQueriesAction(view, QueryAction.CheckAndUpdate);
|
||||
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckAndUpdate);
|
||||
|
||||
callLifecycleHooksChildrenFirst(
|
||||
view, NodeFlags.AfterContentChecked | (view.firstChange ? NodeFlags.AfterContentInit : 0));
|
||||
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
updateViewQueries(view, QueryAction.CheckAndUpdate);
|
||||
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckAndUpdate);
|
||||
|
||||
callLifecycleHooksChildrenFirst(
|
||||
view, NodeFlags.AfterViewChecked | (view.firstChange ? NodeFlags.AfterViewInit : 0));
|
||||
|
@ -352,7 +375,7 @@ const CheckAndUpdate: NodeUpdater = {
|
|||
return undefined;
|
||||
case NodeType.PureExpression:
|
||||
checkAndUpdatePureExpressionInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||
return view.nodes[index].pureExpression.value;
|
||||
return asPureExpressionData(view, index).value;
|
||||
}
|
||||
},
|
||||
checkDynamic: (view: ViewData, index: number, values: any[]): void => {
|
||||
|
@ -369,11 +392,18 @@ const CheckAndUpdate: NodeUpdater = {
|
|||
return undefined;
|
||||
case NodeType.PureExpression:
|
||||
checkAndUpdatePureExpressionDynamic(view, nodeDef, values);
|
||||
return view.nodes[index].pureExpression.value;
|
||||
return asPureExpressionData(view, index).value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) {
|
||||
const queryList = asQueryList(view, nodeDef.index);
|
||||
if (queryList.dirty) {
|
||||
throw new ExpressionChangedAfterItHasBeenCheckedError(false, true, view.firstChange);
|
||||
}
|
||||
}
|
||||
|
||||
export function destroyView(view: ViewData) {
|
||||
callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy);
|
||||
if (view.disposables) {
|
||||
|
@ -401,17 +431,16 @@ function execComponentViewsAction(view: ViewData, action: ViewAction) {
|
|||
const nodeDef = def.nodes[i];
|
||||
if (nodeDef.flags & NodeFlags.HasComponent) {
|
||||
// a leaf
|
||||
const nodeData = view.nodes[i];
|
||||
const providerData = asProviderData(view, i);
|
||||
if (action === ViewAction.InitComponent) {
|
||||
let renderHost = view.nodes[nodeDef.parent].elementOrText.node;
|
||||
let renderHost = asElementData(view, nodeDef.parent).renderElement;
|
||||
if (view.renderer) {
|
||||
renderHost = view.renderer.createViewRoot(renderHost);
|
||||
}
|
||||
initView(
|
||||
nodeData.provider.componentView, renderHost, nodeData.provider.instance,
|
||||
nodeData.provider.instance);
|
||||
providerData.componentView, renderHost, providerData.instance, providerData.instance);
|
||||
} else {
|
||||
callViewAction(nodeData.provider.componentView, action);
|
||||
callViewAction(providerData.componentView, action);
|
||||
}
|
||||
} else if ((nodeDef.childFlags & NodeFlags.HasComponent) === 0) {
|
||||
// a parent with leafs
|
||||
|
@ -431,8 +460,7 @@ function execEmbeddedViewsAction(view: ViewData, action: ViewAction) {
|
|||
const nodeDef = def.nodes[i];
|
||||
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
|
||||
// a leaf
|
||||
const nodeData = view.nodes[i];
|
||||
const embeddedViews = nodeData.elementOrText.embeddedViews;
|
||||
const embeddedViews = asElementData(view, i).embeddedViews;
|
||||
if (embeddedViews) {
|
||||
for (let k = 0; k < embeddedViews.length; k++) {
|
||||
callViewAction(embeddedViews[k], action);
|
||||
|
@ -460,3 +488,32 @@ function callViewAction(view: ViewData, action: ViewAction) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enum QueryAction {
|
||||
CheckAndUpdate,
|
||||
CheckNoChanges
|
||||
}
|
||||
|
||||
function execQueriesAction(view: ViewData, queryFlags: NodeFlags, action: QueryAction) {
|
||||
if (!(view.def.nodeFlags & queryFlags)) {
|
||||
return;
|
||||
}
|
||||
const nodeCount = view.def.nodes.length;
|
||||
for (let i = 0; i < nodeCount; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if (nodeDef.flags & queryFlags) {
|
||||
switch (action) {
|
||||
case QueryAction.CheckAndUpdate:
|
||||
checkAndUpdateQuery(view, nodeDef);
|
||||
break;
|
||||
case QueryAction.CheckNoChanges:
|
||||
checkNoChangesQuery(view, nodeDef);
|
||||
break;
|
||||
}
|
||||
} else if ((nodeDef.childFlags & queryFlags) === 0) {
|
||||
// no child has a content query
|
||||
// then skip the children
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,20 +6,21 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NodeData, NodeFlags, ViewData} from './types';
|
||||
import {declaredViewContainer} from './util';
|
||||
import {dirtyParentQuery} from './query';
|
||||
import {ElementData, NodeData, NodeFlags, NodeType, ViewData, asElementData, asProviderData, asTextData} from './types';
|
||||
import {declaredViewContainer, renderNode} from './util';
|
||||
|
||||
export function attachEmbeddedView(node: NodeData, viewIndex: number, view: ViewData) {
|
||||
let embeddedViews = node.elementOrText.embeddedViews;
|
||||
export function attachEmbeddedView(elementData: ElementData, viewIndex: number, view: ViewData) {
|
||||
let embeddedViews = elementData.embeddedViews;
|
||||
if (viewIndex == null) {
|
||||
viewIndex = embeddedViews.length;
|
||||
}
|
||||
addToArray(embeddedViews, viewIndex, view);
|
||||
const dvc = declaredViewContainer(view);
|
||||
if (dvc && dvc !== node) {
|
||||
let projectedViews = dvc.elementOrText.projectedViews;
|
||||
const dvcElementData = declaredViewContainer(view);
|
||||
if (dvcElementData && dvcElementData !== elementData) {
|
||||
let projectedViews = dvcElementData.projectedViews;
|
||||
if (!projectedViews) {
|
||||
projectedViews = dvc.elementOrText.projectedViews = [];
|
||||
projectedViews = dvcElementData.projectedViews = [];
|
||||
}
|
||||
projectedViews.push(view);
|
||||
}
|
||||
|
@ -30,8 +31,8 @@ export function attachEmbeddedView(node: NodeData, viewIndex: number, view: View
|
|||
|
||||
// update rendering
|
||||
const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null;
|
||||
const prevNode = prevView ? prevView.nodes[prevView.def.lastRootNode] : node;
|
||||
const prevRenderNode = prevNode.elementOrText.node;
|
||||
const prevRenderNode =
|
||||
prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
|
||||
if (view.renderer) {
|
||||
view.renderer.attachViewAfter(prevRenderNode, rootRenderNodes(view));
|
||||
} else {
|
||||
|
@ -44,18 +45,17 @@ export function attachEmbeddedView(node: NodeData, viewIndex: number, view: View
|
|||
}
|
||||
}
|
||||
|
||||
export function detachEmbeddedView(node: NodeData, viewIndex: number): ViewData {
|
||||
const renderData = node.elementOrText;
|
||||
const embeddedViews = renderData.embeddedViews;
|
||||
export function detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData {
|
||||
const embeddedViews = elementData.embeddedViews;
|
||||
if (viewIndex == null) {
|
||||
viewIndex = embeddedViews.length;
|
||||
}
|
||||
const view = embeddedViews[viewIndex];
|
||||
removeFromArray(embeddedViews, viewIndex);
|
||||
|
||||
const dvc = declaredViewContainer(view);
|
||||
if (dvc && dvc !== node) {
|
||||
const projectedViews = dvc.elementOrText.projectedViews;
|
||||
const dvcElementData = declaredViewContainer(view);
|
||||
if (dvcElementData && dvcElementData !== elementData) {
|
||||
const projectedViews = dvcElementData.projectedViews;
|
||||
removeFromArray(projectedViews, projectedViews.indexOf(view));
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ export function detachEmbeddedView(node: NodeData, viewIndex: number): ViewData
|
|||
if (view.renderer) {
|
||||
view.renderer.detachView(rootRenderNodes(view));
|
||||
} else {
|
||||
const parentNode = renderData.node.parentNode;
|
||||
const parentNode = elementData.renderElement.parentNode;
|
||||
if (parentNode) {
|
||||
directDomAttachDetachSiblingRenderNodes(
|
||||
view, 0, DirectDomAction.RemoveChild, parentNode, null);
|
||||
|
@ -94,27 +94,6 @@ function removeFromArray(arr: any[], index: number) {
|
|||
}
|
||||
}
|
||||
|
||||
function dirtyParentQuery(queryId: string, view: ViewData) {
|
||||
let nodeIndex = view.parentIndex;
|
||||
view = view.parent;
|
||||
let providerIdx: number;
|
||||
while (view) {
|
||||
const nodeDef = view.def.nodes[nodeIndex];
|
||||
providerIdx = nodeDef.providerIndices[queryId];
|
||||
if (providerIdx != null) {
|
||||
break;
|
||||
}
|
||||
nodeIndex = view.parentIndex;
|
||||
view = view.parent;
|
||||
}
|
||||
if (!view) {
|
||||
throw new Error(
|
||||
`Illegal State: Tried to dirty parent query ${queryId} but the query could not be found!`);
|
||||
}
|
||||
const providerData = view.nodes[providerIdx].provider;
|
||||
providerData.queries[queryId].setDirty();
|
||||
}
|
||||
|
||||
export function rootRenderNodes(view: ViewData): any[] {
|
||||
const renderNodes: any[] = [];
|
||||
collectSiblingRenderNodes(view, 0, renderNodes);
|
||||
|
@ -122,12 +101,12 @@ export function rootRenderNodes(view: ViewData): any[] {
|
|||
}
|
||||
|
||||
function collectSiblingRenderNodes(view: ViewData, startIndex: number, target: any[]) {
|
||||
for (let i = startIndex; i < view.nodes.length; i++) {
|
||||
const nodeCount = view.def.nodes.length;
|
||||
for (let i = startIndex; i < nodeCount; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
const nodeData = view.nodes[i].elementOrText;
|
||||
target.push(nodeData.node);
|
||||
target.push(renderNode(view, nodeDef));
|
||||
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
|
||||
const embeddedViews = nodeData.embeddedViews;
|
||||
const embeddedViews = asElementData(view, i).embeddedViews;
|
||||
if (embeddedViews) {
|
||||
for (let k = 0; k < embeddedViews.length; k++) {
|
||||
collectSiblingRenderNodes(embeddedViews[k], 0, target);
|
||||
|
@ -148,22 +127,23 @@ enum DirectDomAction {
|
|||
function directDomAttachDetachSiblingRenderNodes(
|
||||
view: ViewData, startIndex: number, action: DirectDomAction, parentNode: any,
|
||||
nextSibling: any) {
|
||||
for (let i = startIndex; i < view.nodes.length; i++) {
|
||||
const nodeCount = view.def.nodes.length;
|
||||
for (let i = startIndex; i < nodeCount; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
const nodeData = view.nodes[i].elementOrText;
|
||||
const rn = renderNode(view, nodeDef);
|
||||
switch (action) {
|
||||
case DirectDomAction.AppendChild:
|
||||
parentNode.appendChild(nodeData.node);
|
||||
parentNode.appendChild(rn);
|
||||
break;
|
||||
case DirectDomAction.InsertBefore:
|
||||
parentNode.insertBefore(nodeData.node, nextSibling);
|
||||
parentNode.insertBefore(rn, nextSibling);
|
||||
break;
|
||||
case DirectDomAction.RemoveChild:
|
||||
parentNode.removeChild(nodeData.node);
|
||||
parentNode.removeChild(rn);
|
||||
break;
|
||||
}
|
||||
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
|
||||
const embeddedViews = nodeData.embeddedViews;
|
||||
const embeddedViews = asElementData(view, i).embeddedViews;
|
||||
if (embeddedViews) {
|
||||
for (let k = 0; k < embeddedViews.length; k++) {
|
||||
directDomAttachDetachSiblingRenderNodes(
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
|
@ -54,13 +54,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
providerDef(
|
||||
NodeFlags.None, null, AComp, [], null, null, null,
|
||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
||||
() => compViewDef([
|
||||
elementDef(NodeFlags.None, null, 0, 'span'),
|
||||
])),
|
||||
]));
|
||||
|
||||
const compView = view.nodes[1].provider.componentView;
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
expect(compView.context).toBe(instance);
|
||||
expect(compView.component).toBe(instance);
|
||||
|
@ -81,13 +81,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const {view, rootNodes} = createAndGetRootNodes(
|
||||
compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
providerDef(NodeFlags.None, null, AComp, [], null, null, null, () => compViewDef(
|
||||
providerDef(NodeFlags.None, null, 0, AComp, [], null, null, () => compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
||||
], update
|
||||
)),
|
||||
], jasmine.createSpy('parentUpdater')));
|
||||
const compView = view.nodes[1].provider.componentView;
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
|
@ -118,10 +118,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
providerDef(
|
||||
NodeFlags.None, null, AComp, [], null, null, null,
|
||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
||||
() => compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.OnDestroy, null, ChildProvider, [])
|
||||
providerDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
|
||||
])),
|
||||
]));
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
|
@ -83,16 +83,16 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
|
||||
const rootChildren = getDOM().childNodes(rootNodes[0]);
|
||||
attachEmbeddedView(parentView.nodes[1], 0, childView0);
|
||||
attachEmbeddedView(parentView.nodes[1], 1, childView1);
|
||||
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
||||
attachEmbeddedView(asElementData(parentView, 1), 1, childView1);
|
||||
|
||||
// 2 anchors + 2 elements
|
||||
expect(rootChildren.length).toBe(4);
|
||||
expect(getDOM().getAttribute(rootChildren[1], 'name')).toBe('child0');
|
||||
expect(getDOM().getAttribute(rootChildren[2], 'name')).toBe('child1');
|
||||
|
||||
detachEmbeddedView(parentView.nodes[1], 1);
|
||||
detachEmbeddedView(parentView.nodes[1], 0);
|
||||
detachEmbeddedView(asElementData(parentView, 1), 1);
|
||||
detachEmbeddedView(asElementData(parentView, 1), 0);
|
||||
|
||||
expect(getDOM().childNodes(rootNodes[0]).length).toBe(2);
|
||||
});
|
||||
|
@ -106,7 +106,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
]));
|
||||
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[0]);
|
||||
attachEmbeddedView(parentView.nodes[0], 0, childView0);
|
||||
attachEmbeddedView(asElementData(parentView, 0), 0, childView0);
|
||||
|
||||
const rootNodes = rootRenderNodes(parentView);
|
||||
expect(rootNodes.length).toBe(3);
|
||||
|
@ -133,7 +133,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
|
||||
const rootEl = rootNodes[0];
|
||||
attachEmbeddedView(parentView.nodes[1], 0, childView0);
|
||||
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
||||
|
||||
checkAndUpdateView(parentView);
|
||||
|
||||
|
@ -163,13 +163,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
anchorDef(NodeFlags.HasEmbeddedViews, null, 0, embeddedViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.OnDestroy, null, ChildProvider, [])
|
||||
providerDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
|
||||
]))
|
||||
]));
|
||||
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
|
||||
attachEmbeddedView(parentView.nodes[1], 0, childView0);
|
||||
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
||||
destroyView(parentView);
|
||||
|
||||
expect(log).toEqual(['ngOnDestroy']);
|
||||
|
|
|
@ -58,7 +58,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [])
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [])
|
||||
]));
|
||||
|
||||
expect(instances.length).toBe(1);
|
||||
|
@ -76,8 +76,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
|
||||
it('should inject deps from the same element', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 2, 'span'), providerDef(NodeFlags.None, null, Dep, []),
|
||||
providerDef(NodeFlags.None, null, SomeService, [Dep])
|
||||
elementDef(NodeFlags.None, null, 2, 'span'),
|
||||
providerDef(NodeFlags.None, null, 0, Dep, []),
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
|
||||
]));
|
||||
|
||||
expect(instance.dep instanceof Dep).toBeTruthy();
|
||||
|
@ -85,9 +86,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
|
||||
it('should inject deps from a parent element', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 3, 'span'), providerDef(NodeFlags.None, null, Dep, []),
|
||||
elementDef(NodeFlags.None, null, 3, 'span'),
|
||||
providerDef(NodeFlags.None, null, 0, Dep, []),
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [Dep])
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
|
||||
]));
|
||||
|
||||
expect(instance.dep instanceof Dep).toBeTruthy();
|
||||
|
@ -95,9 +97,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
|
||||
it('should not inject deps from sibling root elements', () => {
|
||||
const nodes = [
|
||||
elementDef(NodeFlags.None, null, 1, 'span'), providerDef(NodeFlags.None, null, Dep, []),
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [Dep])
|
||||
providerDef(NodeFlags.None, null, 0, Dep, []),
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
|
||||
];
|
||||
|
||||
// root elements
|
||||
|
@ -115,10 +118,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
providerDef(
|
||||
NodeFlags.None, null, Dep, [], null, null, null,
|
||||
NodeFlags.None, null, 0, Dep, [], null, null,
|
||||
() => compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [Dep])
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
|
||||
])),
|
||||
]));
|
||||
|
||||
|
@ -129,7 +132,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
it('should inject ViewContainerRef', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
anchorDef(NodeFlags.HasEmbeddedViews, null, 1),
|
||||
providerDef(NodeFlags.None, null, SomeService, [ViewContainerRef])
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [ViewContainerRef])
|
||||
]));
|
||||
|
||||
expect(instance.dep.createEmbeddedView).toBeTruthy();
|
||||
|
@ -139,7 +142,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
createAndGetRootNodes(compViewDef([
|
||||
anchorDef(
|
||||
NodeFlags.None, null, 1, embeddedViewDef([anchorDef(NodeFlags.None, null, 0)])),
|
||||
providerDef(NodeFlags.None, null, SomeService, [TemplateRef])
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [TemplateRef])
|
||||
]));
|
||||
|
||||
expect(instance.dep.createEmbeddedView).toBeTruthy();
|
||||
|
@ -148,7 +151,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
it('should inject ElementRef', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [ElementRef])
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [ElementRef])
|
||||
]));
|
||||
|
||||
expect(getDOM().nodeName(instance.dep.nativeElement).toLowerCase()).toBe('span');
|
||||
|
@ -158,7 +161,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
it('should not inject Renderer when using directDom', () => {
|
||||
expect(() => createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [Renderer])
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
||||
])))
|
||||
.toThrowError('No provider for Renderer!');
|
||||
});
|
||||
|
@ -166,7 +169,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
it('should inject Renderer when not using directDom', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [Renderer])
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
||||
]));
|
||||
|
||||
expect(instance.dep.createElement).toBeTruthy();
|
||||
|
@ -199,7 +202,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
|
||||
],
|
||||
config.update));
|
||||
|
||||
|
@ -219,7 +222,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [], {a: [0, 'a']})
|
||||
providerDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a']})
|
||||
],
|
||||
(updater, view) => updater.checkInline(view, 1, propValue)));
|
||||
|
||||
|
@ -254,7 +257,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomeService, [], null, {emitter: 'someEventName'})
|
||||
providerDef(
|
||||
NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'})
|
||||
],
|
||||
null, handleEvent));
|
||||
|
||||
|
@ -292,9 +296,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 3, 'span'),
|
||||
providerDef(allFlags, null, SomeService, [], {a: [0, 'a']}),
|
||||
providerDef(allFlags, null, 0, SomeService, [], {a: [0, 'a']}),
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(allFlags, null, SomeService, [], {a: [0, 'a']})
|
||||
providerDef(allFlags, null, 0, SomeService, [], {a: [0, 'a']})
|
||||
],
|
||||
(updater) => {
|
||||
updater.checkInline(view, 1, 'someValue');
|
||||
|
@ -351,7 +355,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.OnChanges, null, SomeService, [], {a: [0, 'nonMinifiedA']})
|
||||
providerDef(NodeFlags.OnChanges, null, 0, SomeService, [], {a: [0, 'nonMinifiedA']})
|
||||
],
|
||||
(updater) => updater.checkInline(view, 1, currValue)));
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, providerDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, providerDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
|
||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, callUpdater} from './helper';
|
||||
|
@ -46,14 +46,14 @@ export function main() {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 2, 'span'), pureArrayDef(2),
|
||||
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
|
||||
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(updater, view) => {
|
||||
callUpdater(
|
||||
updater, inlineDynamic, view, 2,
|
||||
[callUpdater(updater, inlineDynamic, view, 1, values)]);
|
||||
}));
|
||||
const service = view.nodes[2].provider.instance;
|
||||
const service = asProviderData(view, 2).instance;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
|
@ -80,14 +80,14 @@ export function main() {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 2, 'span'), pureObjectDef(['a', 'b']),
|
||||
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
|
||||
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(updater, view) => {
|
||||
callUpdater(
|
||||
updater, inlineDynamic, view, 2,
|
||||
[callUpdater(updater, inlineDynamic, view, 1, values)]);
|
||||
}));
|
||||
const service = view.nodes[2].provider.instance;
|
||||
const service = asProviderData(view, 2).instance;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
|
@ -118,15 +118,15 @@ export function main() {
|
|||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 3, 'span'),
|
||||
providerDef(NodeFlags.None, null, SomePipe, []), purePipeDef(SomePipe, 2),
|
||||
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
|
||||
providerDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
|
||||
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(updater, view) => {
|
||||
callUpdater(
|
||||
updater, inlineDynamic, view, 3,
|
||||
[callUpdater(updater, inlineDynamic, view, 2, values)]);
|
||||
}));
|
||||
const service = view.nodes[3].provider.instance;
|
||||
const service = asProviderData(view, 3).instance;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {ElementRef, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, QueryBindingType, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, QueryBindingType, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, queryDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
|
@ -45,57 +45,59 @@ export function main() {
|
|||
a: QueryList<AService>;
|
||||
}
|
||||
|
||||
function contentQueryProvider() {
|
||||
return providerDef(
|
||||
NodeFlags.None, null, QueryService, [], null, null,
|
||||
{'a': ['query1', QueryBindingType.All]});
|
||||
function contentQueryProviders() {
|
||||
return [
|
||||
providerDef(NodeFlags.None, null, 1, QueryService, []),
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.All})
|
||||
];
|
||||
}
|
||||
|
||||
function viewQueryProviders(compView: ViewDefinition) {
|
||||
return [
|
||||
providerDef(NodeFlags.None, null, 1, QueryService, [], null, null, () => compView),
|
||||
queryDef(NodeFlags.HasViewQuery, 'query1', {'a': QueryBindingType.All})
|
||||
];
|
||||
}
|
||||
|
||||
function aServiceProvider() {
|
||||
return providerDef(NodeFlags.None, [['query1', QueryValueType.Provider]], AService, []);
|
||||
}
|
||||
|
||||
function viewQueryProvider(compView: ViewDefinition) {
|
||||
return providerDef(
|
||||
NodeFlags.None, null, QueryService, [], null, null, null, () => compView,
|
||||
{'a': ['query1', QueryBindingType.All]});
|
||||
return providerDef(NodeFlags.None, [['query1', QueryValueType.Provider]], 0, AService, []);
|
||||
}
|
||||
|
||||
describe('content queries', () => {
|
||||
|
||||
it('should query providers on the same element and child elements', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 4, 'div'),
|
||||
contentQueryProvider(),
|
||||
elementDef(NodeFlags.None, null, 5, 'div'),
|
||||
...contentQueryProviders(),
|
||||
aServiceProvider(),
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
const qs: QueryService = view.nodes[1].provider.instance;
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a).toBeUndefined();
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const as = qs.a.toArray();
|
||||
expect(as.length).toBe(2);
|
||||
expect(as[0]).toBe(view.nodes[2].provider.instance);
|
||||
expect(as[1]).toBe(view.nodes[4].provider.instance);
|
||||
expect(as[0]).toBe(asProviderData(view, 3).instance);
|
||||
expect(as[1]).toBe(asProviderData(view, 5).instance);
|
||||
});
|
||||
|
||||
it('should not query providers on sibling or parent elements', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 5, 'div'),
|
||||
elementDef(NodeFlags.None, null, 6, 'div'),
|
||||
aServiceProvider(),
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
contentQueryProvider(),
|
||||
elementDef(NodeFlags.None, null, 2, 'div'),
|
||||
...contentQueryProviders(),
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = view.nodes[3].provider.instance;
|
||||
const qs: QueryService = asProviderData(view, 3).instance;
|
||||
expect(qs.a.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -103,8 +105,8 @@ export function main() {
|
|||
describe('view queries', () => {
|
||||
it('should query providers in the view', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
viewQueryProvider(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 2, 'div'),
|
||||
...viewQueryProviders(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
aServiceProvider(),
|
||||
])),
|
||||
|
@ -112,23 +114,23 @@ export function main() {
|
|||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const comp: QueryService = view.nodes[1].provider.instance;
|
||||
const compView = view.nodes[1].provider.componentView;
|
||||
const comp: QueryService = asProviderData(view, 1).instance;
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
expect(comp.a.length).toBe(1);
|
||||
expect(comp.a.first).toBe(compView.nodes[1].provider.instance);
|
||||
expect(comp.a.first).toBe(asProviderData(compView, 1).instance);
|
||||
});
|
||||
|
||||
it('should not query providers on the host element', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 2, 'div'),
|
||||
viewQueryProvider(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 3, 'div'),
|
||||
...viewQueryProviders(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
])),
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
const comp: QueryService = view.nodes[1].provider.instance;
|
||||
const comp: QueryService = asProviderData(view, 1).instance;
|
||||
expect(comp.a.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -136,37 +138,37 @@ export function main() {
|
|||
describe('embedded views', () => {
|
||||
it('should query providers in embedded views', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 3, 'div'),
|
||||
contentQueryProvider(),
|
||||
elementDef(NodeFlags.None, null, 5, 'div'),
|
||||
...contentQueryProviders(),
|
||||
anchorDef(
|
||||
NodeFlags.HasEmbeddedViews, null, 1, viewDef(
|
||||
NodeFlags.HasEmbeddedViews, null, 2, viewDef(
|
||||
ViewFlags.None,
|
||||
[
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
aServiceProvider(),
|
||||
])),
|
||||
contentQueryProvider(),
|
||||
...contentQueryProviders(),
|
||||
]));
|
||||
|
||||
const childView = createEmbeddedView(view, view.def.nodes[2]);
|
||||
attachEmbeddedView(view.nodes[2], 0, childView);
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||
checkAndUpdateView(view);
|
||||
|
||||
// queries on parent elements of anchors
|
||||
const qs1: QueryService = view.nodes[1].provider.instance;
|
||||
const qs1: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs1.a.length).toBe(1);
|
||||
expect(qs1.a.first instanceof AService).toBe(true);
|
||||
|
||||
// queries on the anchor
|
||||
const qs2: QueryService = view.nodes[3].provider.instance;
|
||||
const qs2: QueryService = asProviderData(view, 4).instance;
|
||||
expect(qs2.a.length).toBe(1);
|
||||
expect(qs2.a.first instanceof AService).toBe(true);
|
||||
});
|
||||
|
||||
it('should query providers in embedded views only at the template declaration', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 2, 'div'),
|
||||
contentQueryProvider(),
|
||||
elementDef(NodeFlags.None, null, 3, 'div'),
|
||||
...contentQueryProviders(),
|
||||
anchorDef(
|
||||
NodeFlags.HasEmbeddedViews, null, 0, viewDef(
|
||||
ViewFlags.None,
|
||||
|
@ -174,31 +176,31 @@ export function main() {
|
|||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
aServiceProvider(),
|
||||
])),
|
||||
elementDef(NodeFlags.None, null, 2, 'div'),
|
||||
contentQueryProvider(),
|
||||
elementDef(NodeFlags.None, null, 3, 'div'),
|
||||
...contentQueryProviders(),
|
||||
anchorDef(NodeFlags.HasEmbeddedViews, null, 0),
|
||||
]));
|
||||
|
||||
const childView = createEmbeddedView(view, view.def.nodes[2]);
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
// attach at a different place than the one where the template was defined
|
||||
attachEmbeddedView(view.nodes[5], 0, childView);
|
||||
attachEmbeddedView(asElementData(view, 7), 0, childView);
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
// query on the declaration place
|
||||
const qs1: QueryService = view.nodes[1].provider.instance;
|
||||
const qs1: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs1.a.length).toBe(1);
|
||||
expect(qs1.a.first instanceof AService).toBe(true);
|
||||
|
||||
// query on the attach place
|
||||
const qs2: QueryService = view.nodes[4].provider.instance;
|
||||
const qs2: QueryService = asProviderData(view, 5).instance;
|
||||
expect(qs2.a.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should checkNoChanges', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 3, 'div'),
|
||||
contentQueryProvider(),
|
||||
elementDef(NodeFlags.None, null, 4, 'div'),
|
||||
...contentQueryProviders(),
|
||||
anchorDef(
|
||||
NodeFlags.HasEmbeddedViews, null, 1, viewDef(
|
||||
ViewFlags.None,
|
||||
|
@ -211,18 +213,18 @@ export function main() {
|
|||
checkAndUpdateView(view);
|
||||
checkNoChangesView(view);
|
||||
|
||||
const childView = createEmbeddedView(view, view.def.nodes[2]);
|
||||
attachEmbeddedView(view.nodes[2], 0, childView);
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||
|
||||
expect(() => checkNoChangesView(view))
|
||||
.toThrowError(
|
||||
`Expression has changed after it was checked. Previous value: ''. Current value: '[object Object]'.`);
|
||||
`Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.`);
|
||||
});
|
||||
|
||||
it('should update content queries if embedded views are added or removed', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 2, 'div'),
|
||||
contentQueryProvider(),
|
||||
elementDef(NodeFlags.None, null, 3, 'div'),
|
||||
...contentQueryProviders(),
|
||||
anchorDef(
|
||||
NodeFlags.HasEmbeddedViews, null, 0, viewDef(
|
||||
ViewFlags.None,
|
||||
|
@ -234,16 +236,16 @@ export function main() {
|
|||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = view.nodes[1].provider.instance;
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.length).toBe(0);
|
||||
|
||||
const childView = createEmbeddedView(view, view.def.nodes[2]);
|
||||
attachEmbeddedView(view.nodes[2], 0, childView);
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||
checkAndUpdateView(view);
|
||||
|
||||
expect(qs.a.length).toBe(1);
|
||||
|
||||
detachEmbeddedView(view.nodes[2], 0);
|
||||
detachEmbeddedView(asElementData(view, 3), 0);
|
||||
checkAndUpdateView(view);
|
||||
|
||||
expect(qs.a.length).toBe(0);
|
||||
|
@ -251,8 +253,8 @@ export function main() {
|
|||
|
||||
it('should update view queries if embedded views are added or removed', () => {
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 1, 'div'),
|
||||
viewQueryProvider(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 2, 'div'),
|
||||
...viewQueryProviders(compViewDef([
|
||||
anchorDef(
|
||||
NodeFlags.HasEmbeddedViews, null, 0,
|
||||
viewDef(
|
||||
|
@ -266,17 +268,17 @@ export function main() {
|
|||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const comp: QueryService = view.nodes[1].provider.instance;
|
||||
const comp: QueryService = asProviderData(view, 1).instance;
|
||||
expect(comp.a.length).toBe(0);
|
||||
|
||||
const compView = view.nodes[1].provider.componentView;
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
const childView = createEmbeddedView(compView, compView.def.nodes[0]);
|
||||
attachEmbeddedView(compView.nodes[0], 0, childView);
|
||||
attachEmbeddedView(asElementData(compView, 0), 0, childView);
|
||||
checkAndUpdateView(view);
|
||||
|
||||
expect(comp.a.length).toBe(1);
|
||||
|
||||
detachEmbeddedView(compView.nodes[0], 0);
|
||||
detachEmbeddedView(asElementData(compView, 0), 0);
|
||||
checkAndUpdateView(view);
|
||||
|
||||
expect(comp.a.length).toBe(0);
|
||||
|
@ -290,21 +292,20 @@ export function main() {
|
|||
}
|
||||
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 3, 'div'),
|
||||
providerDef(
|
||||
NodeFlags.None, null, QueryService, [], null, null,
|
||||
{'a': ['query1', QueryBindingType.All]}),
|
||||
elementDef(NodeFlags.None, null, 4, 'div'),
|
||||
providerDef(NodeFlags.None, null, 1, QueryService, []),
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.All}),
|
||||
aServiceProvider(),
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = view.nodes[1].provider.instance;
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a instanceof QueryList).toBeTruthy();
|
||||
expect(qs.a.toArray()).toEqual([
|
||||
view.nodes[2].provider.instance,
|
||||
view.nodes[3].provider.instance,
|
||||
asProviderData(view, 3).instance,
|
||||
asProviderData(view, 4).instance,
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -314,18 +315,17 @@ export function main() {
|
|||
}
|
||||
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, 3, 'div'),
|
||||
providerDef(
|
||||
NodeFlags.None, null, QueryService, [], null, null,
|
||||
{'a': ['query1', QueryBindingType.First]}),
|
||||
elementDef(NodeFlags.None, null, 4, 'div'),
|
||||
providerDef(NodeFlags.None, null, 1, QueryService, []),
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||
aServiceProvider(),
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = view.nodes[1].provider.instance;
|
||||
expect(qs.a).toBe(view.nodes[2].provider.instance);
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a).toBe(asProviderData(view, 3).instance);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -336,16 +336,15 @@ export function main() {
|
|||
}
|
||||
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, [['query1', QueryValueType.ElementRef]], 1, 'div'),
|
||||
providerDef(
|
||||
NodeFlags.None, null, QueryService, [], null, null,
|
||||
{'a': ['query1', QueryBindingType.First]}),
|
||||
elementDef(NodeFlags.None, [['query1', QueryValueType.ElementRef]], 2, 'div'),
|
||||
providerDef(NodeFlags.None, null, 1, QueryService, []),
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = view.nodes[1].provider.instance;
|
||||
expect(qs.a.nativeElement).toBe(view.nodes[0].elementOrText.node);
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.nativeElement).toBe(asElementData(view, 0).renderElement);
|
||||
});
|
||||
|
||||
it('should query TemplateRef', () => {
|
||||
|
@ -355,16 +354,15 @@ export function main() {
|
|||
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
anchorDef(
|
||||
NodeFlags.None, [['query1', QueryValueType.TemplateRef]], 1,
|
||||
NodeFlags.None, [['query1', QueryValueType.TemplateRef]], 2,
|
||||
viewDef(ViewFlags.None, [anchorDef(NodeFlags.None, null, 0)])),
|
||||
providerDef(
|
||||
NodeFlags.None, null, QueryService, [], null, null,
|
||||
{'a': ['query1', QueryBindingType.First]}),
|
||||
providerDef(NodeFlags.None, null, 1, QueryService, []),
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = view.nodes[1].provider.instance;
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.createEmbeddedView).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -374,15 +372,14 @@ export function main() {
|
|||
}
|
||||
|
||||
const {view} = createAndGetRootNodes(compViewDef([
|
||||
anchorDef(NodeFlags.None, [['query1', QueryValueType.ViewContainerRef]], 1),
|
||||
providerDef(
|
||||
NodeFlags.None, null, QueryService, [], null, null,
|
||||
{'a': ['query1', QueryBindingType.First]}),
|
||||
anchorDef(NodeFlags.None, [['query1', QueryValueType.ViewContainerRef]], 2),
|
||||
providerDef(NodeFlags.None, null, 1, QueryService, []),
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = view.nodes[1].provider.instance;
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.createEmbeddedView).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -124,7 +124,7 @@ export function main() {
|
|||
it('should calculate childFlags for one level', () => {
|
||||
const vd = viewDef(ViewFlags.None, [
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.AfterContentChecked, null, AService, [])
|
||||
providerDef(NodeFlags.AfterContentChecked, null, 0, AService, [])
|
||||
]);
|
||||
|
||||
expect(childFlags(vd)).toEqual([NodeFlags.AfterContentChecked, NodeFlags.None]);
|
||||
|
@ -133,7 +133,7 @@ export function main() {
|
|||
it('should calculate childFlags for two levels', () => {
|
||||
const vd = viewDef(ViewFlags.None, [
|
||||
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.AfterContentChecked, null, AService, [])
|
||||
providerDef(NodeFlags.AfterContentChecked, null, 0, AService, [])
|
||||
]);
|
||||
|
||||
expect(childFlags(vd)).toEqual([
|
||||
|
@ -144,10 +144,10 @@ export function main() {
|
|||
it('should calculate childFlags for one level, multiple roots', () => {
|
||||
const vd = viewDef(ViewFlags.None, [
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.AfterContentChecked, null, AService, []),
|
||||
providerDef(NodeFlags.AfterContentChecked, null, 0, AService, []),
|
||||
elementDef(NodeFlags.None, null, 2, 'span'),
|
||||
providerDef(NodeFlags.AfterContentInit, null, AService, []),
|
||||
providerDef(NodeFlags.AfterViewChecked, null, AService, []),
|
||||
providerDef(NodeFlags.AfterContentInit, null, 0, AService, []),
|
||||
providerDef(NodeFlags.AfterViewChecked, null, 0, AService, []),
|
||||
]);
|
||||
|
||||
expect(childFlags(vd)).toEqual([
|
||||
|
@ -160,10 +160,10 @@ export function main() {
|
|||
const vd = viewDef(ViewFlags.None, [
|
||||
elementDef(NodeFlags.None, null, 2, 'span'),
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.AfterContentChecked, null, AService, []),
|
||||
providerDef(NodeFlags.AfterContentChecked, null, 0, AService, []),
|
||||
elementDef(NodeFlags.None, null, 2, 'span'),
|
||||
providerDef(NodeFlags.AfterContentInit, null, AService, []),
|
||||
providerDef(NodeFlags.AfterViewInit, null, AService, []),
|
||||
providerDef(NodeFlags.AfterContentInit, null, 0, AService, []),
|
||||
providerDef(NodeFlags.AfterViewInit, null, 0, AService, []),
|
||||
]);
|
||||
|
||||
expect(childFlags(vd)).toEqual([
|
||||
|
@ -181,7 +181,7 @@ export function main() {
|
|||
it('should calculate childMatchedQueries for one level', () => {
|
||||
const vd = viewDef(ViewFlags.None, [
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], AService, [])
|
||||
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, [])
|
||||
]);
|
||||
|
||||
expect(childMatchedQueries(vd)).toEqual([['q1'], []]);
|
||||
|
@ -190,7 +190,7 @@ export function main() {
|
|||
it('should calculate childMatchedQueries for two levels', () => {
|
||||
const vd = viewDef(ViewFlags.None, [
|
||||
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], AService, [])
|
||||
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, [])
|
||||
]);
|
||||
|
||||
expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], []]);
|
||||
|
@ -199,10 +199,10 @@ export function main() {
|
|||
it('should calculate childMatchedQueries for one level, multiple roots', () => {
|
||||
const vd = viewDef(ViewFlags.None, [
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], AService, []),
|
||||
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, []),
|
||||
elementDef(NodeFlags.None, null, 2, 'span'),
|
||||
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], AService, []),
|
||||
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], AService, []),
|
||||
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], 0, AService, []),
|
||||
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], 0, AService, []),
|
||||
]);
|
||||
|
||||
expect(childMatchedQueries(vd)).toEqual([['q1'], [], ['q2', 'q3'], [], []]);
|
||||
|
@ -212,10 +212,10 @@ export function main() {
|
|||
const vd = viewDef(ViewFlags.None, [
|
||||
elementDef(NodeFlags.None, null, 2, 'span'),
|
||||
elementDef(NodeFlags.None, null, 1, 'span'),
|
||||
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], AService, []),
|
||||
providerDef(NodeFlags.None, [['q1', QueryValueType.Provider]], 0, AService, []),
|
||||
elementDef(NodeFlags.None, null, 2, 'span'),
|
||||
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], AService, []),
|
||||
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], AService, []),
|
||||
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], 0, AService, []),
|
||||
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], 0, AService, []),
|
||||
]);
|
||||
|
||||
expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], [], ['q2', 'q3'], [], []]);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {NgIf} from '@angular/common';
|
||||
import {Component, NgModule, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||
import {BindingType, DefaultServices, NodeFlags, NodeUpdater, ViewData, ViewDefinition, ViewFlags, anchorDef, checkAndUpdateView, createRootView, elementDef, providerDef, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {BindingType, DefaultServices, NodeFlags, NodeUpdater, ViewData, ViewDefinition, ViewFlags, anchorDef, asElementData, asProviderData, checkAndUpdateView, createRootView, elementDef, providerDef, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {DomSanitizer, DomSanitizerImpl, SafeStyle} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
||||
|
||||
import {TreeNode, emptyTree} from '../util';
|
||||
|
@ -25,7 +25,7 @@ let viewFlags = ViewFlags.DirectDom;
|
|||
|
||||
const TreeComponent_Host: ViewDefinition = viewDef(viewFlags, [
|
||||
elementDef(NodeFlags.None, null, 1, 'tree'),
|
||||
providerDef(NodeFlags.None, null, TreeComponent, [], null, null, null, () => TreeComponent_0),
|
||||
providerDef(NodeFlags.None, null, 0, TreeComponent, [], null, null, () => TreeComponent_0),
|
||||
]);
|
||||
|
||||
const TreeComponent_1: ViewDefinition = viewDef(
|
||||
|
@ -33,7 +33,7 @@ const TreeComponent_1: ViewDefinition = viewDef(
|
|||
[
|
||||
elementDef(NodeFlags.None, null, 1, 'tree'),
|
||||
providerDef(
|
||||
NodeFlags.None, null, TreeComponent, [], {data: [0, 'data']}, null, null,
|
||||
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null,
|
||||
() => TreeComponent_0),
|
||||
],
|
||||
(updater: NodeUpdater, view: ViewData) => {
|
||||
|
@ -46,7 +46,7 @@ const TreeComponent_2: ViewDefinition = viewDef(
|
|||
[
|
||||
elementDef(NodeFlags.None, null, 1, 'tree'),
|
||||
providerDef(
|
||||
NodeFlags.None, null, TreeComponent, [], {data: [0, 'data']}, null, null,
|
||||
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null,
|
||||
() => TreeComponent_0),
|
||||
],
|
||||
(updater: NodeUpdater, view: ViewData) => {
|
||||
|
@ -62,9 +62,11 @@ const TreeComponent_0: ViewDefinition = viewDef(
|
|||
[[BindingType.ElementStyle, 'backgroundColor', null]]),
|
||||
textDef([' ', ' ']),
|
||||
anchorDef(NodeFlags.HasEmbeddedViews, null, 1, TreeComponent_1),
|
||||
providerDef(NodeFlags.None, null, NgIf, [ViewContainerRef, TemplateRef], {ngIf: [0, 'ngIf']}),
|
||||
providerDef(
|
||||
NodeFlags.None, null, 0, NgIf, [ViewContainerRef, TemplateRef], {ngIf: [0, 'ngIf']}),
|
||||
anchorDef(NodeFlags.HasEmbeddedViews, null, 1, TreeComponent_2),
|
||||
providerDef(NodeFlags.None, null, NgIf, [ViewContainerRef, TemplateRef], {ngIf: [0, 'ngIf']}),
|
||||
providerDef(
|
||||
NodeFlags.None, null, 0, NgIf, [ViewContainerRef, TemplateRef], {ngIf: [0, 'ngIf']}),
|
||||
],
|
||||
(updater: NodeUpdater, view: ViewData) => {
|
||||
const cmp = view.component;
|
||||
|
@ -87,8 +89,8 @@ export class AppModule {
|
|||
}
|
||||
bootstrap() {
|
||||
this.rootView = createRootView(new DefaultServices(null, this.sanitizer), TreeComponent_Host);
|
||||
this.rootComp = this.rootView.nodes[1].provider.instance;
|
||||
this.rootEl = this.rootView.nodes[0].elementOrText.node;
|
||||
this.rootComp = asProviderData(this.rootView, 1).instance;
|
||||
this.rootEl = asElementData(this.rootView, 0).renderElement;
|
||||
}
|
||||
tick() { checkAndUpdateView(this.rootView); }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue