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