From f802194c18a4986aa034fa167b8811f24f050010 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 25 Jan 2017 13:45:07 -0800 Subject: [PATCH] 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 --- modules/@angular/core/src/view/element.ts | 44 ++-- modules/@angular/core/src/view/index.ts | 1 + modules/@angular/core/src/view/provider.ts | 229 ++---------------- .../@angular/core/src/view/pure_expression.ts | 14 +- modules/@angular/core/src/view/query.ts | 160 ++++++++++++ modules/@angular/core/src/view/services.ts | 21 +- modules/@angular/core/src/view/text.ts | 21 +- modules/@angular/core/src/view/types.ts | 200 ++++++++++----- modules/@angular/core/src/view/util.ts | 15 +- modules/@angular/core/src/view/view.ts | 125 +++++++--- modules/@angular/core/src/view/view_attach.ts | 76 +++--- .../core/test/view/component_view_spec.ts | 14 +- .../core/test/view/embedded_view_spec.ts | 18 +- .../@angular/core/test/view/provider_spec.ts | 44 ++-- .../core/test/view/pure_expression_spec.ts | 16 +- modules/@angular/core/test/view/query_spec.ts | 179 +++++++------- .../@angular/core/test/view/view_def_spec.ts | 32 +-- modules/benchmarks/src/tree/ng2_next/tree.ts | 18 +- 18 files changed, 668 insertions(+), 559 deletions(-) create mode 100644 modules/@angular/core/src/view/query.ts diff --git a/modules/@angular/core/src/view/element.ts b/modules/@angular/core/src/view/element.ts index 498dfbe684..d298ef7756 100644 --- a/modules/@angular/core/src/view/element.ts +++ b/modules/@angular/core/src/view/element.ts @@ -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, - embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined, - projectedViews: undefined - }, - provider: undefined, - pureExpression: undefined, + renderElement: el, + embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined, + projectedViews: 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); diff --git a/modules/@angular/core/src/view/index.ts b/modules/@angular/core/src/view/index.ts index a530aeb2f4..360b535ad1 100644 --- a/modules/@angular/core/src/view/index.ts +++ b/modules/@angular/core/src/view/index.ts @@ -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'; diff --git a/modules/@angular/core/src/view/provider.ts b/modules/@angular/core/src/view/provider.ts index b005aaadfc..35c80fda69 100644 --- a/modules/@angular/core/src/view/provider.ts +++ b/modules/@angular/core/src/view/provider.ts @@ -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(); @@ -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}; - 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(); - } - for (let i = 0; i < providerDef.viewQueries.length; i++) { - const def = providerDef.viewQueries[i]; - queries[def.id] = new QueryList(); - } - } - 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, 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 = 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, diff --git a/modules/@angular/core/src/view/pure_expression.ts b/modules/@angular/core/src/view/pure_expression.ts index 818b7343b9..26b6fed10b 100644 --- a/modules/@angular/core/src/view/pure_expression.ts +++ b/modules/@angular/core/src/view/pure_expression.ts @@ -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: diff --git a/modules/@angular/core/src/view/query.ts b/modules/@angular/core/src/view/query.ts new file mode 100644 index 0000000000..55d319fa6d --- /dev/null +++ b/modules/@angular/core/src/view/query.ts @@ -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 { + 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 = 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; +} diff --git a/modules/@angular/core/src/view/services.ts b/modules/@angular/core/src/view/services.ts index 17cb3d2522..7114c0a48a 100644 --- a/modules/@angular/core/src/view/services.ts +++ b/modules/@angular/core/src/view/services.ts @@ -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 { 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 unimplemented(); } @@ -48,18 +51,16 @@ class ViewContainerRef_ implements ViewContainerRef { get parentInjector(): Injector { return 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(templateRef: TemplateRef, context?: C, index?: number): EmbeddedViewRef { @@ -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)._view); + return this._data.embeddedViews.indexOf((viewRef)._view); } remove(index?: number): void { @@ -128,6 +129,6 @@ class TemplateRef_ implements TemplateRef { } get elementRef(): ElementRef { - return new ElementRef(this._parentView.nodes[this._def.index].elementOrText.node); + return new ElementRef(asElementData(this._parentView, this._def.index).renderElement); } } diff --git a/modules/@angular/core/src/view/text.ts b/modules/@angular/core/src/view/text.ts index 457a87f71b..0291344765 100644 --- a/modules/@angular/core/src/view/text.ts +++ b/modules/@angular/core/src/view/text.ts @@ -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 { diff --git a/modules/@angular/core/src/view/types.ts b/modules/@angular/core/src/view/types.ts index 3e00c9c15d..40903ee78b 100644 --- a/modules/@angular/core/src/view/types.ts +++ b/modules/@angular/core/src/view/types.ts @@ -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) + * + * 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 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 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}; } +/** + * Accessor for view.nodes, enforcing that every usage site stays monomorphic. + */ +export function asProviderData(view: ViewData, index: number): ProviderData { + return 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 view.nodes[index]; +} + +/** + * Accessor for view.nodes, enforcing that every usage site stays monomorphic. + */ +export function asQueryList(view: ViewData, index: number): QueryList { + return 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; } diff --git a/modules/@angular/core/src/view/util.ts b/modules/@angular/core/src/view/util.ts index 56074c3fb5..17e8ee536f 100644 --- a/modules/@angular/core/src/view/util.ts +++ b/modules/@angular/core/src/view/util.ts @@ -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; + } +} \ No newline at end of file diff --git a/modules/@angular/core/src/view/view.ts b/modules/@angular/core/src/view/view.ts index d83f270184..f2ef5bb204 100644 --- a/modules/@angular/core/src/view/view.ts +++ b/modules/@angular/core/src/view/view.ts @@ -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 = {}; 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 = {}; + 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; + } + } +} diff --git a/modules/@angular/core/src/view/view_attach.ts b/modules/@angular/core/src/view/view_attach.ts index 8452de2285..d9fc2109dd 100644 --- a/modules/@angular/core/src/view/view_attach.ts +++ b/modules/@angular/core/src/view/view_attach.ts @@ -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( diff --git a/modules/@angular/core/test/view/component_view_spec.ts b/modules/@angular/core/test/view/component_view_spec.ts index 39d5fa9de0..4c323b4c32 100644 --- a/modules/@angular/core/test/view/component_view_spec.ts +++ b/modules/@angular/core/test/view/component_view_spec.ts @@ -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, []) ])), ])); diff --git a/modules/@angular/core/test/view/embedded_view_spec.ts b/modules/@angular/core/test/view/embedded_view_spec.ts index 6f461cc955..b64a66d1be 100644 --- a/modules/@angular/core/test/view/embedded_view_spec.ts +++ b/modules/@angular/core/test/view/embedded_view_spec.ts @@ -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']); diff --git a/modules/@angular/core/test/view/provider_spec.ts b/modules/@angular/core/test/view/provider_spec.ts index 54fa910398..2b2a8d527d 100644 --- a/modules/@angular/core/test/view/provider_spec.ts +++ b/modules/@angular/core/test/view/provider_spec.ts @@ -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))); diff --git a/modules/@angular/core/test/view/pure_expression_spec.ts b/modules/@angular/core/test/view/pure_expression_spec.ts index 1550ed2e6a..f601dfbb72 100644 --- a/modules/@angular/core/test/view/pure_expression_spec.ts +++ b/modules/@angular/core/test/view/pure_expression_spec.ts @@ -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); diff --git a/modules/@angular/core/test/view/query_spec.ts b/modules/@angular/core/test/view/query_spec.ts index 5bab183c9b..bd49540d23 100644 --- a/modules/@angular/core/test/view/query_spec.ts +++ b/modules/@angular/core/test/view/query_spec.ts @@ -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; } - 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(); }); }); diff --git a/modules/@angular/core/test/view/view_def_spec.ts b/modules/@angular/core/test/view/view_def_spec.ts index a2a0bc4030..142203b488 100644 --- a/modules/@angular/core/test/view/view_def_spec.ts +++ b/modules/@angular/core/test/view/view_def_spec.ts @@ -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'], [], []]); diff --git a/modules/benchmarks/src/tree/ng2_next/tree.ts b/modules/benchmarks/src/tree/ng2_next/tree.ts index 29146f6932..45dd4ad68d 100644 --- a/modules/benchmarks/src/tree/ng2_next/tree.ts +++ b/modules/benchmarks/src/tree/ng2_next/tree.ts @@ -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); } }