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