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:
Tobias Bosch 2017-01-25 13:45:07 -08:00 committed by Miško Hevery
parent 7ad616a177
commit f802194c18
18 changed files with 668 additions and 559 deletions

View File

@ -8,7 +8,7 @@
import {SecurityContext} from '../security'; 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'; import {checkAndUpdateBinding, setBindingDebugInfo} from './util';
export function anchorDef( export function anchorDef(
@ -28,16 +28,22 @@ export function anchorDef(
childMatchedQueries: undefined, childMatchedQueries: undefined,
bindingIndex: undefined, bindingIndex: undefined,
disposableIndex: undefined, disposableIndex: undefined,
providerIndices: undefined,
// regular values // regular values
flags, flags,
matchedQueries: matchedQueryDefs, childCount, matchedQueries: matchedQueryDefs, childCount,
bindings: [], bindings: [],
disposableCount: 0, 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, provider: undefined,
text: undefined, text: undefined,
pureExpression: undefined pureExpression: undefined,
query: undefined,
}; };
} }
@ -95,21 +101,29 @@ export function elementDef(
childMatchedQueries: undefined, childMatchedQueries: undefined,
bindingIndex: undefined, bindingIndex: undefined,
disposableIndex: undefined, disposableIndex: undefined,
providerIndices: undefined,
// regular values // regular values
flags, flags,
matchedQueries: matchedQueryDefs, childCount, matchedQueries: matchedQueryDefs, childCount,
bindings: bindingDefs, bindings: bindingDefs,
disposableCount: outputDefs.length, 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, provider: undefined,
text: undefined, text: undefined,
pureExpression: undefined pureExpression: undefined,
query: undefined,
}; };
} }
export function createElement(view: ViewData, renderHost: any, def: NodeDef): NodeData { export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
const parentNode = def.parent != null ? view.nodes[def.parent].elementOrText.node : renderHost; const parentNode =
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
const elDef = def.element; const elDef = def.element;
let el: any; let el: any;
if (view.renderer) { if (view.renderer) {
@ -162,13 +176,9 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): No
} }
} }
return { return {
elementOrText: { renderElement: el,
node: el, embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined,
embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined, projectedViews: 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 binding = def.bindings[bindingIdx];
const name = binding.name; const name = binding.name;
const renderNode = view.nodes[def.index].elementOrText.node; const renderNode = asElementData(view, def.index).renderElement;
switch (binding.type) { switch (binding.type) {
case BindingType.ElementAttribute: case BindingType.ElementAttribute:
setElementAttribute(view, binding, renderNode, name, value); setElementAttribute(view, binding, renderNode, name, value);

View File

@ -9,6 +9,7 @@
export {anchorDef, elementDef} from './element'; export {anchorDef, elementDef} from './element';
export {providerDef} from './provider'; export {providerDef} from './provider';
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression'; export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
export {queryDef} from './query';
export {textDef} from './text'; export {textDef} from './text';
export {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, viewDef} from './view'; export {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, viewDef} from './view';
export {attachEmbeddedView, detachEmbeddedView, rootRenderNodes} from './view_attach'; export {attachEmbeddedView, detachEmbeddedView, rootRenderNodes} from './view_attach';

View File

@ -10,14 +10,13 @@ import {SimpleChange, SimpleChanges} from '../change_detection/change_detection'
import {Injector} from '../di'; import {Injector} from '../di';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
import {ElementRef} from '../linker/element_ref'; import {ElementRef} from '../linker/element_ref';
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
import {QueryList} from '../linker/query_list';
import {TemplateRef} from '../linker/template_ref'; import {TemplateRef} from '../linker/template_ref';
import {ViewContainerRef} from '../linker/view_container_ref'; import {ViewContainerRef} from '../linker/view_container_ref';
import {Renderer} from '../render/api'; 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 {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, declaredViewContainer, setBindingDebugInfo} from './util'; import {checkAndUpdateBinding, checkAndUpdateBindingWithChange, setBindingDebugInfo} from './util';
const _tokenKeyCache = new Map<any, string>(); const _tokenKeyCache = new Map<any, string>();
@ -27,11 +26,9 @@ const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
const TemplateRefTokenKey = tokenKey(TemplateRef); const TemplateRefTokenKey = tokenKey(TemplateRef);
export function providerDef( 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]}, deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
outputs?: {[name: string]: string}, outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef {
contentQueries?: {[name: string]: [string, QueryBindingType]}, component?: () => ViewDefinition,
viewQueries?: {[name: string]: [string, QueryBindingType]}, ): NodeDef {
const matchedQueryDefs: {[queryId: string]: QueryValueType} = {}; const matchedQueryDefs: {[queryId: string]: QueryValueType} = {};
if (matchedQueries) { if (matchedQueries) {
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; }); matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
@ -66,26 +63,9 @@ export function providerDef(
} }
return {flags, token, tokenKey: tokenKey(token)}; 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) { if (component) {
flags = flags | NodeFlags.HasComponent; flags = flags | NodeFlags.HasComponent;
} }
if (contentQueryDefs.length) {
flags = flags | NodeFlags.HasContentQuery;
}
if (viewQueryDefs.length) {
flags = flags | NodeFlags.HasViewQuery;
}
return { return {
type: NodeType.Provider, type: NodeType.Provider,
@ -97,23 +77,15 @@ export function providerDef(
childMatchedQueries: undefined, childMatchedQueries: undefined,
bindingIndex: undefined, bindingIndex: undefined,
disposableIndex: undefined, disposableIndex: undefined,
providerIndices: undefined,
// regular values // regular values
flags, flags,
matchedQueries: matchedQueryDefs, matchedQueries: matchedQueryDefs, childCount, bindings,
childCount: 0, bindings,
disposableCount: outputDefs.length, disposableCount: outputDefs.length,
element: undefined, element: undefined,
provider: { provider: {tokenKey: tokenKey(ctor), ctor, deps: depDefs, outputs: outputDefs, component},
tokenKey: tokenKey(ctor),
ctor,
deps: depDefs,
outputs: outputDefs,
contentQueries: contentQueryDefs,
viewQueries: viewQueryDefs, component
},
text: undefined, text: undefined,
pureExpression: undefined, pureExpression: undefined,
query: undefined
}; };
} }
@ -126,7 +98,8 @@ export function tokenKey(token: any): string {
return key; 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 providerDef = def.provider;
const provider = createInstance(view, def.parent, providerDef.ctor, providerDef.deps); const provider = createInstance(view, def.parent, providerDef.ctor, providerDef.deps);
if (providerDef.outputs.length) { 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); view.disposables[def.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
} }
} }
let queries: {[queryId: string]: QueryList<any>}; return {instance: provider, componentView: componentView};
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,
};
} }
export function checkAndUpdateProviderInline( export function checkAndUpdateProviderInline(
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v7: any, v8: any, v9: 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; let changes: SimpleChanges;
// Note: fallthrough is intended! // Note: fallthrough is intended!
switch (def.bindings.length) { switch (def.bindings.length) {
@ -196,7 +153,7 @@ export function checkAndUpdateProviderInline(
} }
export function checkAndUpdateProviderDynamic(view: ViewData, def: NodeDef, values: any[]) { 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; let changes: SimpleChanges;
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
changes = checkAndUpdateProp(view, provider, def, i, values[i], changes); changes = checkAndUpdateProp(view, provider, def, i, values[i], changes);
@ -265,15 +222,15 @@ export function resolveDep(
return Injector.NULL.get(depDef.token, notFoundValue); return Injector.NULL.get(depDef.token, notFoundValue);
} }
case ElementRefTokenKey: case ElementRefTokenKey:
return new ElementRef(view.nodes[elIndex].elementOrText.node); return new ElementRef(asElementData(view, elIndex).renderElement);
case ViewContainerRefTokenKey: case ViewContainerRefTokenKey:
return view.services.createViewContainerRef(view.nodes[elIndex]); return view.services.createViewContainerRef(asElementData(view, elIndex));
case TemplateRefTokenKey: case TemplateRefTokenKey:
return view.services.createTemplateRef(view, elDef); return view.services.createTemplateRef(view, elDef);
default: default:
const providerIndex = elDef.providerIndices[tokenKey]; const providerIndex = elDef.element.providerIndices[tokenKey];
if (providerIndex != null) { if (providerIndex != null) {
return view.nodes[providerIndex].provider.instance; return asProviderData(view, providerIndex).instance;
} }
} }
elIndex = view.parentDiIndex; elIndex = view.parentDiIndex;
@ -303,7 +260,8 @@ function checkAndUpdateProp(
if (view.def.flags & ViewFlags.LogBindingUpdate) { if (view.def.flags & ViewFlags.LogBindingUpdate) {
setBindingDebugInfo( setBindingDebugInfo(
view.renderer, view.nodes[def.parent].elementOrText.node, binding.nonMinifiedName, value); view.renderer, asElementData(view, def.parent).renderElement, binding.nonMinifiedName,
value);
} }
if (change) { if (change) {
changes = changes || {}; changes = changes || {};
@ -313,153 +271,6 @@ function checkAndUpdateProp(
return changes; 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) { export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: NodeFlags) {
if (!(view.def.nodeFlags & lifecycles)) { if (!(view.def.nodeFlags & lifecycles)) {
return; return;
@ -471,7 +282,7 @@ export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: Node
const nodeIndex = nodeDef.index; const nodeIndex = nodeDef.index;
if (nodeDef.flags & lifecycles) { if (nodeDef.flags & lifecycles) {
// a leaf // 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) { } else if ((nodeDef.childFlags & lifecycles) === 0) {
// a parent with leafs // a parent with leafs
// no child matches one of the lifecycles, // no child matches one of the lifecycles,

View File

@ -7,7 +7,7 @@
*/ */
import {resolveDep, tokenKey} from './provider'; 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'; import {checkAndUpdateBinding} from './util';
export function purePipeDef(pipeToken: any, argCount: number): NodeDef { export function purePipeDef(pipeToken: any, argCount: number): NodeDef {
@ -47,7 +47,6 @@ function _pureExpressionDef(
childMatchedQueries: undefined, childMatchedQueries: undefined,
bindingIndex: undefined, bindingIndex: undefined,
disposableIndex: undefined, disposableIndex: undefined,
providerIndices: undefined,
// regular values // regular values
flags: 0, flags: 0,
matchedQueries: {}, matchedQueries: {},
@ -56,15 +55,16 @@ function _pureExpressionDef(
element: undefined, element: undefined,
provider: undefined, provider: undefined,
text: 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 ? const pipe = def.pureExpression.pipeDep ?
resolveDep(view, def.parent, def.pureExpression.pipeDep) : resolveDep(view, def.parent, def.pureExpression.pipeDep) :
undefined; undefined;
return {elementOrText: undefined, provider: undefined, pureExpression: {value: undefined, pipe}}; return {value: undefined, pipe};
} }
export function checkAndUpdatePureExpressionInline( export function checkAndUpdatePureExpressionInline(
@ -97,7 +97,7 @@ export function checkAndUpdatePureExpressionInline(
} }
if (changed) { if (changed) {
const data = view.nodes[def.index].pureExpression; const data = asPureExpressionData(view, def.index);
let value: any; let value: any;
switch (def.pureExpression.type) { switch (def.pureExpression.type) {
case PureExpressionType.Array: case PureExpressionType.Array:
@ -202,7 +202,7 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef
} }
} }
if (changed) { if (changed) {
const data = view.nodes[def.index].pureExpression; const data = asPureExpressionData(view, def.index);
let value: any; let value: any;
switch (def.pureExpression.type) { switch (def.pureExpression.type) {
case PureExpressionType.Array: case PureExpressionType.Array:

View File

@ -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;
}

View File

@ -16,7 +16,7 @@ import {EmbeddedViewRef, ViewRef} from '../linker/view_ref';
import {RenderComponentType, Renderer, RootRenderer} from '../render/api'; import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
import {Sanitizer, SecurityContext} from '../security'; 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 {checkAndUpdateView, checkNoChangesView, createEmbeddedView, destroyView} from './view';
import {attachEmbeddedView, detachEmbeddedView, rootRenderNodes} from './view_attach'; import {attachEmbeddedView, detachEmbeddedView, rootRenderNodes} from './view_attach';
@ -31,7 +31,10 @@ export class DefaultServices implements Services {
return this._sanitizer.sanitize(context, value); return this._sanitizer.sanitize(context, value);
} }
// Note: This needs to be here to prevent a cycle in source files. // 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. // Note: This needs to be here to prevent a cycle in source files.
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> { createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> {
return new TemplateRef_(parentView, def); return new TemplateRef_(parentView, def);
@ -39,7 +42,7 @@ export class DefaultServices implements Services {
} }
class ViewContainerRef_ implements ViewContainerRef { class ViewContainerRef_ implements ViewContainerRef {
constructor(private _data: NodeData) {} constructor(private _data: ElementData) {}
get element(): ElementRef { return <ElementRef>unimplemented(); } get element(): ElementRef { return <ElementRef>unimplemented(); }
@ -48,18 +51,16 @@ class ViewContainerRef_ implements ViewContainerRef {
get parentInjector(): Injector { return <Injector>unimplemented(); } get parentInjector(): Injector { return <Injector>unimplemented(); }
clear(): void { clear(): void {
const len = this._data.elementOrText.embeddedViews.length; const len = this._data.embeddedViews.length;
for (let i = len - 1; i >= 0; i--) { for (let i = len - 1; i >= 0; i--) {
const view = detachEmbeddedView(this._data, i); const view = detachEmbeddedView(this._data, i);
destroyView(view); destroyView(view);
} }
} }
get(index: number): ViewRef { get(index: number): ViewRef { return new ViewRef_(this._data.embeddedViews[index]); }
return new ViewRef_(this._data.elementOrText.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): createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number):
EmbeddedViewRef<C> { EmbeddedViewRef<C> {
@ -83,7 +84,7 @@ class ViewContainerRef_ implements ViewContainerRef {
move(viewRef: ViewRef, currentIndex: number): ViewRef { return unimplemented(); } move(viewRef: ViewRef, currentIndex: number): ViewRef { return unimplemented(); }
indexOf(viewRef: ViewRef): number { 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 { remove(index?: number): void {
@ -128,6 +129,6 @@ class TemplateRef_ implements TemplateRef<any> {
} }
get elementRef(): ElementRef { get elementRef(): ElementRef {
return new ElementRef(this._parentView.nodes[this._def.index].elementOrText.node); return new ElementRef(asElementData(this._parentView, this._def.index).renderElement);
} }
} }

View File

@ -8,7 +8,7 @@
import {looseIdentical} from '../facade/lang'; 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'; import {checkAndUpdateBinding} from './util';
export function textDef(constants: string[]): NodeDef { export function textDef(constants: string[]): NodeDef {
@ -32,7 +32,6 @@ export function textDef(constants: string[]): NodeDef {
childMatchedQueries: undefined, childMatchedQueries: undefined,
bindingIndex: undefined, bindingIndex: undefined,
disposableIndex: undefined, disposableIndex: undefined,
providerIndices: undefined,
// regular values // regular values
flags: 0, flags: 0,
matchedQueries: {}, matchedQueries: {},
@ -41,12 +40,14 @@ export function textDef(constants: string[]): NodeDef {
element: undefined, element: undefined,
provider: undefined, provider: undefined,
text: {prefix: constants[0]}, text: {prefix: constants[0]},
pureExpression: undefined pureExpression: undefined,
query: undefined,
}; };
} }
export function createText(view: ViewData, renderHost: any, def: NodeDef): NodeData { export function createText(view: ViewData, renderHost: any, def: NodeDef): TextData {
const parentNode = def.parent != null ? view.nodes[def.parent].elementOrText.node : renderHost; const parentNode =
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
let renderNode: any; let renderNode: any;
if (view.renderer) { if (view.renderer) {
renderNode = view.renderer.createText(parentNode, def.text.prefix); 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); parentNode.appendChild(renderNode);
} }
} }
return { return {renderText: renderNode};
elementOrText: {node: renderNode, embeddedViews: undefined, projectedViews: undefined},
provider: undefined,
pureExpression: undefined
};
} }
export function checkAndUpdateTextInline( export function checkAndUpdateTextInline(
@ -118,7 +115,7 @@ export function checkAndUpdateTextInline(
value = _addInterpolationPart(v0, bindings[0]) + value; value = _addInterpolationPart(v0, bindings[0]) + value;
} }
value = def.text.prefix + value; value = def.text.prefix + value;
const renderNode = view.nodes[def.index].elementOrText.node; const renderNode = asTextData(view, def.index).renderText;
if (view.renderer) { if (view.renderer) {
view.renderer.setText(renderNode, value); view.renderer.setText(renderNode, value);
} else { } else {
@ -143,7 +140,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
value = value + _addInterpolationPart(values[i], bindings[i]); value = value + _addInterpolationPart(values[i], bindings[i]);
} }
value = def.text.prefix + value; value = def.text.prefix + value;
const renderNode = view.nodes[def.index].elementOrText.node; const renderNode = asTextData(view, def.index).renderText;
if (view.renderer) { if (view.renderer) {
view.renderer.setText(renderNode, value); view.renderer.setText(renderNode, value);
} else { } else {

View File

@ -34,7 +34,7 @@ export interface ViewDefinition {
* Especially providers are after elements / anchros. * Especially providers are after elements / anchros.
*/ */
reverseChildNodes: NodeDef[]; reverseChildNodes: NodeDef[];
lastRootNode: number; lastRootNode: NodeDef;
bindingCount: number; bindingCount: number;
disposableCount: number; disposableCount: number;
/** /**
@ -65,6 +65,12 @@ export enum ViewFlags {
DirectDom = 1 << 1 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 { export interface NodeDef {
type: NodeType; type: NodeType;
index: number; index: number;
@ -76,7 +82,6 @@ export interface NodeDef {
/** aggregated NodeFlags for all children **/ /** aggregated NodeFlags for all children **/
childFlags: NodeFlags; childFlags: NodeFlags;
providerIndices: {[tokenKey: string]: number};
bindingIndex: number; bindingIndex: number;
bindings: BindingDef[]; bindings: BindingDef[];
disposableIndex: number; disposableIndex: number;
@ -94,6 +99,7 @@ export interface NodeDef {
provider: ProviderDef; provider: ProviderDef;
text: TextDef; text: TextDef;
pureExpression: PureExpressionDef; pureExpression: PureExpressionDef;
query: QueryDef;
} }
export enum NodeType { export enum NodeType {
@ -101,6 +107,7 @@ export enum NodeType {
Text, Text,
Provider, Provider,
PureExpression, PureExpression,
Query,
} }
/** /**
@ -122,11 +129,43 @@ export enum NodeFlags {
HasViewQuery = 1 << 11, 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 { export interface ElementDef {
name: string; name: string;
attrs: {[name: string]: string}; attrs: {[name: string]: string};
outputs: ElementOutputDef[]; outputs: ElementOutputDef[];
template: ViewDefinition; 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 { export interface ElementOutputDef {
@ -134,6 +173,21 @@ export interface ElementOutputDef {
eventName: string; 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 * Bitmask for DI flags
*/ */
@ -142,46 +196,11 @@ export enum DepFlags {
SkipSelf = 1 << 0 SkipSelf = 1 << 0
} }
export interface DepDef {
flags: DepFlags;
token: any;
tokenKey: string;
}
export interface ProviderOutputDef { export interface ProviderOutputDef {
propName: string; propName: string;
eventName: 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 TextDef { prefix: string; }
export interface PureExpressionDef { export interface PureExpressionDef {
@ -195,22 +214,19 @@ export enum PureExpressionType {
Pipe Pipe
} }
export enum BindingType { export interface QueryDef {
ElementAttribute, id: string;
ElementClass, bindings: QueryBindingDef[];
ElementStyle,
ElementProperty,
ProviderProperty,
Interpolation,
PureExpressionProperty
} }
export interface BindingDef { export interface QueryBindingDef {
type: BindingType; propName: string;
name: string; bindingType: QueryBindingType;
nonMinifiedName: string; }
securityContext: SecurityContext;
suffix: string; export enum QueryBindingType {
First,
All
} }
// ------------------------------------- // -------------------------------------
@ -235,7 +251,12 @@ export interface ViewData {
parent: ViewData; parent: ViewData;
component: any; component: any;
context: 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; firstChange: boolean;
oldValues: any[]; oldValues: any[];
disposables: DisposableFn[]; disposables: DisposableFn[];
@ -245,16 +266,38 @@ export type DisposableFn = () => void;
/** /**
* Node instance data. * 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! * Attention: Adding fields to this is performance sensitive!
*/ */
export interface NodeData { export interface TextData { renderText: any; }
elementOrText: ElementOrTextData;
provider: ProviderData; /**
pureExpression: PureExpressionData; * 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[]; embeddedViews: ViewData[];
// views that have been created from the template // views that have been created from the template
// of this element, // of this element,
@ -263,22 +306,59 @@ export interface ElementOrTextData {
projectedViews: ViewData[]; 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 { export interface ProviderData {
instance: any; instance: any;
componentView: ViewData; 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 { export interface PureExpressionData {
value: any; value: any;
pipe: PipeTransform; 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 { export interface Services {
renderComponent(rcp: RenderComponentType): Renderer; renderComponent(rcp: RenderComponentType): Renderer;
sanitize(context: SecurityContext, value: string): string; sanitize(context: SecurityContext, value: string): string;
// Note: This needs to be here to prevent a cycle in source files. // 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. // Note: This needs to be here to prevent a cycle in source files.
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any>; createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any>;
} }

View File

@ -12,7 +12,7 @@ import {looseIdentical} from '../facade/lang';
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors'; import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
import {Renderer} from '../render/api'; 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( export function setBindingDebugInfo(
renderer: Renderer, renderNode: any, propName: string, value: any) { renderer: Renderer, renderNode: any, propName: string, value: any) {
@ -61,10 +61,19 @@ export function checkAndUpdateBindingWithChange(
return null; return null;
} }
export function declaredViewContainer(view: ViewData) { export function declaredViewContainer(view: ViewData): ElementData {
if (view.parent) { if (view.parent) {
const parentView = view.parent; const parentView = view.parent;
return parentView.nodes[view.parentIndex]; return asElementData(parentView, view.parentIndex);
} }
return undefined; 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;
}
}

View File

@ -10,10 +10,11 @@ import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
import {RenderComponentType, Renderer} from '../render/api'; import {RenderComponentType, Renderer} from '../render/api';
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element'; 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 {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text'; 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'; import {checkBindingNoChanges} from './util';
const NOOP = (): any => undefined; const NOOP = (): any => undefined;
@ -25,6 +26,7 @@ export function viewDef(
if (nodesWithoutIndices.length === 0) { if (nodesWithoutIndices.length === 0) {
throw new Error(`Illegal State: Views without nodes are not allowed!`); throw new Error(`Illegal State: Views without nodes are not allowed!`);
} }
const nodes: NodeDef[] = new Array(nodesWithoutIndices.length); const nodes: NodeDef[] = new Array(nodesWithoutIndices.length);
const reverseChildNodes: NodeDef[] = new Array(nodesWithoutIndices.length); const reverseChildNodes: NodeDef[] = new Array(nodesWithoutIndices.length);
let viewBindingCount = 0; let viewBindingCount = 0;
@ -42,15 +44,21 @@ export function viewDef(
} }
currentParent = newParent; currentParent = newParent;
} }
const nodeWithoutIndices = nodesWithoutIndices[i];
const reverseChildIndex = calculateReverseChildIndex( const reverseChildIndex = calculateReverseChildIndex(
currentParent, i, nodesWithoutIndices[i].childCount, nodesWithoutIndices.length); currentParent, i, nodeWithoutIndices.childCount, nodesWithoutIndices.length);
const node = cloneAndModifyNode(nodesWithoutIndices[i], {
const node = cloneAndModifyNode(nodeWithoutIndices, {
index: i, index: i,
parent: currentParent ? currentParent.index : undefined, parent: currentParent ? currentParent.index : undefined,
bindingIndex: viewBindingCount, bindingIndex: viewBindingCount,
disposableIndex: viewDisposableCount, reverseChildIndex, 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; nodes[i] = node;
reverseChildNodes[reverseChildIndex] = node; reverseChildNodes[reverseChildIndex] = node;
validateNode(currentParent, node); validateNode(currentParent, node);
@ -71,13 +79,11 @@ export function viewDef(
lastRootNode = node; lastRootNode = node;
} }
if (node.provider) { if (node.provider) {
currentParent.providerIndices[node.provider.tokenKey] = i; currentParent.element.providerIndices[node.provider.tokenKey] = i;
for (let k = 0; k < node.provider.contentQueries.length; k++) { }
currentParent.providerIndices[node.provider.contentQueries[k].id] = i; if (node.query) {
} const elementDef = nodes[currentParent.parent];
for (let k = 0; k < node.provider.viewQueries.length; k++) { elementDef.element.providerIndices[node.query.id] = i;
currentParent.providerIndices[node.provider.viewQueries[k].id] = i;
}
} }
if (node.childCount) { if (node.childCount) {
currentParent = node; currentParent = node;
@ -99,8 +105,7 @@ export function viewDef(
update: update || NOOP, update: update || NOOP,
handleEvent: handleEvent || NOOP, componentType, handleEvent: handleEvent || NOOP, componentType,
bindingCount: viewBindingCount, bindingCount: viewBindingCount,
disposableCount: viewDisposableCount, disposableCount: viewDisposableCount, lastRootNode
lastRootNode: lastRootNode.index
}; };
} }
@ -152,8 +157,7 @@ function calculateReverseChildIndex(
function validateNode(parent: NodeDef, node: NodeDef) { function validateNode(parent: NodeDef, node: NodeDef) {
const template = node.element && node.element.template; const template = node.element && node.element.template;
if (template) { if (template) {
if (template.lastRootNode != null && if (template.lastRootNode && template.lastRootNode.flags & NodeFlags.HasEmbeddedViews) {
template.nodes[template.lastRootNode].flags & NodeFlags.HasEmbeddedViews) {
throw new Error( throw new Error(
`Illegal State: Last root node of a template can't have embedded views, at index ${node.index}!`); `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}!`); `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 (node.childCount) {
if (parent) { if (parent) {
const parentEnd = parent.index + parent.childCount; const parentEnd = parent.index + parent.childCount;
@ -182,7 +193,6 @@ function cloneAndModifyNode(nodeDef: NodeDef, values: {
parent: number, parent: number,
bindingIndex: number, bindingIndex: number,
disposableIndex: number, disposableIndex: number,
providerIndices: {[tokenKey: string]: number}
}): NodeDef { }): NodeDef {
const clonedNode: NodeDef = <any>{}; const clonedNode: NodeDef = <any>{};
copyInto(nodeDef, clonedNode); copyInto(nodeDef, clonedNode);
@ -192,13 +202,21 @@ function cloneAndModifyNode(nodeDef: NodeDef, values: {
clonedNode.disposableIndex = values.disposableIndex; clonedNode.disposableIndex = values.disposableIndex;
clonedNode.parent = values.parent; clonedNode.parent = values.parent;
clonedNode.reverseChildIndex = values.reverseChildIndex; clonedNode.reverseChildIndex = values.reverseChildIndex;
clonedNode.providerIndices = values.providerIndices;
// Note: We can't set the value immediately, as we need to walk the children first. // Note: We can't set the value immediately, as we need to walk the children first.
clonedNode.childFlags = 0; clonedNode.childFlags = 0;
clonedNode.childMatchedQueries = {}; clonedNode.childMatchedQueries = {};
return clonedNode; 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 { export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
// embedded views are seen as siblings to the anchor, so we need // embedded views are seen as siblings to the anchor, so we need
// to get the parent of the anchor and use it as parentIndex. // 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: case NodeType.Provider:
let componentView: ViewData; let componentView: ViewData;
if (nodeDef.provider.component) { 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); nodeData = createProvider(view, nodeDef, componentView);
break; break;
case NodeType.PureExpression: case NodeType.PureExpression:
nodeData = createPureExpression(view, nodeDef); nodeData = createPureExpression(view, nodeDef);
break; break;
case NodeType.Query:
nodeData = createQuery();
break;
} }
nodes[i] = nodeData; nodes[i] = nodeData;
} }
@ -272,9 +295,9 @@ function initView(view: ViewData, renderHost: any, component: any, context: any)
export function checkNoChangesView(view: ViewData) { export function checkNoChangesView(view: ViewData) {
view.def.update(CheckNoChanges, view); view.def.update(CheckNoChanges, view);
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges); execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
execContentQueriesAction(view, QueryAction.CheckNoChanges); execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckNoChanges);
execComponentViewsAction(view, ViewAction.CheckNoChanges); execComponentViewsAction(view, ViewAction.CheckNoChanges);
updateViewQueries(view, QueryAction.CheckNoChanges); execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckNoChanges);
} }
const CheckNoChanges: NodeUpdater = { const CheckNoChanges: NodeUpdater = {
@ -305,7 +328,7 @@ const CheckNoChanges: NodeUpdater = {
checkBindingNoChanges(view, nodeDef, 0, v0); checkBindingNoChanges(view, nodeDef, 0, v0);
} }
if (nodeDef.type === NodeType.PureExpression) { if (nodeDef.type === NodeType.PureExpression) {
return view.nodes[index].pureExpression.value; return asPureExpressionData(view, index).value;
} }
return undefined; return undefined;
}, },
@ -315,7 +338,7 @@ const CheckNoChanges: NodeUpdater = {
checkBindingNoChanges(view, nodeDef, i, values[i]); checkBindingNoChanges(view, nodeDef, i, values[i]);
} }
if (nodeDef.type === NodeType.PureExpression) { if (nodeDef.type === NodeType.PureExpression) {
return view.nodes[index].pureExpression.value; return asPureExpressionData(view, index).value;
} }
return undefined; return undefined;
} }
@ -324,12 +347,12 @@ const CheckNoChanges: NodeUpdater = {
export function checkAndUpdateView(view: ViewData) { export function checkAndUpdateView(view: ViewData) {
view.def.update(CheckAndUpdate, view); view.def.update(CheckAndUpdate, view);
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate); execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
execContentQueriesAction(view, QueryAction.CheckAndUpdate); execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckAndUpdate);
callLifecycleHooksChildrenFirst( callLifecycleHooksChildrenFirst(
view, NodeFlags.AfterContentChecked | (view.firstChange ? NodeFlags.AfterContentInit : 0)); view, NodeFlags.AfterContentChecked | (view.firstChange ? NodeFlags.AfterContentInit : 0));
execComponentViewsAction(view, ViewAction.CheckAndUpdate); execComponentViewsAction(view, ViewAction.CheckAndUpdate);
updateViewQueries(view, QueryAction.CheckAndUpdate); execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckAndUpdate);
callLifecycleHooksChildrenFirst( callLifecycleHooksChildrenFirst(
view, NodeFlags.AfterViewChecked | (view.firstChange ? NodeFlags.AfterViewInit : 0)); view, NodeFlags.AfterViewChecked | (view.firstChange ? NodeFlags.AfterViewInit : 0));
@ -352,7 +375,7 @@ const CheckAndUpdate: NodeUpdater = {
return undefined; return undefined;
case NodeType.PureExpression: case NodeType.PureExpression:
checkAndUpdatePureExpressionInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); 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 => { checkDynamic: (view: ViewData, index: number, values: any[]): void => {
@ -369,11 +392,18 @@ const CheckAndUpdate: NodeUpdater = {
return undefined; return undefined;
case NodeType.PureExpression: case NodeType.PureExpression:
checkAndUpdatePureExpressionDynamic(view, nodeDef, values); 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) { export function destroyView(view: ViewData) {
callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy); callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy);
if (view.disposables) { if (view.disposables) {
@ -401,17 +431,16 @@ function execComponentViewsAction(view: ViewData, action: ViewAction) {
const nodeDef = def.nodes[i]; const nodeDef = def.nodes[i];
if (nodeDef.flags & NodeFlags.HasComponent) { if (nodeDef.flags & NodeFlags.HasComponent) {
// a leaf // a leaf
const nodeData = view.nodes[i]; const providerData = asProviderData(view, i);
if (action === ViewAction.InitComponent) { if (action === ViewAction.InitComponent) {
let renderHost = view.nodes[nodeDef.parent].elementOrText.node; let renderHost = asElementData(view, nodeDef.parent).renderElement;
if (view.renderer) { if (view.renderer) {
renderHost = view.renderer.createViewRoot(renderHost); renderHost = view.renderer.createViewRoot(renderHost);
} }
initView( initView(
nodeData.provider.componentView, renderHost, nodeData.provider.instance, providerData.componentView, renderHost, providerData.instance, providerData.instance);
nodeData.provider.instance);
} else { } else {
callViewAction(nodeData.provider.componentView, action); callViewAction(providerData.componentView, action);
} }
} else if ((nodeDef.childFlags & NodeFlags.HasComponent) === 0) { } else if ((nodeDef.childFlags & NodeFlags.HasComponent) === 0) {
// a parent with leafs // a parent with leafs
@ -431,8 +460,7 @@ function execEmbeddedViewsAction(view: ViewData, action: ViewAction) {
const nodeDef = def.nodes[i]; const nodeDef = def.nodes[i];
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) { if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
// a leaf // a leaf
const nodeData = view.nodes[i]; const embeddedViews = asElementData(view, i).embeddedViews;
const embeddedViews = nodeData.elementOrText.embeddedViews;
if (embeddedViews) { if (embeddedViews) {
for (let k = 0; k < embeddedViews.length; k++) { for (let k = 0; k < embeddedViews.length; k++) {
callViewAction(embeddedViews[k], action); callViewAction(embeddedViews[k], action);
@ -460,3 +488,32 @@ function callViewAction(view: ViewData, action: ViewAction) {
break; 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;
}
}
}

View File

@ -6,20 +6,21 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {NodeData, NodeFlags, ViewData} from './types'; import {dirtyParentQuery} from './query';
import {declaredViewContainer} from './util'; 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) { export function attachEmbeddedView(elementData: ElementData, viewIndex: number, view: ViewData) {
let embeddedViews = node.elementOrText.embeddedViews; let embeddedViews = elementData.embeddedViews;
if (viewIndex == null) { if (viewIndex == null) {
viewIndex = embeddedViews.length; viewIndex = embeddedViews.length;
} }
addToArray(embeddedViews, viewIndex, view); addToArray(embeddedViews, viewIndex, view);
const dvc = declaredViewContainer(view); const dvcElementData = declaredViewContainer(view);
if (dvc && dvc !== node) { if (dvcElementData && dvcElementData !== elementData) {
let projectedViews = dvc.elementOrText.projectedViews; let projectedViews = dvcElementData.projectedViews;
if (!projectedViews) { if (!projectedViews) {
projectedViews = dvc.elementOrText.projectedViews = []; projectedViews = dvcElementData.projectedViews = [];
} }
projectedViews.push(view); projectedViews.push(view);
} }
@ -30,8 +31,8 @@ export function attachEmbeddedView(node: NodeData, viewIndex: number, view: View
// update rendering // update rendering
const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null; const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null;
const prevNode = prevView ? prevView.nodes[prevView.def.lastRootNode] : node; const prevRenderNode =
const prevRenderNode = prevNode.elementOrText.node; prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
if (view.renderer) { if (view.renderer) {
view.renderer.attachViewAfter(prevRenderNode, rootRenderNodes(view)); view.renderer.attachViewAfter(prevRenderNode, rootRenderNodes(view));
} else { } else {
@ -44,18 +45,17 @@ export function attachEmbeddedView(node: NodeData, viewIndex: number, view: View
} }
} }
export function detachEmbeddedView(node: NodeData, viewIndex: number): ViewData { export function detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData {
const renderData = node.elementOrText; const embeddedViews = elementData.embeddedViews;
const embeddedViews = renderData.embeddedViews;
if (viewIndex == null) { if (viewIndex == null) {
viewIndex = embeddedViews.length; viewIndex = embeddedViews.length;
} }
const view = embeddedViews[viewIndex]; const view = embeddedViews[viewIndex];
removeFromArray(embeddedViews, viewIndex); removeFromArray(embeddedViews, viewIndex);
const dvc = declaredViewContainer(view); const dvcElementData = declaredViewContainer(view);
if (dvc && dvc !== node) { if (dvcElementData && dvcElementData !== elementData) {
const projectedViews = dvc.elementOrText.projectedViews; const projectedViews = dvcElementData.projectedViews;
removeFromArray(projectedViews, projectedViews.indexOf(view)); removeFromArray(projectedViews, projectedViews.indexOf(view));
} }
@ -67,7 +67,7 @@ export function detachEmbeddedView(node: NodeData, viewIndex: number): ViewData
if (view.renderer) { if (view.renderer) {
view.renderer.detachView(rootRenderNodes(view)); view.renderer.detachView(rootRenderNodes(view));
} else { } else {
const parentNode = renderData.node.parentNode; const parentNode = elementData.renderElement.parentNode;
if (parentNode) { if (parentNode) {
directDomAttachDetachSiblingRenderNodes( directDomAttachDetachSiblingRenderNodes(
view, 0, DirectDomAction.RemoveChild, parentNode, null); 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[] { export function rootRenderNodes(view: ViewData): any[] {
const renderNodes: any[] = []; const renderNodes: any[] = [];
collectSiblingRenderNodes(view, 0, renderNodes); collectSiblingRenderNodes(view, 0, renderNodes);
@ -122,12 +101,12 @@ export function rootRenderNodes(view: ViewData): any[] {
} }
function collectSiblingRenderNodes(view: ViewData, startIndex: number, target: 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 nodeDef = view.def.nodes[i];
const nodeData = view.nodes[i].elementOrText; target.push(renderNode(view, nodeDef));
target.push(nodeData.node);
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) { if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
const embeddedViews = nodeData.embeddedViews; const embeddedViews = asElementData(view, i).embeddedViews;
if (embeddedViews) { if (embeddedViews) {
for (let k = 0; k < embeddedViews.length; k++) { for (let k = 0; k < embeddedViews.length; k++) {
collectSiblingRenderNodes(embeddedViews[k], 0, target); collectSiblingRenderNodes(embeddedViews[k], 0, target);
@ -148,22 +127,23 @@ enum DirectDomAction {
function directDomAttachDetachSiblingRenderNodes( function directDomAttachDetachSiblingRenderNodes(
view: ViewData, startIndex: number, action: DirectDomAction, parentNode: any, view: ViewData, startIndex: number, action: DirectDomAction, parentNode: any,
nextSibling: 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 nodeDef = view.def.nodes[i];
const nodeData = view.nodes[i].elementOrText; const rn = renderNode(view, nodeDef);
switch (action) { switch (action) {
case DirectDomAction.AppendChild: case DirectDomAction.AppendChild:
parentNode.appendChild(nodeData.node); parentNode.appendChild(rn);
break; break;
case DirectDomAction.InsertBefore: case DirectDomAction.InsertBefore:
parentNode.insertBefore(nodeData.node, nextSibling); parentNode.insertBefore(rn, nextSibling);
break; break;
case DirectDomAction.RemoveChild: case DirectDomAction.RemoveChild:
parentNode.removeChild(nodeData.node); parentNode.removeChild(rn);
break; break;
} }
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) { if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
const embeddedViews = nodeData.embeddedViews; const embeddedViews = asElementData(view, i).embeddedViews;
if (embeddedViews) { if (embeddedViews) {
for (let k = 0; k < embeddedViews.length; k++) { for (let k = 0; k < embeddedViews.length; k++) {
directDomAttachDetachSiblingRenderNodes( directDomAttachDetachSiblingRenderNodes(

View File

@ -7,7 +7,7 @@
*/ */
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core'; 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 {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; 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([ const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 1, 'div'),
providerDef( providerDef(
NodeFlags.None, null, AComp, [], null, null, null, NodeFlags.None, null, 0, AComp, [], null, null,
() => compViewDef([ () => compViewDef([
elementDef(NodeFlags.None, null, 0, 'span'), 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.context).toBe(instance);
expect(compView.component).toBe(instance); expect(compView.component).toBe(instance);
@ -81,13 +81,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes( const {view, rootNodes} = createAndGetRootNodes(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'), 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]]), elementDef(NodeFlags.None, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
], update ], update
)), )),
], jasmine.createSpy('parentUpdater'))); ], jasmine.createSpy('parentUpdater')));
const compView = view.nodes[1].provider.componentView; const compView = asProviderData(view, 1).componentView;
checkAndUpdateView(view); checkAndUpdateView(view);
@ -118,10 +118,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef([ const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 1, 'div'),
providerDef( providerDef(
NodeFlags.None, null, AComp, [], null, null, null, NodeFlags.None, null, 0, AComp, [], null, null,
() => compViewDef([ () => compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.OnDestroy, null, ChildProvider, []) providerDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
])), ])),
])); ]));

View File

@ -7,7 +7,7 @@
*/ */
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core'; 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 {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; 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 childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
const rootChildren = getDOM().childNodes(rootNodes[0]); const rootChildren = getDOM().childNodes(rootNodes[0]);
attachEmbeddedView(parentView.nodes[1], 0, childView0); attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
attachEmbeddedView(parentView.nodes[1], 1, childView1); attachEmbeddedView(asElementData(parentView, 1), 1, childView1);
// 2 anchors + 2 elements // 2 anchors + 2 elements
expect(rootChildren.length).toBe(4); expect(rootChildren.length).toBe(4);
expect(getDOM().getAttribute(rootChildren[1], 'name')).toBe('child0'); expect(getDOM().getAttribute(rootChildren[1], 'name')).toBe('child0');
expect(getDOM().getAttribute(rootChildren[2], 'name')).toBe('child1'); expect(getDOM().getAttribute(rootChildren[2], 'name')).toBe('child1');
detachEmbeddedView(parentView.nodes[1], 1); detachEmbeddedView(asElementData(parentView, 1), 1);
detachEmbeddedView(parentView.nodes[1], 0); detachEmbeddedView(asElementData(parentView, 1), 0);
expect(getDOM().childNodes(rootNodes[0]).length).toBe(2); 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]); const childView0 = createEmbeddedView(parentView, parentView.def.nodes[0]);
attachEmbeddedView(parentView.nodes[0], 0, childView0); attachEmbeddedView(asElementData(parentView, 0), 0, childView0);
const rootNodes = rootRenderNodes(parentView); const rootNodes = rootRenderNodes(parentView);
expect(rootNodes.length).toBe(3); 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 childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
const rootEl = rootNodes[0]; const rootEl = rootNodes[0];
attachEmbeddedView(parentView.nodes[1], 0, childView0); attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
checkAndUpdateView(parentView); checkAndUpdateView(parentView);
@ -163,13 +163,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 1, 'div'),
anchorDef(NodeFlags.HasEmbeddedViews, null, 0, embeddedViewDef([ anchorDef(NodeFlags.HasEmbeddedViews, null, 0, embeddedViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), 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]); const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
attachEmbeddedView(parentView.nodes[1], 0, childView0); attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
destroyView(parentView); destroyView(parentView);
expect(log).toEqual(['ngOnDestroy']); expect(log).toEqual(['ngOnDestroy']);

View File

@ -58,7 +58,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
createAndGetRootNodes(compViewDef([ createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, []) providerDef(NodeFlags.None, null, 0, SomeService, [])
])); ]));
expect(instances.length).toBe(1); expect(instances.length).toBe(1);
@ -76,8 +76,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should inject deps from the same element', () => { it('should inject deps from the same element', () => {
createAndGetRootNodes(compViewDef([ createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 2, 'span'), providerDef(NodeFlags.None, null, Dep, []), elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.None, null, SomeService, [Dep]) providerDef(NodeFlags.None, null, 0, Dep, []),
providerDef(NodeFlags.None, null, 0, SomeService, [Dep])
])); ]));
expect(instance.dep instanceof Dep).toBeTruthy(); 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', () => { it('should inject deps from a parent element', () => {
createAndGetRootNodes(compViewDef([ 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'), 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(); 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', () => { it('should not inject deps from sibling root elements', () => {
const nodes = [ const nodes = [
elementDef(NodeFlags.None, null, 1, 'span'), providerDef(NodeFlags.None, null, Dep, []),
elementDef(NodeFlags.None, null, 1, 'span'), 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 // root elements
@ -115,10 +118,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
createAndGetRootNodes(compViewDef([ createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 1, 'div'),
providerDef( providerDef(
NodeFlags.None, null, Dep, [], null, null, null, NodeFlags.None, null, 0, Dep, [], null, null,
() => compViewDef([ () => compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), 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', () => { it('should inject ViewContainerRef', () => {
createAndGetRootNodes(compViewDef([ createAndGetRootNodes(compViewDef([
anchorDef(NodeFlags.HasEmbeddedViews, null, 1), anchorDef(NodeFlags.HasEmbeddedViews, null, 1),
providerDef(NodeFlags.None, null, SomeService, [ViewContainerRef]) providerDef(NodeFlags.None, null, 0, SomeService, [ViewContainerRef])
])); ]));
expect(instance.dep.createEmbeddedView).toBeTruthy(); expect(instance.dep.createEmbeddedView).toBeTruthy();
@ -139,7 +142,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
createAndGetRootNodes(compViewDef([ createAndGetRootNodes(compViewDef([
anchorDef( anchorDef(
NodeFlags.None, null, 1, embeddedViewDef([anchorDef(NodeFlags.None, null, 0)])), 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(); expect(instance.dep.createEmbeddedView).toBeTruthy();
@ -148,7 +151,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
it('should inject ElementRef', () => { it('should inject ElementRef', () => {
createAndGetRootNodes(compViewDef([ createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), 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'); 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', () => { it('should not inject Renderer when using directDom', () => {
expect(() => createAndGetRootNodes(compViewDef([ expect(() => createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [Renderer]) providerDef(NodeFlags.None, null, 0, SomeService, [Renderer])
]))) ])))
.toThrowError('No provider for 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', () => { it('should inject Renderer when not using directDom', () => {
createAndGetRootNodes(compViewDef([ createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.None, null, SomeService, [Renderer]) providerDef(NodeFlags.None, null, 0, SomeService, [Renderer])
])); ]));
expect(instance.dep.createElement).toBeTruthy(); expect(instance.dep.createElement).toBeTruthy();
@ -199,7 +202,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, 1, 'span'), 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)); config.update));
@ -219,7 +222,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, 1, 'span'), 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))); (updater, view) => updater.checkInline(view, 1, propValue)));
@ -254,7 +257,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, 1, 'span'), 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)); null, handleEvent));
@ -292,9 +296,9 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, 3, 'span'), 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'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(allFlags, null, SomeService, [], {a: [0, 'a']}) providerDef(allFlags, null, 0, SomeService, [], {a: [0, 'a']})
], ],
(updater) => { (updater) => {
updater.checkInline(view, 1, 'someValue'); updater.checkInline(view, 1, 'someValue');
@ -351,7 +355,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, 1, 'span'), 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))); (updater) => updater.checkInline(view, 1, currValue)));

View File

@ -7,7 +7,7 @@
*/ */
import {PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core'; 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 {inject} from '@angular/core/testing';
import {INLINE_DYNAMIC_VALUES, InlineDynamic, callUpdater} from './helper'; import {INLINE_DYNAMIC_VALUES, InlineDynamic, callUpdater} from './helper';
@ -46,14 +46,14 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, 2, 'span'), pureArrayDef(2), 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) => { (updater, view) => {
callUpdater( callUpdater(
updater, inlineDynamic, view, 2, updater, inlineDynamic, view, 2,
[callUpdater(updater, inlineDynamic, view, 1, values)]); [callUpdater(updater, inlineDynamic, view, 1, values)]);
})); }));
const service = view.nodes[2].provider.instance; const service = asProviderData(view, 2).instance;
values = [1, 2]; values = [1, 2];
checkAndUpdateView(view); checkAndUpdateView(view);
@ -80,14 +80,14 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, 2, 'span'), pureObjectDef(['a', 'b']), 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) => { (updater, view) => {
callUpdater( callUpdater(
updater, inlineDynamic, view, 2, updater, inlineDynamic, view, 2,
[callUpdater(updater, inlineDynamic, view, 1, values)]); [callUpdater(updater, inlineDynamic, view, 1, values)]);
})); }));
const service = view.nodes[2].provider.instance; const service = asProviderData(view, 2).instance;
values = [1, 2]; values = [1, 2];
checkAndUpdateView(view); checkAndUpdateView(view);
@ -118,15 +118,15 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, 3, 'span'), elementDef(NodeFlags.None, null, 3, 'span'),
providerDef(NodeFlags.None, null, SomePipe, []), purePipeDef(SomePipe, 2), providerDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']}) providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
], ],
(updater, view) => { (updater, view) => {
callUpdater( callUpdater(
updater, inlineDynamic, view, 3, updater, inlineDynamic, view, 3,
[callUpdater(updater, inlineDynamic, view, 2, values)]); [callUpdater(updater, inlineDynamic, view, 2, values)]);
})); }));
const service = view.nodes[3].provider.instance; const service = asProviderData(view, 3).instance;
values = [1, 2]; values = [1, 2];
checkAndUpdateView(view); checkAndUpdateView(view);

