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

View File

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

View File

@ -10,14 +10,13 @@ import {SimpleChange, SimpleChanges} from '../change_detection/change_detection'
import {Injector} from '../di';
import {stringify} from '../facade/lang';
import {ElementRef} from '../linker/element_ref';
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
import {QueryList} from '../linker/query_list';
import {TemplateRef} from '../linker/template_ref';
import {ViewContainerRef} from '../linker/view_container_ref';
import {Renderer} from '../render/api';
import {queryDef} from './query';
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderOutputDef, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags} from './types';
import {checkAndUpdateBinding, checkAndUpdateBindingWithChange, declaredViewContainer, setBindingDebugInfo} from './util';
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderOutputDef, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, asElementData, asProviderData} from './types';
import {checkAndUpdateBinding, checkAndUpdateBindingWithChange, setBindingDebugInfo} from './util';
const _tokenKeyCache = new Map<any, string>();
@ -27,11 +26,9 @@ const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
const TemplateRefTokenKey = tokenKey(TemplateRef);
export function providerDef(
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ctor: any,
flags: NodeFlags, matchedQueries: [string, QueryValueType][], childCount: number, ctor: any,
deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
outputs?: {[name: string]: string},
contentQueries?: {[name: string]: [string, QueryBindingType]}, component?: () => ViewDefinition,
viewQueries?: {[name: string]: [string, QueryBindingType]}, ): NodeDef {
outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef {
const matchedQueryDefs: {[queryId: string]: QueryValueType} = {};
if (matchedQueries) {
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
@ -66,26 +63,9 @@ export function providerDef(
}
return {flags, token, tokenKey: tokenKey(token)};
});
const contentQueryDefs: QueryDef[] = [];
for (let propName in contentQueries) {
const [id, bindingType] = contentQueries[propName];
contentQueryDefs.push({id, propName, bindingType});
}
const viewQueryDefs: QueryDef[] = [];
for (let propName in viewQueries) {
const [id, bindingType] = viewQueries[propName];
viewQueryDefs.push({id, propName, bindingType});
}
if (component) {
flags = flags | NodeFlags.HasComponent;
}
if (contentQueryDefs.length) {
flags = flags | NodeFlags.HasContentQuery;
}
if (viewQueryDefs.length) {
flags = flags | NodeFlags.HasViewQuery;
}
return {
type: NodeType.Provider,
@ -97,23 +77,15 @@ export function providerDef(
childMatchedQueries: undefined,
bindingIndex: undefined,
disposableIndex: undefined,
providerIndices: undefined,
// regular values
flags,
matchedQueries: matchedQueryDefs,
childCount: 0, bindings,
matchedQueries: matchedQueryDefs, childCount, bindings,
disposableCount: outputDefs.length,
element: undefined,
provider: {
tokenKey: tokenKey(ctor),
ctor,
deps: depDefs,
outputs: outputDefs,
contentQueries: contentQueryDefs,
viewQueries: viewQueryDefs, component
},
provider: {tokenKey: tokenKey(ctor), ctor, deps: depDefs, outputs: outputDefs, component},
text: undefined,
pureExpression: undefined,
query: undefined
};
}
@ -126,7 +98,8 @@ export function tokenKey(token: any): string {
return key;
}
export function createProvider(view: ViewData, def: NodeDef, componentView: ViewData): NodeData {
export function createProvider(
view: ViewData, def: NodeDef, componentView: ViewData): ProviderData {
const providerDef = def.provider;
const provider = createInstance(view, def.parent, providerDef.ctor, providerDef.deps);
if (providerDef.outputs.length) {
@ -137,29 +110,13 @@ export function createProvider(view: ViewData, def: NodeDef, componentView: View
view.disposables[def.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
}
}
let queries: {[queryId: string]: QueryList<any>};
if (providerDef.contentQueries.length || providerDef.viewQueries.length) {
queries = {};
for (let i = 0; i < providerDef.contentQueries.length; i++) {
const def = providerDef.contentQueries[i];
queries[def.id] = new QueryList<any>();
}
for (let i = 0; i < providerDef.viewQueries.length; i++) {
const def = providerDef.viewQueries[i];
queries[def.id] = new QueryList<any>();
}
}
return {
elementOrText: undefined,
provider: {instance: provider, componentView: componentView, queries},
pureExpression: undefined,
};
return {instance: provider, componentView: componentView};
}
export function checkAndUpdateProviderInline(
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v7: any, v8: any, v9: any) {
const provider = view.nodes[def.index].provider.instance;
const provider = asProviderData(view, def.index).instance;
let changes: SimpleChanges;
// Note: fallthrough is intended!
switch (def.bindings.length) {
@ -196,7 +153,7 @@ export function checkAndUpdateProviderInline(
}
export function checkAndUpdateProviderDynamic(view: ViewData, def: NodeDef, values: any[]) {
const provider = view.nodes[def.index].provider.instance;
const provider = asProviderData(view, def.index).instance;
let changes: SimpleChanges;
for (let i = 0; i < values.length; i++) {
changes = checkAndUpdateProp(view, provider, def, i, values[i], changes);
@ -265,15 +222,15 @@ export function resolveDep(
return Injector.NULL.get(depDef.token, notFoundValue);
}
case ElementRefTokenKey:
return new ElementRef(view.nodes[elIndex].elementOrText.node);
return new ElementRef(asElementData(view, elIndex).renderElement);
case ViewContainerRefTokenKey:
return view.services.createViewContainerRef(view.nodes[elIndex]);
return view.services.createViewContainerRef(asElementData(view, elIndex));
case TemplateRefTokenKey:
return view.services.createTemplateRef(view, elDef);
default:
const providerIndex = elDef.providerIndices[tokenKey];
const providerIndex = elDef.element.providerIndices[tokenKey];
if (providerIndex != null) {
return view.nodes[providerIndex].provider.instance;
return asProviderData(view, providerIndex).instance;
}
}
elIndex = view.parentDiIndex;
@ -303,7 +260,8 @@ function checkAndUpdateProp(
if (view.def.flags & ViewFlags.LogBindingUpdate) {
setBindingDebugInfo(
view.renderer, view.nodes[def.parent].elementOrText.node, binding.nonMinifiedName, value);
view.renderer, asElementData(view, def.parent).renderElement, binding.nonMinifiedName,
value);
}
if (change) {
changes = changes || {};
@ -313,153 +271,6 @@ function checkAndUpdateProp(
return changes;
}
export enum QueryAction {
CheckNoChanges,
CheckAndUpdate,
}
export function execContentQueriesAction(view: ViewData, action: QueryAction) {
if (!(view.def.nodeFlags & NodeFlags.HasContentQuery)) {
return;
}
for (let i = 0; i < view.nodes.length; i++) {
const nodeDef = view.def.nodes[i];
if (nodeDef.flags & NodeFlags.HasContentQuery) {
execContentQuery(view, nodeDef, action);
} else if ((nodeDef.childFlags & NodeFlags.HasContentQuery) === 0) {
// no child has a content query
// then skip the children
i += nodeDef.childCount;
}
}
}
export function updateViewQueries(view: ViewData, action: QueryAction) {
if (!(view.def.nodeFlags & NodeFlags.HasViewQuery)) {
return;
}
for (let i = 0; i < view.nodes.length; i++) {
const nodeDef = view.def.nodes[i];
if (nodeDef.flags & NodeFlags.HasViewQuery) {
updateViewQuery(view, nodeDef, action);
} else if ((nodeDef.childFlags & NodeFlags.HasViewQuery) === 0) {
// no child has a view query
// then skip the children
i += nodeDef.childCount;
}
}
}
function execContentQuery(view: ViewData, nodeDef: NodeDef, action: QueryAction) {
const providerData = view.nodes[nodeDef.index].provider;
for (let i = 0; i < nodeDef.provider.contentQueries.length; i++) {
const queryDef = nodeDef.provider.contentQueries[i];
const queryId = queryDef.id;
const queryList = providerData.queries[queryId];
if (queryList.dirty) {
const elementDef = view.def.nodes[nodeDef.parent];
const newValues = calcQueryValues(
view, elementDef.index, elementDef.index + elementDef.childCount, queryId, []);
execQueryAction(view, providerData.instance, queryList, queryDef, newValues, action);
}
}
}
function updateViewQuery(view: ViewData, nodeDef: NodeDef, action: QueryAction) {
for (let i = 0; i < nodeDef.provider.viewQueries.length; i++) {
const queryDef = nodeDef.provider.viewQueries[i];
const queryId = queryDef.id;
const providerData = view.nodes[nodeDef.index].provider;
const queryList = providerData.queries[queryId];
if (queryList.dirty) {
const componentView = providerData.componentView;
const newValues =
calcQueryValues(componentView, 0, componentView.nodes.length - 1, queryId, []);
execQueryAction(view, providerData.instance, queryList, queryDef, newValues, action);
}
}
}
function execQueryAction(
view: ViewData, provider: any, queryList: QueryList<any>, queryDef: QueryDef, newValues: any[],
action: QueryAction) {
switch (action) {
case QueryAction.CheckAndUpdate:
queryList.reset(newValues);
let boundValue: any;
switch (queryDef.bindingType) {
case QueryBindingType.First:
boundValue = queryList.first;
break;
case QueryBindingType.All:
boundValue = queryList;
break;
}
provider[queryDef.propName] = boundValue;
break;
case QueryAction.CheckNoChanges:
// queries should always be non dirty when we go into checkNoChanges!
const oldValuesStr = queryList.toArray().map(v => stringify(v));
const newValuesStr = newValues.map(v => stringify(v));
throw new ExpressionChangedAfterItHasBeenCheckedError(
oldValuesStr, newValuesStr, view.firstChange);
}
}
function calcQueryValues(
view: ViewData, startIndex: number, endIndex: number, queryId: string, values: any[]): any[] {
const len = view.def.nodes.length;
for (let i = startIndex; i <= endIndex; i++) {
const nodeDef = view.def.nodes[i];
const queryValueType = <QueryValueType>nodeDef.matchedQueries[queryId];
if (queryValueType != null) {
// a match
let value: any;
switch (queryValueType) {
case QueryValueType.ElementRef:
value = new ElementRef(view.nodes[i].elementOrText.node);
break;
case QueryValueType.TemplateRef:
value = view.services.createTemplateRef(view, nodeDef);
break;
case QueryValueType.ViewContainerRef:
value = view.services.createViewContainerRef(view.nodes[i]);
break;
case QueryValueType.Provider:
value = view.nodes[i].provider.instance;
break;
}
values.push(value);
}
if (nodeDef.flags & NodeFlags.HasEmbeddedViews &&
queryId in nodeDef.element.template.nodeMatchedQueries) {
// check embedded views that were attached at the place of their template.
const nodeData = view.nodes[i];
const embeddedViews = nodeData.elementOrText.embeddedViews;
for (let k = 0; k < embeddedViews.length; k++) {
const embeddedView = embeddedViews[k];
const dvc = declaredViewContainer(embeddedView);
if (dvc && dvc === nodeData) {
calcQueryValues(embeddedView, 0, embeddedView.nodes.length - 1, queryId, values);
}
}
const projectedViews = nodeData.elementOrText.projectedViews;
if (projectedViews) {
for (let k = 0; k < projectedViews.length; k++) {
const projectedView = projectedViews[k];
calcQueryValues(projectedView, 0, projectedView.nodes.length - 1, queryId, values);
}
}
}
if (!(queryId in nodeDef.childMatchedQueries)) {
// If don't check descendants, skip the children.
// Or: no child matches the query, then skip the children as well.
i += nodeDef.childCount;
}
}
return values;
}
export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: NodeFlags) {
if (!(view.def.nodeFlags & lifecycles)) {
return;
@ -471,7 +282,7 @@ export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: Node
const nodeIndex = nodeDef.index;
if (nodeDef.flags & lifecycles) {
// a leaf
callProviderLifecycles(view.nodes[nodeIndex].provider.instance, nodeDef.flags & lifecycles);
callProviderLifecycles(asProviderData(view, nodeIndex).instance, nodeDef.flags & lifecycles);
} else if ((nodeDef.childFlags & lifecycles) === 0) {
// a parent with leafs
// no child matches one of the lifecycles,

View File

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

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

View File

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

View File

@ -34,7 +34,7 @@ export interface ViewDefinition {
* Especially providers are after elements / anchros.
*/
reverseChildNodes: NodeDef[];
lastRootNode: number;
lastRootNode: NodeDef;
bindingCount: number;
disposableCount: number;
/**
@ -65,6 +65,12 @@ export enum ViewFlags {
DirectDom = 1 << 1
}
/**
* A node definition in the view.
*
* Note: We use one type for all nodes so that loops that loop over all nodes
* of a ViewDefinition stay monomorphic!
*/
export interface NodeDef {
type: NodeType;
index: number;
@ -76,7 +82,6 @@ export interface NodeDef {
/** aggregated NodeFlags for all children **/
childFlags: NodeFlags;
providerIndices: {[tokenKey: string]: number};
bindingIndex: number;
bindings: BindingDef[];
disposableIndex: number;
@ -94,6 +99,7 @@ export interface NodeDef {
provider: ProviderDef;
text: TextDef;
pureExpression: PureExpressionDef;
query: QueryDef;
}
export enum NodeType {
@ -101,6 +107,7 @@ export enum NodeType {
Text,
Provider,
PureExpression,
Query,
}
/**
@ -122,11 +129,43 @@ export enum NodeFlags {
HasViewQuery = 1 << 11,
}
export interface BindingDef {
type: BindingType;
name: string;
nonMinifiedName: string;
securityContext: SecurityContext;
suffix: string;
}
export enum BindingType {
ElementAttribute,
ElementClass,
ElementStyle,
ElementProperty,
ProviderProperty,
Interpolation,
PureExpressionProperty
}
export enum QueryValueType {
ElementRef,
TemplateRef,
ViewContainerRef,
Provider
}
export interface ElementDef {
name: string;
attrs: {[name: string]: string};
outputs: ElementOutputDef[];
template: ViewDefinition;
/**
* visible providers for DI in the view,
* as see from this element.
* Note: We use protoypical inheritance
* to indices in parent ElementDefs.
*/
providerIndices: {[tokenKey: string]: number};
}
export interface ElementOutputDef {
@ -134,6 +173,21 @@ export interface ElementOutputDef {
eventName: string;
}
export interface ProviderDef {
tokenKey: string;
ctor: any;
deps: DepDef[];
outputs: ProviderOutputDef[];
// closure to allow recursive components
component: () => ViewDefinition;
}
export interface DepDef {
flags: DepFlags;
token: any;
tokenKey: string;
}
/**
* Bitmask for DI flags
*/
@ -142,46 +196,11 @@ export enum DepFlags {
SkipSelf = 1 << 0
}
export interface DepDef {
flags: DepFlags;
token: any;
tokenKey: string;
}
export interface ProviderOutputDef {
propName: string;
eventName: string;
}
export interface ProviderDef {
tokenKey: string;
ctor: any;
deps: DepDef[];
outputs: ProviderOutputDef[];
contentQueries: QueryDef[];
viewQueries: QueryDef[];
// closure to allow recursive components
component: () => ViewDefinition;
}
export interface QueryDef {
id: string;
propName: string;
bindingType: QueryBindingType;
}
export enum QueryBindingType {
First,
All
}
export enum QueryValueType {
ElementRef,
TemplateRef,
ViewContainerRef,
Provider
}
export interface TextDef { prefix: string; }
export interface PureExpressionDef {
@ -195,22 +214,19 @@ export enum PureExpressionType {
Pipe
}
export enum BindingType {
ElementAttribute,
ElementClass,
ElementStyle,
ElementProperty,
ProviderProperty,
Interpolation,
PureExpressionProperty
export interface QueryDef {
id: string;
bindings: QueryBindingDef[];
}
export interface BindingDef {
type: BindingType;
name: string;
nonMinifiedName: string;
securityContext: SecurityContext;
suffix: string;
export interface QueryBindingDef {
propName: string;
bindingType: QueryBindingType;
}
export enum QueryBindingType {
First,
All
}
// -------------------------------------
@ -235,7 +251,12 @@ export interface ViewData {
parent: ViewData;
component: any;
context: any;
nodes: NodeData[];
// Attention: Never loop over this, as this will
// create a polymorphic usage site.
// Instead: Always loop over ViewDefinition.nodes,
// and call the right accessor (e.g. `elementData`) based on
// the NodeType.
nodes: {[key: number]: NodeData};
firstChange: boolean;
oldValues: any[];
disposables: DisposableFn[];
@ -245,16 +266,38 @@ export type DisposableFn = () => void;
/**
* Node instance data.
*
* We have a separate type per NodeType to save memory
* (TextData | ElementData | ProviderData | PureExpressionData | QueryList<any>)
*
* To keep our code monomorphic,
* we prohibit using `NodeData` directly but enforce the use of accessors (`asElementData`, ...).
* This way, no usage site can get a `NodeData` from view.nodes and then use it for different
* purposes.
*/
export class NodeData { private __brand: any; }
/**
* Data for an instantiated NodeType.Text.
*
* Attention: Adding fields to this is performance sensitive!
*/
export interface NodeData {
elementOrText: ElementOrTextData;
provider: ProviderData;
pureExpression: PureExpressionData;
export interface TextData { renderText: any; }
/**
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
*/
export function asTextData(view: ViewData, index: number): TextData {
return <any>view.nodes[index];
}
export interface ElementOrTextData {
node: any;
/**
* Data for an instantiated NodeType.Element.
*
* Attention: Adding fields to this is performance sensitive!
*/
export interface ElementData {
renderElement: any;
embeddedViews: ViewData[];
// views that have been created from the template
// of this element,
@ -263,22 +306,59 @@ export interface ElementOrTextData {
projectedViews: ViewData[];
}
/**
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
*/
export function asElementData(view: ViewData, index: number): ElementData {
return <any>view.nodes[index];
}
/**
* Data for an instantiated NodeType.Provider.
*
* Attention: Adding fields to this is performance sensitive!
*/
export interface ProviderData {
instance: any;
componentView: ViewData;
queries: {[queryId: string]: QueryList<any>};
}
/**
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
*/
export function asProviderData(view: ViewData, index: number): ProviderData {
return <any>view.nodes[index];
}
/**
* Data for an instantiated NodeType.PureExpression.
*
* Attention: Adding fields to this is performance sensitive!
*/
export interface PureExpressionData {
value: any;
pipe: PipeTransform;
}
/**
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
*/
export function asPureExpressionData(view: ViewData, index: number): PureExpressionData {
return <any>view.nodes[index];
}
/**
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
*/
export function asQueryList(view: ViewData, index: number): QueryList<any> {
return <any>view.nodes[index];
}
export interface Services {
renderComponent(rcp: RenderComponentType): Renderer;
sanitize(context: SecurityContext, value: string): string;
// Note: This needs to be here to prevent a cycle in source files.
createViewContainerRef(data: NodeData): ViewContainerRef;
createViewContainerRef(data: ElementData): ViewContainerRef;
// Note: This needs to be here to prevent a cycle in source files.
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any>;
}

View File

@ -12,7 +12,7 @@ import {looseIdentical} from '../facade/lang';
import {ExpressionChangedAfterItHasBeenCheckedError} from '../linker/errors';
import {Renderer} from '../render/api';
import {NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewDefinition} from './types';
import {ElementData, NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewDefinition, asElementData, asTextData} from './types';
export function setBindingDebugInfo(
renderer: Renderer, renderNode: any, propName: string, value: any) {
@ -61,10 +61,19 @@ export function checkAndUpdateBindingWithChange(
return null;
}
export function declaredViewContainer(view: ViewData) {
export function declaredViewContainer(view: ViewData): ElementData {
if (view.parent) {
const parentView = view.parent;
return parentView.nodes[view.parentIndex];
return asElementData(parentView, view.parentIndex);
}
return undefined;
}
export function renderNode(view: ViewData, def: NodeDef): any {
switch (def.type) {
case NodeType.Element:
return asElementData(view, def.index).renderElement;
case NodeType.Text:
return asTextData(view, def.index).renderText;
}
}

View File

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

View File

@ -6,20 +6,21 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NodeData, NodeFlags, ViewData} from './types';
import {declaredViewContainer} from './util';
import {dirtyParentQuery} from './query';
import {ElementData, NodeData, NodeFlags, NodeType, ViewData, asElementData, asProviderData, asTextData} from './types';
import {declaredViewContainer, renderNode} from './util';
export function attachEmbeddedView(node: NodeData, viewIndex: number, view: ViewData) {
let embeddedViews = node.elementOrText.embeddedViews;
export function attachEmbeddedView(elementData: ElementData, viewIndex: number, view: ViewData) {
let embeddedViews = elementData.embeddedViews;
if (viewIndex == null) {
viewIndex = embeddedViews.length;
}
addToArray(embeddedViews, viewIndex, view);
const dvc = declaredViewContainer(view);
if (dvc && dvc !== node) {
let projectedViews = dvc.elementOrText.projectedViews;
const dvcElementData = declaredViewContainer(view);
if (dvcElementData && dvcElementData !== elementData) {
let projectedViews = dvcElementData.projectedViews;
if (!projectedViews) {
projectedViews = dvc.elementOrText.projectedViews = [];
projectedViews = dvcElementData.projectedViews = [];
}
projectedViews.push(view);
}
@ -30,8 +31,8 @@ export function attachEmbeddedView(node: NodeData, viewIndex: number, view: View
// update rendering
const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null;
const prevNode = prevView ? prevView.nodes[prevView.def.lastRootNode] : node;
const prevRenderNode = prevNode.elementOrText.node;
const prevRenderNode =
prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
if (view.renderer) {
view.renderer.attachViewAfter(prevRenderNode, rootRenderNodes(view));
} else {
@ -44,18 +45,17 @@ export function attachEmbeddedView(node: NodeData, viewIndex: number, view: View
}
}
export function detachEmbeddedView(node: NodeData, viewIndex: number): ViewData {
const renderData = node.elementOrText;
const embeddedViews = renderData.embeddedViews;
export function detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData {
const embeddedViews = elementData.embeddedViews;
if (viewIndex == null) {
viewIndex = embeddedViews.length;
}
const view = embeddedViews[viewIndex];
removeFromArray(embeddedViews, viewIndex);
const dvc = declaredViewContainer(view);
if (dvc && dvc !== node) {
const projectedViews = dvc.elementOrText.projectedViews;
const dvcElementData = declaredViewContainer(view);
if (dvcElementData && dvcElementData !== elementData) {
const projectedViews = dvcElementData.projectedViews;
removeFromArray(projectedViews, projectedViews.indexOf(view));
}
@ -67,7 +67,7 @@ export function detachEmbeddedView(node: NodeData, viewIndex: number): ViewData
if (view.renderer) {
view.renderer.detachView(rootRenderNodes(view));
} else {
const parentNode = renderData.node.parentNode;
const parentNode = elementData.renderElement.parentNode;
if (parentNode) {
directDomAttachDetachSiblingRenderNodes(
view, 0, DirectDomAction.RemoveChild, parentNode, null);
@ -94,27 +94,6 @@ function removeFromArray(arr: any[], index: number) {
}
}
function dirtyParentQuery(queryId: string, view: ViewData) {
let nodeIndex = view.parentIndex;
view = view.parent;
let providerIdx: number;
while (view) {
const nodeDef = view.def.nodes[nodeIndex];
providerIdx = nodeDef.providerIndices[queryId];
if (providerIdx != null) {
break;
}
nodeIndex = view.parentIndex;
view = view.parent;
}
if (!view) {
throw new Error(
`Illegal State: Tried to dirty parent query ${queryId} but the query could not be found!`);
}
const providerData = view.nodes[providerIdx].provider;
providerData.queries[queryId].setDirty();
}
export function rootRenderNodes(view: ViewData): any[] {
const renderNodes: any[] = [];
collectSiblingRenderNodes(view, 0, renderNodes);
@ -122,12 +101,12 @@ export function rootRenderNodes(view: ViewData): any[] {
}
function collectSiblingRenderNodes(view: ViewData, startIndex: number, target: any[]) {
for (let i = startIndex; i < view.nodes.length; i++) {
const nodeCount = view.def.nodes.length;
for (let i = startIndex; i < nodeCount; i++) {
const nodeDef = view.def.nodes[i];
const nodeData = view.nodes[i].elementOrText;
target.push(nodeData.node);
target.push(renderNode(view, nodeDef));
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
const embeddedViews = nodeData.embeddedViews;
const embeddedViews = asElementData(view, i).embeddedViews;
if (embeddedViews) {
for (let k = 0; k < embeddedViews.length; k++) {
collectSiblingRenderNodes(embeddedViews[k], 0, target);
@ -148,22 +127,23 @@ enum DirectDomAction {
function directDomAttachDetachSiblingRenderNodes(
view: ViewData, startIndex: number, action: DirectDomAction, parentNode: any,
nextSibling: any) {
for (let i = startIndex; i < view.nodes.length; i++) {
const nodeCount = view.def.nodes.length;
for (let i = startIndex; i < nodeCount; i++) {
const nodeDef = view.def.nodes[i];
const nodeData = view.nodes[i].elementOrText;
const rn = renderNode(view, nodeDef);
switch (action) {
case DirectDomAction.AppendChild:
parentNode.appendChild(nodeData.node);
parentNode.appendChild(rn);
break;
case DirectDomAction.InsertBefore:
parentNode.insertBefore(nodeData.node, nextSibling);
parentNode.insertBefore(rn, nextSibling);
break;
case DirectDomAction.RemoveChild:
parentNode.removeChild(nodeData.node);
parentNode.removeChild(rn);
break;
}
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
const embeddedViews = nodeData.embeddedViews;
const embeddedViews = asElementData(view, i).embeddedViews;
if (embeddedViews) {
for (let k = 0; k < embeddedViews.length; k++) {
directDomAttachDetachSiblingRenderNodes(

View File

@ -7,7 +7,7 @@
*/
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, checkAndUpdateView, checkNoChangesView, createRootView, destroyView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -54,13 +54,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
providerDef(
NodeFlags.None, null, AComp, [], null, null, null,
NodeFlags.None, null, 0, AComp, [], null, null,
() => compViewDef([
elementDef(NodeFlags.None, null, 0, 'span'),
])),
]));
const compView = view.nodes[1].provider.componentView;
const compView = asProviderData(view, 1).componentView;
expect(compView.context).toBe(instance);
expect(compView.component).toBe(instance);
@ -81,13 +81,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(
compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
providerDef(NodeFlags.None, null, AComp, [], null, null, null, () => compViewDef(
providerDef(NodeFlags.None, null, 0, AComp, [], null, null, () => compViewDef(
[
elementDef(NodeFlags.None, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
], update
)),
], jasmine.createSpy('parentUpdater')));
const compView = view.nodes[1].provider.componentView;
const compView = asProviderData(view, 1).componentView;
checkAndUpdateView(view);
@ -118,10 +118,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, 1, 'div'),
providerDef(
NodeFlags.None, null, AComp, [], null, null, null,
NodeFlags.None, null, 0, AComp, [], null, null,
() => compViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.OnDestroy, null, ChildProvider, [])
providerDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
])),
]));

View File

@ -7,7 +7,7 @@
*/
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {BindingType, DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -83,16 +83,16 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
const rootChildren = getDOM().childNodes(rootNodes[0]);
attachEmbeddedView(parentView.nodes[1], 0, childView0);
attachEmbeddedView(parentView.nodes[1], 1, childView1);
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
attachEmbeddedView(asElementData(parentView, 1), 1, childView1);
// 2 anchors + 2 elements
expect(rootChildren.length).toBe(4);
expect(getDOM().getAttribute(rootChildren[1], 'name')).toBe('child0');
expect(getDOM().getAttribute(rootChildren[2], 'name')).toBe('child1');
detachEmbeddedView(parentView.nodes[1], 1);
detachEmbeddedView(parentView.nodes[1], 0);
detachEmbeddedView(asElementData(parentView, 1), 1);
detachEmbeddedView(asElementData(parentView, 1), 0);
expect(getDOM().childNodes(rootNodes[0]).length).toBe(2);
});
@ -106,7 +106,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
]));
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[0]);
attachEmbeddedView(parentView.nodes[0], 0, childView0);
attachEmbeddedView(asElementData(parentView, 0), 0, childView0);
const rootNodes = rootRenderNodes(parentView);
expect(rootNodes.length).toBe(3);
@ -133,7 +133,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
const rootEl = rootNodes[0];
attachEmbeddedView(parentView.nodes[1], 0, childView0);
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
checkAndUpdateView(parentView);
@ -163,13 +163,13 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
elementDef(NodeFlags.None, null, 1, 'div'),
anchorDef(NodeFlags.HasEmbeddedViews, null, 0, embeddedViewDef([
elementDef(NodeFlags.None, null, 1, 'span'),
providerDef(NodeFlags.OnDestroy, null, ChildProvider, [])
providerDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
]))
]));
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
attachEmbeddedView(parentView.nodes[1], 0, childView0);
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
destroyView(parentView);
expect(log).toEqual(['ngOnDestroy']);

View File

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

View File

@ -7,7 +7,7 @@
*/
import {PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, providerDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {DefaultServices, NodeDef, NodeFlags, NodeUpdater, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, checkAndUpdateView, checkNoChangesView, createRootView, elementDef, providerDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {INLINE_DYNAMIC_VALUES, InlineDynamic, callUpdater} from './helper';
@ -46,14 +46,14 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 2, 'span'), pureArrayDef(2),
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
],
(updater, view) => {
callUpdater(
updater, inlineDynamic, view, 2,
[callUpdater(updater, inlineDynamic, view, 1, values)]);
}));
const service = view.nodes[2].provider.instance;
const service = asProviderData(view, 2).instance;
values = [1, 2];
checkAndUpdateView(view);
@ -80,14 +80,14 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 2, 'span'), pureObjectDef(['a', 'b']),
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
],
(updater, view) => {
callUpdater(
updater, inlineDynamic, view, 2,
[callUpdater(updater, inlineDynamic, view, 1, values)]);
}));
const service = view.nodes[2].provider.instance;
const service = asProviderData(view, 2).instance;
values = [1, 2];
checkAndUpdateView(view);
@ -118,15 +118,15 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, 3, 'span'),
providerDef(NodeFlags.None, null, SomePipe, []), purePipeDef(SomePipe, 2),
providerDef(NodeFlags.None, null, Service, [], {data: [0, 'data']})
providerDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
providerDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
],
(updater, view) => {
callUpdater(
updater, inlineDynamic, view, 3,
[callUpdater(updater, inlineDynamic, view, 2, values)]);
}));
const service = view.nodes[3].provider.instance;
const service = asProviderData(view, 3).instance;
values = [1, 2];
checkAndUpdateView(view);

View File

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

View File

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

View File

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