View File

@ -7,7 +7,7 @@
*/ */
import {ElementRef, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; 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 {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -45,57 +45,59 @@ export function main() {
a: QueryList<AService>; a: QueryList<AService>;
} }
function contentQueryProvider() { function contentQueryProviders() {
return providerDef( return [
NodeFlags.None, null, QueryService, [], null, null, providerDef(NodeFlags.None, null, 1, QueryService, []),
{'a': ['query1', QueryBindingType.All]}); 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() { function aServiceProvider() {
return providerDef(NodeFlags.None, [['query1', QueryValueType.Provider]], AService, []); return providerDef(NodeFlags.None, [['query1', QueryValueType.Provider]], 0, AService, []);
}
function viewQueryProvider(compView: ViewDefinition) {
return providerDef(
NodeFlags.None, null, QueryService, [], null, null, null, () => compView,
{'a': ['query1', QueryBindingType.All]});
} }
describe('content queries', () => { describe('content queries', () => {
it('should query providers on the same element and child elements', () => { it('should query providers on the same element and child elements', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 4, 'div'), elementDef(NodeFlags.None, null, 5, 'div'),
contentQueryProvider(), ...contentQueryProviders(),
aServiceProvider(), aServiceProvider(),
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])); ]));
const qs: QueryService = view.nodes[1].provider.instance; const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a).toBeUndefined(); expect(qs.a).toBeUndefined();
checkAndUpdateView(view); checkAndUpdateView(view);
const as = qs.a.toArray(); const as = qs.a.toArray();
expect(as.length).toBe(2); expect(as.length).toBe(2);
expect(as[0]).toBe(view.nodes[2].provider.instance); expect(as[0]).toBe(asProviderData(view, 3).instance);
expect(as[1]).toBe(view.nodes[4].provider.instance); expect(as[1]).toBe(asProviderData(view, 5).instance);
}); });
it('should not query providers on sibling or parent elements', () => { it('should not query providers on sibling or parent elements', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 5, 'div'), elementDef(NodeFlags.None, null, 6, 'div'),
aServiceProvider(), aServiceProvider(),
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 2, 'div'),
contentQueryProvider(), ...contentQueryProviders(),
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])); ]));
checkAndUpdateView(view); checkAndUpdateView(view);
const qs: QueryService = view.nodes[3].provider.instance; const qs: QueryService = asProviderData(view, 3).instance;
expect(qs.a.length).toBe(0); expect(qs.a.length).toBe(0);
}); });
}); });
@ -103,8 +105,8 @@ export function main() {
describe('view queries', () => { describe('view queries', () => {
it('should query providers in the view', () => { it('should query providers in the view', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 2, 'div'),
viewQueryProvider(compViewDef([ ...viewQueryProviders(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
aServiceProvider(), aServiceProvider(),
])), ])),
@ -112,23 +114,23 @@ export function main() {
checkAndUpdateView(view); checkAndUpdateView(view);
const comp: QueryService = view.nodes[1].provider.instance; const comp: QueryService = asProviderData(view, 1).instance;
const compView = view.nodes[1].provider.componentView; const compView = asProviderData(view, 1).componentView;
expect(comp.a.length).toBe(1); 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', () => { it('should not query providers on the host element', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 2, 'div'), elementDef(NodeFlags.None, null, 3, 'div'),
viewQueryProvider(compViewDef([ ...viewQueryProviders(compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
])), ])),
aServiceProvider(), aServiceProvider(),
])); ]));
checkAndUpdateView(view); checkAndUpdateView(view);
const comp: QueryService = view.nodes[1].provider.instance; const comp: QueryService = asProviderData(view, 1).instance;
expect(comp.a.length).toBe(0); expect(comp.a.length).toBe(0);
}); });
}); });
@ -136,37 +138,37 @@ export function main() {
describe('embedded views', () => { describe('embedded views', () => {
it('should query providers in embedded views', () => { it('should query providers in embedded views', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'), elementDef(NodeFlags.None, null, 5, 'div'),
contentQueryProvider(), ...contentQueryProviders(),
anchorDef( anchorDef(
NodeFlags.HasEmbeddedViews, null, 1, viewDef( NodeFlags.HasEmbeddedViews, null, 2, viewDef(
ViewFlags.None, ViewFlags.None,
[ [
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])), ])),
contentQueryProvider(), ...contentQueryProviders(),
])); ]));
const childView = createEmbeddedView(view, view.def.nodes[2]); const childView = createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(view.nodes[2], 0, childView); attachEmbeddedView(asElementData(view, 3), 0, childView);
checkAndUpdateView(view); checkAndUpdateView(view);
// queries on parent elements of anchors // 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.length).toBe(1);
expect(qs1.a.first instanceof AService).toBe(true); expect(qs1.a.first instanceof AService).toBe(true);
// queries on the anchor // 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.length).toBe(1);
expect(qs2.a.first instanceof AService).toBe(true); expect(qs2.a.first instanceof AService).toBe(true);
}); });
it('should query providers in embedded views only at the template declaration', () => { it('should query providers in embedded views only at the template declaration', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 2, 'div'), elementDef(NodeFlags.None, null, 3, 'div'),
contentQueryProvider(), ...contentQueryProviders(),
anchorDef( anchorDef(
NodeFlags.HasEmbeddedViews, null, 0, viewDef( NodeFlags.HasEmbeddedViews, null, 0, viewDef(
ViewFlags.None, ViewFlags.None,
@ -174,31 +176,31 @@ export function main() {
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])), ])),
elementDef(NodeFlags.None, null, 2, 'div'), elementDef(NodeFlags.None, null, 3, 'div'),
contentQueryProvider(), ...contentQueryProviders(),
anchorDef(NodeFlags.HasEmbeddedViews, null, 0), 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 // 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); checkAndUpdateView(view);
// query on the declaration place // 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.length).toBe(1);
expect(qs1.a.first instanceof AService).toBe(true); expect(qs1.a.first instanceof AService).toBe(true);
// query on the attach place // 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); expect(qs2.a.length).toBe(0);
}); });
it('should checkNoChanges', () => { it('should checkNoChanges', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'), elementDef(NodeFlags.None, null, 4, 'div'),
contentQueryProvider(), ...contentQueryProviders(),
anchorDef( anchorDef(
NodeFlags.HasEmbeddedViews, null, 1, viewDef( NodeFlags.HasEmbeddedViews, null, 1, viewDef(
ViewFlags.None, ViewFlags.None,
@ -211,18 +213,18 @@ export function main() {
checkAndUpdateView(view); checkAndUpdateView(view);
checkNoChangesView(view); checkNoChangesView(view);
const childView = createEmbeddedView(view, view.def.nodes[2]); const childView = createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(view.nodes[2], 0, childView); attachEmbeddedView(asElementData(view, 3), 0, childView);
expect(() => checkNoChangesView(view)) expect(() => checkNoChangesView(view))
.toThrowError( .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', () => { it('should update content queries if embedded views are added or removed', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 2, 'div'), elementDef(NodeFlags.None, null, 3, 'div'),
contentQueryProvider(), ...contentQueryProviders(),
anchorDef( anchorDef(
NodeFlags.HasEmbeddedViews, null, 0, viewDef( NodeFlags.HasEmbeddedViews, null, 0, viewDef(
ViewFlags.None, ViewFlags.None,
@ -234,16 +236,16 @@ export function main() {
checkAndUpdateView(view); checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance; const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a.length).toBe(0); expect(qs.a.length).toBe(0);
const childView = createEmbeddedView(view, view.def.nodes[2]); const childView = createEmbeddedView(view, view.def.nodes[3]);
attachEmbeddedView(view.nodes[2], 0, childView); attachEmbeddedView(asElementData(view, 3), 0, childView);
checkAndUpdateView(view); checkAndUpdateView(view);
expect(qs.a.length).toBe(1); expect(qs.a.length).toBe(1);
detachEmbeddedView(view.nodes[2], 0); detachEmbeddedView(asElementData(view, 3), 0);
checkAndUpdateView(view); checkAndUpdateView(view);
expect(qs.a.length).toBe(0); 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', () => { it('should update view queries if embedded views are added or removed', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'), elementDef(NodeFlags.None, null, 2, 'div'),
viewQueryProvider(compViewDef([ ...viewQueryProviders(compViewDef([
anchorDef( anchorDef(
NodeFlags.HasEmbeddedViews, null, 0, NodeFlags.HasEmbeddedViews, null, 0,
viewDef( viewDef(
@ -266,17 +268,17 @@ export function main() {
checkAndUpdateView(view); checkAndUpdateView(view);
const comp: QueryService = view.nodes[1].provider.instance; const comp: QueryService = asProviderData(view, 1).instance;
expect(comp.a.length).toBe(0); 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]); const childView = createEmbeddedView(compView, compView.def.nodes[0]);
attachEmbeddedView(compView.nodes[0], 0, childView); attachEmbeddedView(asElementData(compView, 0), 0, childView);
checkAndUpdateView(view); checkAndUpdateView(view);
expect(comp.a.length).toBe(1); expect(comp.a.length).toBe(1);
detachEmbeddedView(compView.nodes[0], 0); detachEmbeddedView(asElementData(compView, 0), 0);
checkAndUpdateView(view); checkAndUpdateView(view);
expect(comp.a.length).toBe(0); expect(comp.a.length).toBe(0);
@ -290,21 +292,20 @@ export function main() {
} }
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'), elementDef(NodeFlags.None, null, 4, 'div'),
providerDef( providerDef(NodeFlags.None, null, 1, QueryService, []),
NodeFlags.None, null, QueryService, [], null, null, queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.All}),
{'a': ['query1', QueryBindingType.All]}),
aServiceProvider(), aServiceProvider(),
aServiceProvider(), aServiceProvider(),
])); ]));
checkAndUpdateView(view); 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 instanceof QueryList).toBeTruthy();
expect(qs.a.toArray()).toEqual([ expect(qs.a.toArray()).toEqual([
view.nodes[2].provider.instance, asProviderData(view, 3).instance,
view.nodes[3].provider.instance, asProviderData(view, 4).instance,
]); ]);
}); });
@ -314,18 +315,17 @@ export function main() {
} }
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 3, 'div'), elementDef(NodeFlags.None, null, 4, 'div'),
providerDef( providerDef(NodeFlags.None, null, 1, QueryService, []),
NodeFlags.None, null, QueryService, [], null, null, queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
{'a': ['query1', QueryBindingType.First]}),
aServiceProvider(), aServiceProvider(),
aServiceProvider(), aServiceProvider(),
])); ]));
checkAndUpdateView(view); checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance; const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a).toBe(view.nodes[2].provider.instance); expect(qs.a).toBe(asProviderData(view, 3).instance);
}); });
}); });
@ -336,16 +336,15 @@ export function main() {
} }
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, [['query1', QueryValueType.ElementRef]], 1, 'div'), elementDef(NodeFlags.None, [['query1', QueryValueType.ElementRef]], 2, 'div'),
providerDef( providerDef(NodeFlags.None, null, 1, QueryService, []),
NodeFlags.None, null, QueryService, [], null, null, queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
{'a': ['query1', QueryBindingType.First]}),
])); ]));
checkAndUpdateView(view); checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance; const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a.nativeElement).toBe(view.nodes[0].elementOrText.node); expect(qs.a.nativeElement).toBe(asElementData(view, 0).renderElement);
}); });
it('should query TemplateRef', () => { it('should query TemplateRef', () => {
@ -355,16 +354,15 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
anchorDef( anchorDef(
NodeFlags.None, [['query1', QueryValueType.TemplateRef]], 1, NodeFlags.None, [['query1', QueryValueType.TemplateRef]], 2,
viewDef(ViewFlags.None, [anchorDef(NodeFlags.None, null, 0)])), viewDef(ViewFlags.None, [anchorDef(NodeFlags.None, null, 0)])),
providerDef( providerDef(NodeFlags.None, null, 1, QueryService, []),
NodeFlags.None, null, QueryService, [], null, null, queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
{'a': ['query1', QueryBindingType.First]}),
])); ]));
checkAndUpdateView(view); checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance; const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a.createEmbeddedView).toBeTruthy(); expect(qs.a.createEmbeddedView).toBeTruthy();
}); });
@ -374,15 +372,14 @@ export function main() {
} }
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
anchorDef(NodeFlags.None, [['query1', QueryValueType.ViewContainerRef]], 1), anchorDef(NodeFlags.None, [['query1', QueryValueType.ViewContainerRef]], 2),
providerDef( providerDef(NodeFlags.None, null, 1, QueryService, []),
NodeFlags.None, null, QueryService, [], null, null, queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
{'a': ['query1', QueryBindingType.First]}),
])); ]));
checkAndUpdateView(view); checkAndUpdateView(view);
const qs: QueryService = view.nodes[1].provider.instance; const qs: QueryService = asProviderData(view, 1).instance;
expect(qs.a.createEmbeddedView).toBeTruthy(); expect(qs.a.createEmbeddedView).toBeTruthy();
}); });
}); });

View File

@ -124,7 +124,7 @@ export function main() {
it('should calculate childFlags for one level', () => { it('should calculate childFlags for one level', () => {
const vd = viewDef(ViewFlags.None, [ const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 1, 'span'), 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]); expect(childFlags(vd)).toEqual([NodeFlags.AfterContentChecked, NodeFlags.None]);
@ -133,7 +133,7 @@ export function main() {
it('should calculate childFlags for two levels', () => { it('should calculate childFlags for two levels', () => {
const vd = viewDef(ViewFlags.None, [ const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 1, 'span'), 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([ expect(childFlags(vd)).toEqual([
@ -144,10 +144,10 @@ export function main() {
it('should calculate childFlags for one level, multiple roots', () => { it('should calculate childFlags for one level, multiple roots', () => {
const vd = viewDef(ViewFlags.None, [ const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 1, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.AfterContentChecked, null, AService, []), providerDef(NodeFlags.AfterContentChecked, null, 0, AService, []),
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.AfterContentInit, null, AService, []), providerDef(NodeFlags.AfterContentInit, null, 0, AService, []),
providerDef(NodeFlags.AfterViewChecked, null, AService, []), providerDef(NodeFlags.AfterViewChecked, null, 0, AService, []),
]); ]);
expect(childFlags(vd)).toEqual([ expect(childFlags(vd)).toEqual([
@ -160,10 +160,10 @@ export function main() {
const vd = viewDef(ViewFlags.None, [ const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 2, 'span'),
elementDef(NodeFlags.None, null, 1, 'span'), elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.AfterContentChecked, null, AService, []), providerDef(NodeFlags.AfterContentChecked, null, 0, AService, []),
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.AfterContentInit, null, AService, []), providerDef(NodeFlags.AfterContentInit, null, 0, AService, []),
providerDef(NodeFlags.AfterViewInit, null, AService, []), providerDef(NodeFlags.AfterViewInit, null, 0, AService, []),
]); ]);
expect(childFlags(vd)).toEqual([ expect(childFlags(vd)).toEqual([
@ -181,7 +181,7 @@ export function main() {
it('should calculate childMatchedQueries for one level', () => { it('should calculate childMatchedQueries for one level', () => {
const vd = viewDef(ViewFlags.None, [ const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 1, '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'], []]); expect(childMatchedQueries(vd)).toEqual([['q1'], []]);
@ -190,7 +190,7 @@ export function main() {
it('should calculate childMatchedQueries for two levels', () => { it('should calculate childMatchedQueries for two levels', () => {
const vd = viewDef(ViewFlags.None, [ const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 1, 'span'), 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'], []]); expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], []]);
@ -199,10 +199,10 @@ export function main() {
it('should calculate childMatchedQueries for one level, multiple roots', () => { it('should calculate childMatchedQueries for one level, multiple roots', () => {
const vd = viewDef(ViewFlags.None, [ const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 1, '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'), elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], AService, []), providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], 0, AService, []),
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], AService, []), providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], 0, AService, []),
]); ]);
expect(childMatchedQueries(vd)).toEqual([['q1'], [], ['q2', 'q3'], [], []]); expect(childMatchedQueries(vd)).toEqual([['q1'], [], ['q2', 'q3'], [], []]);
@ -212,10 +212,10 @@ export function main() {
const vd = viewDef(ViewFlags.None, [ const vd = viewDef(ViewFlags.None, [
elementDef(NodeFlags.None, null, 2, 'span'), elementDef(NodeFlags.None, null, 2, 'span'),
elementDef(NodeFlags.None, null, 1, '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'), elementDef(NodeFlags.None, null, 2, 'span'),
providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], AService, []), providerDef(NodeFlags.None, [['q2', QueryValueType.Provider]], 0, AService, []),
providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], AService, []), providerDef(NodeFlags.None, [['q3', QueryValueType.Provider]], 0, AService, []),
]); ]);
expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], [], ['q2', 'q3'], [], []]); expect(childMatchedQueries(vd)).toEqual([['q1'], ['q1'], [], ['q2', 'q3'], [], []]);

View File

@ -8,7 +8,7 @@
import {NgIf} from '@angular/common'; import {NgIf} from '@angular/common';
import {Component, NgModule, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; 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 {DomSanitizer, DomSanitizerImpl, SafeStyle} from '@angular/platform-browser/src/security/dom_sanitization_service';
import {TreeNode, emptyTree} from '../util'; import {TreeNode, emptyTree} from '../util';
@ -25,7 +25,7 @@ let viewFlags = ViewFlags.DirectDom;
const TreeComponent_Host: ViewDefinition = viewDef(viewFlags, [ const TreeComponent_Host: ViewDefinition = viewDef(viewFlags, [
elementDef(NodeFlags.None, null, 1, 'tree'), 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( const TreeComponent_1: ViewDefinition = viewDef(
@ -33,7 +33,7 @@ const TreeComponent_1: ViewDefinition = viewDef(
[ [
elementDef(NodeFlags.None, null, 1, 'tree'), elementDef(NodeFlags.None, null, 1, 'tree'),
providerDef( providerDef(
NodeFlags.None, null, TreeComponent, [], {data: [0, 'data']}, null, null, NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null,
() => TreeComponent_0), () => TreeComponent_0),
], ],
(updater: NodeUpdater, view: ViewData) => { (updater: NodeUpdater, view: ViewData) => {
@ -46,7 +46,7 @@ const TreeComponent_2: ViewDefinition = viewDef(
[ [
elementDef(NodeFlags.None, null, 1, 'tree'), elementDef(NodeFlags.None, null, 1, 'tree'),
providerDef( providerDef(
NodeFlags.None, null, TreeComponent, [], {data: [0, 'data']}, null, null, NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null,
() => TreeComponent_0), () => TreeComponent_0),
], ],
(updater: NodeUpdater, view: ViewData) => { (updater: NodeUpdater, view: ViewData) => {
@ -62,9 +62,11 @@ const TreeComponent_0: ViewDefinition = viewDef(
[[BindingType.ElementStyle, 'backgroundColor', null]]), [[BindingType.ElementStyle, 'backgroundColor', null]]),
textDef([' ', ' ']), textDef([' ', ' ']),
anchorDef(NodeFlags.HasEmbeddedViews, null, 1, TreeComponent_1), 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), 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) => { (updater: NodeUpdater, view: ViewData) => {
const cmp = view.component; const cmp = view.component;
@ -87,8 +89,8 @@ export class AppModule {
} }
bootstrap() { bootstrap() {
this.rootView = createRootView(new DefaultServices(null, this.sanitizer), TreeComponent_Host); this.rootView = createRootView(new DefaultServices(null, this.sanitizer), TreeComponent_Host);
this.rootComp = this.rootView.nodes[1].provider.instance; this.rootComp = asProviderData(this.rootView, 1).instance;
this.rootEl = this.rootView.nodes[0].elementOrText.node; this.rootEl = asElementData(this.rootView, 0).renderElement;
} }
tick() { checkAndUpdateView(this.rootView); } tick() { checkAndUpdateView(this.rootView); }
} }