From ab3527c99b67ff7c4b0afa9fb99cfe458a884fbf Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 22 Feb 2017 10:05:56 -0800 Subject: [PATCH] refactor(core): view engine - misc - fix bug when detaching view from `ApplicationRef` - fix integration of adding `ng-reflect` attributes in debug mode. --- modules/@angular/compiler/src/identifiers.ts | 2 +- .../src/view_compiler_next/view_compiler.ts | 4 +- modules/@angular/core/src/application_ref.ts | 2 +- modules/@angular/core/src/linker/view_ref.ts | 4 +- modules/@angular/core/src/view/element.ts | 33 ++--- modules/@angular/core/src/view/provider.ts | 109 ++++++++++------ .../@angular/core/src/view/pure_expression.ts | 13 +- modules/@angular/core/src/view/refs.ts | 32 +++-- modules/@angular/core/src/view/services.ts | 97 +++++++++------ modules/@angular/core/src/view/text.ts | 6 +- modules/@angular/core/src/view/types.ts | 18 +-- modules/@angular/core/src/view/util.ts | 41 +++--- modules/@angular/core/src/view/view.ts | 117 ++++++++++-------- modules/@angular/core/src/view/view_attach.ts | 23 ++-- .../core/test/application_ref_spec.ts | 2 +- .../core/test/view/embedded_view_spec.ts | 14 +-- .../core/test/view/ng_content_spec.ts | 2 +- modules/@angular/core/test/view/query_spec.ts | 11 +- 18 files changed, 307 insertions(+), 223 deletions(-) diff --git a/modules/@angular/compiler/src/identifiers.ts b/modules/@angular/compiler/src/identifiers.ts index cc110f39f5..d8f4246e03 100644 --- a/modules/@angular/compiler/src/identifiers.ts +++ b/modules/@angular/compiler/src/identifiers.ts @@ -255,7 +255,7 @@ export class Identifiers { static pureArrayDef: IdentifierSpec = {name: 'ɵpureArrayDef', moduleUrl: CORE, runtime: ɵpureArrayDef}; static pureObjectDef: - IdentifierSpec = {name: 'ɵpureObjectRef', moduleUrl: CORE, runtime: ɵpureObjectDef}; + IdentifierSpec = {name: 'ɵpureObjectDef', moduleUrl: CORE, runtime: ɵpureObjectDef}; static purePipeDef: IdentifierSpec = {name: 'ɵpurePipeDef', moduleUrl: CORE, runtime: ɵpurePipeDef}; static pipeDef: IdentifierSpec = {name: 'ɵpipeDef', moduleUrl: CORE, runtime: ɵpipeDef}; diff --git a/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts b/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts index d4805f767a..6a8637b3ca 100644 --- a/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts +++ b/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts @@ -447,7 +447,9 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter dirAst.directive.queries.forEach((query, queryIndex) => { let flags = NodeFlags.HasContentQuery; const queryId = dirAst.contentQueryStartId + queryIndex; - if (queryIds.staticQueryIds.has(queryId)) { + // Note: We only make queries static that query for a single item. + // This is because of backwards compatibility with the old view compiler... + if (queryIds.staticQueryIds.has(queryId) && query.first) { flags |= NodeFlags.HasStaticQuery; } else { flags |= NodeFlags.HasDynamicQuery; diff --git a/modules/@angular/core/src/application_ref.ts b/modules/@angular/core/src/application_ref.ts index f94ecc2e41..b95e810e23 100644 --- a/modules/@angular/core/src/application_ref.ts +++ b/modules/@angular/core/src/application_ref.ts @@ -493,7 +493,7 @@ export class ApplicationRef_ extends ApplicationRef { detachView(viewRef: ViewRef): void { const view = (viewRef as InternalViewRef); ListWrapper.remove(this._views, view); - view.detachFromContainer(); + view.detachFromAppRef(); } bootstrap(componentOrFactory: ComponentFactory|Type): ComponentRef { diff --git a/modules/@angular/core/src/linker/view_ref.ts b/modules/@angular/core/src/linker/view_ref.ts index bceff42160..f546417044 100644 --- a/modules/@angular/core/src/linker/view_ref.ts +++ b/modules/@angular/core/src/linker/view_ref.ts @@ -89,7 +89,7 @@ export abstract class EmbeddedViewRef extends ViewRef { } export interface InternalViewRef extends ViewRef { - detachFromContainer(): void; + detachFromAppRef(): void; attachToAppRef(appRef: ApplicationRef): void; } @@ -131,7 +131,7 @@ export class ViewRef_ implements EmbeddedViewRef, ChangeDetectorRef, Inter destroy() { this._view.detachAndDestroy(); } - detachFromContainer() { this._view.detach(); } + detachFromAppRef() { this._view.detach(); } attachToAppRef(appRef: ApplicationRef) { this._view.attachToAppRef(appRef); } } diff --git a/modules/@angular/core/src/view/element.ts b/modules/@angular/core/src/view/element.ts index eac14c2242..25cbec9275 100644 --- a/modules/@angular/core/src/view/element.ts +++ b/modules/@angular/core/src/view/element.ts @@ -213,29 +213,33 @@ function renderEventHandlerClosure(view: ViewData, index: number, eventName: str export function checkAndUpdateElementInline( 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): boolean { const bindLen = def.bindings.length; - if (bindLen > 0) checkAndUpdateElementValue(view, def, 0, v0); - if (bindLen > 1) checkAndUpdateElementValue(view, def, 1, v1); - if (bindLen > 2) checkAndUpdateElementValue(view, def, 2, v2); - if (bindLen > 3) checkAndUpdateElementValue(view, def, 3, v3); - if (bindLen > 4) checkAndUpdateElementValue(view, def, 4, v4); - if (bindLen > 5) checkAndUpdateElementValue(view, def, 5, v5); - if (bindLen > 6) checkAndUpdateElementValue(view, def, 6, v6); - if (bindLen > 7) checkAndUpdateElementValue(view, def, 7, v7); - if (bindLen > 8) checkAndUpdateElementValue(view, def, 8, v8); - if (bindLen > 9) checkAndUpdateElementValue(view, def, 9, v9); + let changed = false; + if (bindLen > 0 && checkAndUpdateElementValue(view, def, 0, v0)) changed = true; + if (bindLen > 1 && checkAndUpdateElementValue(view, def, 1, v1)) changed = true; + if (bindLen > 2 && checkAndUpdateElementValue(view, def, 2, v2)) changed = true; + if (bindLen > 3 && checkAndUpdateElementValue(view, def, 3, v3)) changed = true; + if (bindLen > 4 && checkAndUpdateElementValue(view, def, 4, v4)) changed = true; + if (bindLen > 5 && checkAndUpdateElementValue(view, def, 5, v5)) changed = true; + if (bindLen > 6 && checkAndUpdateElementValue(view, def, 6, v6)) changed = true; + if (bindLen > 7 && checkAndUpdateElementValue(view, def, 7, v7)) changed = true; + if (bindLen > 8 && checkAndUpdateElementValue(view, def, 8, v8)) changed = true; + if (bindLen > 9 && checkAndUpdateElementValue(view, def, 9, v9)) changed = true; + return changed; } -export function checkAndUpdateElementDynamic(view: ViewData, def: NodeDef, values: any[]) { +export function checkAndUpdateElementDynamic(view: ViewData, def: NodeDef, values: any[]): boolean { + let changed = false; for (let i = 0; i < values.length; i++) { - checkAndUpdateElementValue(view, def, i, values[i]); + if (checkAndUpdateElementValue(view, def, i, values[i])) changed = true; } + return changed; } function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: number, value: any) { if (!checkAndUpdateBinding(view, def, bindingIdx, value)) { - return; + return false; } const binding = def.bindings[bindingIdx]; const elData = asElementData(view, def.index); @@ -258,6 +262,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu setElementProperty(elData.componentView, binding, renderNode, name, value); break; } + return true; } function setElementAttribute( diff --git a/modules/@angular/core/src/view/provider.ts b/modules/@angular/core/src/view/provider.ts index 64a48a356f..722558ff83 100644 --- a/modules/@angular/core/src/view/provider.ts +++ b/modules/@angular/core/src/view/provider.ts @@ -16,7 +16,7 @@ import {Renderer as RendererV1, RendererFactoryV2, RendererTypeV2, RendererV2} f import {createChangeDetectorRef, createInjector, createRendererV1, createTemplateRef, createViewContainerRef} from './refs'; import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, OutputDef, OutputType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types'; -import {checkAndUpdateBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util'; +import {checkBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util'; const RendererV1TokenKey = tokenKey(RendererV1); const RendererV2TokenKey = tokenKey(RendererV2); @@ -156,21 +156,52 @@ function eventHandlerClosure(view: ViewData, index: number, eventName: string) { export function checkAndUpdateDirectiveInline( 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): boolean { const providerData = asProviderData(view, def.index); const directive = providerData.instance; + let changed = false; let changes: SimpleChanges; const bindLen = def.bindings.length; - if (bindLen > 0) changes = checkAndUpdateProp(view, providerData, def, 0, v0, changes); - if (bindLen > 1) changes = checkAndUpdateProp(view, providerData, def, 1, v1, changes); - if (bindLen > 2) changes = checkAndUpdateProp(view, providerData, def, 2, v2, changes); - if (bindLen > 3) changes = checkAndUpdateProp(view, providerData, def, 3, v3, changes); - if (bindLen > 4) changes = checkAndUpdateProp(view, providerData, def, 4, v4, changes); - if (bindLen > 5) changes = checkAndUpdateProp(view, providerData, def, 5, v5, changes); - if (bindLen > 6) changes = checkAndUpdateProp(view, providerData, def, 6, v6, changes); - if (bindLen > 7) changes = checkAndUpdateProp(view, providerData, def, 7, v7, changes); - if (bindLen > 8) changes = checkAndUpdateProp(view, providerData, def, 8, v8, changes); - if (bindLen > 9) changes = checkAndUpdateProp(view, providerData, def, 9, v9, changes); + if (bindLen > 0 && checkBinding(view, def, 0, v0)) { + changed = true; + changes = updateProp(view, providerData, def, 0, v0, changes); + }; + if (bindLen > 1 && checkBinding(view, def, 1, v1)) { + changed = true; + changes = updateProp(view, providerData, def, 1, v1, changes); + }; + if (bindLen > 2 && checkBinding(view, def, 2, v2)) { + changed = true; + changes = updateProp(view, providerData, def, 2, v2, changes); + }; + if (bindLen > 3 && checkBinding(view, def, 3, v3)) { + changed = true; + changes = updateProp(view, providerData, def, 3, v3, changes); + }; + if (bindLen > 4 && checkBinding(view, def, 4, v4)) { + changed = true; + changes = updateProp(view, providerData, def, 4, v4, changes); + }; + if (bindLen > 5 && checkBinding(view, def, 5, v5)) { + changed = true; + changes = updateProp(view, providerData, def, 5, v5, changes); + }; + if (bindLen > 6 && checkBinding(view, def, 6, v6)) { + changed = true; + changes = updateProp(view, providerData, def, 6, v6, changes); + }; + if (bindLen > 7 && checkBinding(view, def, 7, v7)) { + changed = true; + changes = updateProp(view, providerData, def, 7, v7, changes); + }; + if (bindLen > 8 && checkBinding(view, def, 8, v8)) { + changed = true; + changes = updateProp(view, providerData, def, 8, v8, changes); + }; + if (bindLen > 9 && checkBinding(view, def, 9, v9)) { + changed = true; + changes = updateProp(view, providerData, def, 9, v9, changes); + }; if (changes) { directive.ngOnChanges(changes); } @@ -180,14 +211,20 @@ export function checkAndUpdateDirectiveInline( if (def.flags & NodeFlags.DoCheck) { directive.ngDoCheck(); } + return changed; } -export function checkAndUpdateDirectiveDynamic(view: ViewData, def: NodeDef, values: any[]) { +export function checkAndUpdateDirectiveDynamic( + view: ViewData, def: NodeDef, values: any[]): boolean { const providerData = asProviderData(view, def.index); const directive = providerData.instance; + let changed = false; let changes: SimpleChanges; for (let i = 0; i < values.length; i++) { - changes = checkAndUpdateProp(view, providerData, def, i, values[i], changes); + if (checkBinding(view, def, i, values[i])) { + changed = true; + changes = updateProp(view, providerData, def, i, values[i], changes); + } } if (changes) { directive.ngOnChanges(changes); @@ -198,6 +235,7 @@ export function checkAndUpdateDirectiveDynamic(view: ViewData, def: NodeDef, val if (def.flags & NodeFlags.DoCheck) { directive.ngDoCheck(); } + return changed; } function _createProviderInstance(view: ViewData, def: NodeDef): any { @@ -366,38 +404,29 @@ function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: bool return compView; } -function checkAndUpdateProp( +function updateProp( view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any, changes: SimpleChanges): SimpleChanges { - let change: SimpleChange; - let changed: boolean; + if (def.flags & NodeFlags.IsComponent) { + const compView = asElementData(view, def.parent.index).componentView; + if (compView.def.flags & ViewFlags.OnPush) { + compView.state |= ViewState.ChecksEnabled; + } + } + const binding = def.bindings[bindingIdx]; + const propName = binding.name; + // Note: This is still safe with Closure Compiler as + // the user passed in the property name as an object has to `providerDef`, + // so Closure Compiler will have renamed the property correctly already. + providerData.instance[propName] = value; if (def.flags & NodeFlags.OnChanges) { + changes = changes || {}; const oldValue = view.oldValues[def.bindingIndex + bindingIdx]; - changed = checkAndUpdateBinding(view, def, bindingIdx, value); - change = changed ? - new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0) : - null; - } else { - changed = checkAndUpdateBinding(view, def, bindingIdx, value); - } - if (changed) { - if (def.flags & NodeFlags.IsComponent) { - const compView = asElementData(view, def.parent.index).componentView; - if (compView.def.flags & ViewFlags.OnPush) { - compView.state |= ViewState.ChecksEnabled; - } - } const binding = def.bindings[bindingIdx]; - const propName = binding.name; - // Note: This is still safe with Closure Compiler as - // the user passed in the property name as an object has to `providerDef`, - // so Closure Compiler will have renamed the property correctly already. - providerData.instance[propName] = value; - if (change) { - changes = changes || {}; - changes[binding.nonMinifiedName] = change; - } + changes[binding.nonMinifiedName] = + new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0); } + view.oldValues[def.bindingIndex + bindingIdx] = value; return changes; } diff --git a/modules/@angular/core/src/view/pure_expression.ts b/modules/@angular/core/src/view/pure_expression.ts index 05f483add4..9e5e7d2579 100644 --- a/modules/@angular/core/src/view/pure_expression.ts +++ b/modules/@angular/core/src/view/pure_expression.ts @@ -69,7 +69,7 @@ export function createPureExpression(view: ViewData, def: NodeDef): PureExpressi export function checkAndUpdatePureExpressionInline( 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): boolean { const bindings = def.bindings; let changed = false; const bindLen = bindings.length; @@ -84,8 +84,8 @@ export function checkAndUpdatePureExpressionInline( if (bindLen > 8 && checkAndUpdateBinding(view, def, 8, v8)) changed = true; if (bindLen > 9 && checkAndUpdateBinding(view, def, 9, v9)) changed = true; - const data = asPureExpressionData(view, def.index); if (changed) { + const data = asPureExpressionData(view, def.index); let value: any; switch (def.pureExpression.type) { case PureExpressionType.Array: @@ -152,10 +152,11 @@ export function checkAndUpdatePureExpressionInline( } data.value = value; } - return data.value; + return changed; } -export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef, values: any[]) { +export function checkAndUpdatePureExpressionDynamic( + view: ViewData, def: NodeDef, values: any[]): boolean { const bindings = def.bindings; let changed = false; for (let i = 0; i < values.length; i++) { @@ -165,8 +166,8 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef changed = true; } } - const data = asPureExpressionData(view, def.index); if (changed) { + const data = asPureExpressionData(view, def.index); let value: any; switch (def.pureExpression.type) { case PureExpressionType.Array: @@ -186,5 +187,5 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef } data.value = value; } - return data.value; + return changed; } diff --git a/modules/@angular/core/src/view/refs.ts b/modules/@angular/core/src/view/refs.ts index 2246fb81c4..4b3aa4ac39 100644 --- a/modules/@angular/core/src/view/refs.ts +++ b/modules/@angular/core/src/view/refs.ts @@ -20,7 +20,8 @@ import {Type} from '../type'; import {VERSION} from '../version'; import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types'; -import {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util'; +import {isComponentView, markParentViewsForCheck, renderNode, resolveViewDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util'; +import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachView} from './view_attach'; const EMPTY_CONTEXT = new Object(); @@ -98,14 +99,13 @@ class ViewContainerRef_ implements ViewContainerRef { clear(): void { const len = this._data.embeddedViews.length; for (let i = len - 1; i >= 0; i--) { - const view = Services.detachEmbeddedView(this._data, i); + const view = detachEmbeddedView(this._data, i); Services.destroyView(view); } } - get(index: number): ViewRef { return this._getViewRef(this._data.embeddedViews[index]); } - - private _getViewRef(view: ViewData) { + get(index: number): ViewRef { + const view = this._data.embeddedViews[index]; if (view) { const ref = new ViewRef_(view); ref.attachToViewContainerRef(this); @@ -135,14 +135,14 @@ class ViewContainerRef_ implements ViewContainerRef { insert(viewRef: ViewRef, index?: number): ViewRef { const viewRef_ = viewRef; const viewData = viewRef_._view; - Services.attachEmbeddedView(this._data, index, viewData); + attachEmbeddedView(this._view, this._data, index, viewData); viewRef_.attachToViewContainerRef(this); return viewRef; } move(viewRef: ViewRef_, currentIndex: number): ViewRef { const previousIndex = this._data.embeddedViews.indexOf(viewRef._view); - Services.moveEmbeddedView(this._data, previousIndex, currentIndex); + moveEmbeddedView(this._data, previousIndex, currentIndex); return viewRef; } @@ -151,20 +151,15 @@ class ViewContainerRef_ implements ViewContainerRef { } remove(index?: number): void { - const viewData = Services.detachEmbeddedView(this._data, index); + const viewData = detachEmbeddedView(this._data, index); if (viewData) { Services.destroyView(viewData); } } detach(index?: number): ViewRef { - const view = Services.detachEmbeddedView(this._data, index); - if (view) { - const viewRef = this._getViewRef(view); - viewRef.detachFromContainer(); - return viewRef; - } - return null; + const view = detachEmbeddedView(this._data, index); + return view ? new ViewRef_(view) : null; } } @@ -190,7 +185,7 @@ export class ViewRef_ implements EmbeddedViewRef, InternalViewRef { get destroyed(): boolean { return (this._view.state & ViewState.Destroyed) !== 0; } - markForCheck(): void { this.reattach(); } + markForCheck(): void { markParentViewsForCheck(this._view); } detach(): void { this._view.state &= ~ViewState.ChecksEnabled; } detectChanges(): void { Services.checkAndUpdateView(this._view); } checkNoChanges(): void { Services.checkNoChangesView(this._view); } @@ -212,9 +207,10 @@ export class ViewRef_ implements EmbeddedViewRef, InternalViewRef { Services.destroyView(this._view); } - detachFromContainer() { + detachFromAppRef() { this._appRef = null; - this._viewContainerRef = null; + renderDetachView(this._view); + Services.dirtyParentQueries(this._view); } attachToAppRef(appRef: ApplicationRef) { diff --git a/modules/@angular/core/src/view/services.ts b/modules/@angular/core/src/view/services.ts index ed75e27def..f0b4c730a0 100644 --- a/modules/@angular/core/src/view/services.ts +++ b/modules/@angular/core/src/view/services.ts @@ -14,12 +14,11 @@ import {Sanitizer, SecurityContext} from '../security'; import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors'; import {resolveDep} from './provider'; -import {getQueryValue} from './query'; +import {dirtyParentQueries, getQueryValue} from './query'; import {createInjector} from './refs'; -import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types'; +import {ArgumentType, BindingType, CheckType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asPureExpressionData} from './types'; import {checkBinding, isComponentView, renderNode, viewParentEl} from './util'; -import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view'; -import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach'; +import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view'; let initialized = false; @@ -35,14 +34,12 @@ export function initServicesIfNeeded() { Services.checkAndUpdateView = services.checkAndUpdateView; Services.checkNoChangesView = services.checkNoChangesView; Services.destroyView = services.destroyView; - Services.attachEmbeddedView = services.attachEmbeddedView, - Services.detachEmbeddedView = services.detachEmbeddedView, - Services.moveEmbeddedView = services.moveEmbeddedView; - Services.resolveDep = services.resolveDep; + Services.resolveDep = resolveDep; Services.createDebugContext = services.createDebugContext; Services.handleEvent = services.handleEvent; Services.updateDirectives = services.updateDirectives; Services.updateRenderer = services.updateRenderer; + Services.dirtyParentQueries = dirtyParentQueries; } function createProdServices() { @@ -53,16 +50,17 @@ function createProdServices() { checkAndUpdateView: checkAndUpdateView, checkNoChangesView: checkNoChangesView, destroyView: destroyView, - attachEmbeddedView: attachEmbeddedView, - detachEmbeddedView: detachEmbeddedView, - moveEmbeddedView: moveEmbeddedView, - resolveDep: resolveDep, createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex), handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) => view.def.handleEvent(view, nodeIndex, eventName, event), - updateDirectives: (check: NodeCheckFn, view: ViewData) => - view.def.updateDirectives(check, view), - updateRenderer: (check: NodeCheckFn, view: ViewData) => view.def.updateRenderer(check, view), + updateDirectives: (view: ViewData, checkType: CheckType) => view.def.updateDirectives( + checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode : + prodCheckNoChangesNode, + view), + updateRenderer: (view: ViewData, checkType: CheckType) => view.def.updateRenderer( + checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode : + prodCheckNoChangesNode, + view), }; } @@ -74,10 +72,6 @@ function createDebugServices() { checkAndUpdateView: debugCheckAndUpdateView, checkNoChangesView: debugCheckNoChangesView, destroyView: debugDestroyView, - attachEmbeddedView: attachEmbeddedView, - detachEmbeddedView: detachEmbeddedView, - moveEmbeddedView: moveEmbeddedView, - resolveDep: resolveDep, createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex), handleEvent: debugHandleEvent, updateDirectives: debugUpdateDirectives, @@ -115,6 +109,24 @@ function createRootData( }; } +function prodCheckAndUpdateNode( + view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, + v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any { + const nodeDef = view.def.nodes[nodeIndex]; + checkAndUpdateNode(view, nodeDef, argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + return (nodeDef.type === NodeType.PureExpression) ? asPureExpressionData(view, nodeIndex).value : + undefined; +} + +function prodCheckNoChangesNode( + view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, + v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any { + const nodeDef = view.def.nodes[nodeIndex]; + checkNoChangesNode(view, nodeDef, argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + return (nodeDef.type === NodeType.PureExpression) ? asPureExpressionData(view, nodeIndex).value : + undefined; +} + function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData { return callWithDebugContext( DebugAction.create, createEmbeddedView, null, [parent, anchorDef, context]); @@ -158,7 +170,7 @@ function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string, DebugAction.handleEvent, view.def.handleEvent, null, [view, nodeIndex, eventName, event]); } -function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) { +function debugUpdateDirectives(view: ViewData, checkType: CheckType) { if (view.state & ViewState.Destroyed) { throw viewDestroyedError(DebugAction[_currentAction]); } @@ -167,15 +179,22 @@ function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) { function debugCheckDirectivesFn( view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) { - const result = debugCheckFn(check, view, nodeIndex, argStyle, values); - if (view.def.nodes[nodeIndex].type === NodeType.Directive) { + const nodeDef = view.def.nodes[nodeIndex]; + if (checkType === CheckType.CheckAndUpdate) { + debugCheckAndUpdateNode(view, nodeDef, argStyle, values); + } else { + debugCheckNoChangesNode(view, nodeDef, argStyle, values); + } + if (nodeDef.type === NodeType.Directive) { debugSetCurrentNode(view, nextDirectiveWithBinding(view, nodeIndex)); } - return result; + return (nodeDef.type === NodeType.PureExpression) ? + asPureExpressionData(view, nodeDef.index).value : + undefined; }; } -function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) { +function debugUpdateRenderer(view: ViewData, checkType: CheckType) { if (view.state & ViewState.Destroyed) { throw viewDestroyedError(DebugAction[_currentAction]); } @@ -184,21 +203,26 @@ function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) { function debugCheckRenderNodeFn( view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) { - const result = debugCheckFn(check, view, nodeIndex, argStyle, values); const nodeDef = view.def.nodes[nodeIndex]; + if (checkType === CheckType.CheckAndUpdate) { + debugCheckAndUpdateNode(view, nodeDef, argStyle, values); + } else { + debugCheckNoChangesNode(view, nodeDef, argStyle, values); + } if (nodeDef.type === NodeType.Element || nodeDef.type === NodeType.Text) { debugSetCurrentNode(view, nextRenderNodeWithBinding(view, nodeIndex)); } - return result; + return (nodeDef.type === NodeType.PureExpression) ? + asPureExpressionData(view, nodeDef.index).value : + undefined; } } -function debugCheckFn( - delegate: NodeCheckFn, view: ViewData, nodeIndex: number, argStyle: ArgumentType, - givenValues: any[]) { - if (_currentAction === DebugAction.detectChanges) { +function debugCheckAndUpdateNode( + view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, givenValues: any[]): void { + const changed = (checkAndUpdateNode)(view, nodeDef, argStyle, ...givenValues); + if (changed) { const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues; - const nodeDef = view.def.nodes[nodeIndex]; if (nodeDef.type === NodeType.Directive || nodeDef.type === NodeType.Element) { const bindingValues: {[key: string]: string} = {}; for (let i = 0; i < nodeDef.bindings.length; i++) { @@ -206,8 +230,7 @@ function debugCheckFn( const value = values[i]; if ((binding.type === BindingType.ElementProperty || binding.type === BindingType.ComponentHostProperty || - binding.type === BindingType.DirectiveProperty) && - checkBinding(view, nodeDef, i, value)) { + binding.type === BindingType.DirectiveProperty)) { bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] = normalizeDebugBindingValue(value); } @@ -225,8 +248,12 @@ function debugCheckFn( } } } - return (delegate)(view, nodeIndex, argStyle, ...givenValues); -}; +} + +function debugCheckNoChangesNode( + view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, values: any[]): void { + (checkNoChangesNode)(view, nodeDef, argStyle, ...values); +} function normalizeDebugBindingName(name: string) { // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers diff --git a/modules/@angular/core/src/view/text.ts b/modules/@angular/core/src/view/text.ts index 5c1da286d9..99b0330afd 100644 --- a/modules/@angular/core/src/view/text.ts +++ b/modules/@angular/core/src/view/text.ts @@ -66,7 +66,7 @@ export function createText(view: ViewData, renderHost: any, def: NodeDef): TextD export function checkAndUpdateTextInline( 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): boolean { let changed = false; const bindings = def.bindings; const bindLen = bindings.length; @@ -96,9 +96,10 @@ export function checkAndUpdateTextInline( const renderNode = asTextData(view, def.index).renderText; view.renderer.setValue(renderNode, value); } + return changed; } -export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values: any[]) { +export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values: any[]): boolean { const bindings = def.bindings; let changed = false; for (let i = 0; i < values.length; i++) { @@ -117,6 +118,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values: const renderNode = asTextData(view, def.index).renderText; view.renderer.setValue(renderNode, value); } + return changed; } function _addInterpolationPart(value: any, binding: BindingDef): string { diff --git a/modules/@angular/core/src/view/types.ts b/modules/@angular/core/src/view/types.ts index 36951c4036..fc8ce56880 100644 --- a/modules/@angular/core/src/view/types.ts +++ b/modules/@angular/core/src/view/types.ts @@ -312,6 +312,7 @@ export interface ViewData { // index of component provider / anchor. parentNodeDef: NodeDef; parent: ViewData; + viewContainerParent: ViewData; component: any; context: any; // Attention: Never loop over this, as this will @@ -448,6 +449,11 @@ export abstract class DebugContext { // Other // ------------------------------------- +export enum CheckType { + CheckAndUpdate, + CheckNoChanges +} + export interface Services { setCurrentNode(view: ViewData, nodeIndex: number): void; createRootView( @@ -456,17 +462,15 @@ export interface Services { createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData; checkAndUpdateView(view: ViewData): void; checkNoChangesView(view: ViewData): void; - attachEmbeddedView(elementData: ElementData, viewIndex: number, view: ViewData): void; - detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData; - moveEmbeddedView(elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData; destroyView(view: ViewData): void; resolveDep( view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef, notFoundValue?: any): any; createDebugContext(view: ViewData, nodeIndex: number): DebugContext; handleEvent: ViewHandleEventFn; - updateDirectives: ViewUpdateFn; - updateRenderer: ViewUpdateFn; + updateDirectives: (view: ViewData, checkType: CheckType) => void; + updateRenderer: (view: ViewData, checkType: CheckType) => void; + dirtyParentQueries: (view: ViewData) => void; } /** @@ -480,12 +484,10 @@ export const Services: Services = { checkAndUpdateView: undefined, checkNoChangesView: undefined, destroyView: undefined, - attachEmbeddedView: undefined, - detachEmbeddedView: undefined, - moveEmbeddedView: undefined, resolveDep: undefined, createDebugContext: undefined, handleEvent: undefined, updateDirectives: undefined, updateRenderer: undefined, + dirtyParentQueries: undefined, }; diff --git a/modules/@angular/core/src/view/util.ts b/modules/@angular/core/src/view/util.ts index 3e71d7ee1a..b940ac653e 100644 --- a/modules/@angular/core/src/view/util.ts +++ b/modules/@angular/core/src/view/util.ts @@ -60,9 +60,22 @@ export function createRendererTypeV2(values: { export function checkBinding( view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean { - const oldValue = view.oldValues[def.bindingIndex + bindingIdx]; - return unwrapCounter > 0 || !!(view.state & ViewState.FirstCheck) || - !devModeEqual(oldValue, value); + const oldValues = view.oldValues; + if (unwrapCounter > 0 || !!(view.state & ViewState.FirstCheck) || + !looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) { + unwrapCounter = 0; + return true; + } + return false; +} + +export function checkAndUpdateBinding( + view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean { + if (checkBinding(view, def, bindingIdx, value)) { + view.oldValues[def.bindingIndex + bindingIdx] = value; + return true; + } + return false; } export function checkBindingNoChanges( @@ -76,27 +89,19 @@ export function checkBindingNoChanges( } } -export function checkAndUpdateBinding( - view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean { - const oldValues = view.oldValues; - if (unwrapCounter || (view.state & ViewState.FirstCheck) || - !looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) { - unwrapCounter = 0; - oldValues[def.bindingIndex + bindingIdx] = value; - return true; - } - return false; -} - -export function dispatchEvent( - view: ViewData, nodeIndex: number, eventName: string, event: any): boolean { +export function markParentViewsForCheck(view: ViewData) { let currView = view; while (currView) { if (currView.def.flags & ViewFlags.OnPush) { currView.state |= ViewState.ChecksEnabled; } - currView = currView.parent; + currView = currView.viewContainerParent || currView.parent; } +} + +export function dispatchEvent( + view: ViewData, nodeIndex: number, eventName: string, event: any): boolean { + markParentViewsForCheck(view); return Services.handleEvent(view, nodeIndex, eventName, event); } diff --git a/modules/@angular/core/src/view/view.ts b/modules/@angular/core/src/view/view.ts index f1a91a7fdf..4c6815c9a8 100644 --- a/modules/@angular/core/src/view/view.ts +++ b/modules/@angular/core/src/view/view.ts @@ -16,7 +16,7 @@ import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAn import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression'; import {checkAndUpdateQuery, createQuery, queryDef} from './query'; import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text'; -import {ArgumentType, ElementData, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList, asTextData} from './types'; +import {ArgumentType, CheckType, ElementData, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList, asTextData} from './types'; import {checkBindingNoChanges, isComponentView, resolveViewDefinition, viewParentEl} from './util'; const NOOP = (): any => undefined; @@ -246,7 +246,7 @@ function createView( const view: ViewData = { def, parent, - parentNodeDef, + viewContainerParent: undefined, parentNodeDef, context: undefined, component: undefined, nodes, state: ViewState.FirstCheck | ViewState.ChecksEnabled, root, renderer, @@ -339,35 +339,35 @@ function createViewNodes(view: ViewData) { // fill static content and view queries execQueriesAction( view, NodeFlags.HasContentQuery | NodeFlags.HasViewQuery, NodeFlags.HasStaticQuery, - QueryAction.CheckAndUpdate); + CheckType.CheckAndUpdate); } export function checkNoChangesView(view: ViewData) { - Services.updateDirectives(checkNoChangesNode, view); + Services.updateDirectives(view, CheckType.CheckNoChanges); execEmbeddedViewsAction(view, ViewAction.CheckNoChanges); execQueriesAction( - view, NodeFlags.HasContentQuery, NodeFlags.HasDynamicQuery, QueryAction.CheckNoChanges); - Services.updateRenderer(checkNoChangesNode, view); + view, NodeFlags.HasContentQuery, NodeFlags.HasDynamicQuery, CheckType.CheckNoChanges); + Services.updateRenderer(view, CheckType.CheckNoChanges); execComponentViewsAction(view, ViewAction.CheckNoChanges); execQueriesAction( - view, NodeFlags.HasViewQuery, NodeFlags.HasDynamicQuery, QueryAction.CheckNoChanges); + view, NodeFlags.HasViewQuery, NodeFlags.HasDynamicQuery, CheckType.CheckNoChanges); } export function checkAndUpdateView(view: ViewData) { - Services.updateDirectives(checkAndUpdateNode, view); + Services.updateDirectives(view, CheckType.CheckAndUpdate); execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate); execQueriesAction( - view, NodeFlags.HasContentQuery, NodeFlags.HasDynamicQuery, QueryAction.CheckAndUpdate); + view, NodeFlags.HasContentQuery, NodeFlags.HasDynamicQuery, CheckType.CheckAndUpdate); callLifecycleHooksChildrenFirst( view, NodeFlags.AfterContentChecked | (view.state & ViewState.FirstCheck ? NodeFlags.AfterContentInit : 0)); - Services.updateRenderer(checkAndUpdateNode, view); + Services.updateRenderer(view, CheckType.CheckAndUpdate); execComponentViewsAction(view, ViewAction.CheckAndUpdate); execQueriesAction( - view, NodeFlags.HasViewQuery, NodeFlags.HasDynamicQuery, QueryAction.CheckAndUpdate); + view, NodeFlags.HasViewQuery, NodeFlags.HasDynamicQuery, CheckType.CheckAndUpdate); callLifecycleHooksChildrenFirst( view, NodeFlags.AfterViewChecked | @@ -379,61 +379,83 @@ export function checkAndUpdateView(view: ViewData) { view.state &= ~ViewState.FirstCheck; } -function checkAndUpdateNode( - view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, - v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any { +export function checkAndUpdateNode( + view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, + v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): boolean { if (argStyle === ArgumentType.Inline) { - return checkAndUpdateNodeInline(view, nodeIndex, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + return checkAndUpdateNodeInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); } else { - return checkAndUpdateNodeDynamic(view, nodeIndex, v0); + return checkAndUpdateNodeDynamic(view, nodeDef, v0); } } function checkAndUpdateNodeInline( - view: ViewData, nodeIndex: number, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any, - v6?: any, v7?: any, v8?: any, v9?: any): any { - const nodeDef = view.def.nodes[nodeIndex]; + view: ViewData, nodeDef: NodeDef, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any, + v6?: any, v7?: any, v8?: any, v9?: any): boolean { + let changed = false; switch (nodeDef.type) { case NodeType.Element: - return checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + changed = checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + break; case NodeType.Text: - return checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + changed = checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + break; case NodeType.Directive: - return checkAndUpdateDirectiveInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + changed = + checkAndUpdateDirectiveInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + break; case NodeType.PureExpression: - return checkAndUpdatePureExpressionInline( - view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + changed = + checkAndUpdatePureExpressionInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + break; } + return changed; } -function checkAndUpdateNodeDynamic(view: ViewData, nodeIndex: number, values: any[]): any { - const nodeDef = view.def.nodes[nodeIndex]; +function checkAndUpdateNodeDynamic(view: ViewData, nodeDef: NodeDef, values: any[]): boolean { + let changed = false; switch (nodeDef.type) { case NodeType.Element: - return checkAndUpdateElementDynamic(view, nodeDef, values); + changed = checkAndUpdateElementDynamic(view, nodeDef, values); + break; case NodeType.Text: - return checkAndUpdateTextDynamic(view, nodeDef, values); + changed = checkAndUpdateTextDynamic(view, nodeDef, values); + break; case NodeType.Directive: - return checkAndUpdateDirectiveDynamic(view, nodeDef, values); + changed = checkAndUpdateDirectiveDynamic(view, nodeDef, values); + break; case NodeType.PureExpression: - return checkAndUpdatePureExpressionDynamic(view, nodeDef, values); + changed = checkAndUpdatePureExpressionDynamic(view, nodeDef, values); + break; } + if (changed) { + // Update oldValues after all bindings have been updated, + // as a setter for a property might update other properties. + const bindLen = nodeDef.bindings.length; + const bindingStart = nodeDef.bindingIndex; + const oldValues = view.oldValues; + for (let i = 0; i < bindLen; i++) { + oldValues[bindingStart + i] = values[i]; + } + } + return changed; } -function checkNoChangesNode( - view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, +export function checkNoChangesNode( + view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any { if (argStyle === ArgumentType.Inline) { - return checkNoChangesNodeInline(view, nodeIndex, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); + checkNoChangesNodeInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); } else { - return checkNoChangesNodeDynamic(view, nodeIndex, v0); + checkNoChangesNodeDynamic(view, nodeDef, v0); } + // Returning false is ok here as we would have thrown in case of a change. + return false; } function checkNoChangesNodeInline( - view: ViewData, nodeIndex: number, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, - v6: any, v7: any, v8: any, v9: any): void { - const nodeDef = view.def.nodes[nodeIndex]; + view: ViewData, nodeDef: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, + v7: any, v8: any, v9: any): void { const bindLen = nodeDef.bindings.length; if (bindLen > 0) checkBindingNoChanges(view, nodeDef, 0, v0); if (bindLen > 1) checkBindingNoChanges(view, nodeDef, 1, v1); @@ -445,17 +467,12 @@ function checkNoChangesNodeInline( if (bindLen > 7) checkBindingNoChanges(view, nodeDef, 7, v7); if (bindLen > 8) checkBindingNoChanges(view, nodeDef, 8, v8); if (bindLen > 9) checkBindingNoChanges(view, nodeDef, 9, v9); - return nodeDef.type === NodeType.PureExpression ? asPureExpressionData(view, nodeIndex).value : - undefined; } -function checkNoChangesNodeDynamic(view: ViewData, nodeIndex: number, values: any[]): void { - const nodeDef = view.def.nodes[nodeIndex]; +function checkNoChangesNodeDynamic(view: ViewData, nodeDef: NodeDef, values: any[]): void { for (let i = 0; i < values.length; i++) { checkBindingNoChanges(view, nodeDef, i, values[i]); } - return nodeDef.type === NodeType.PureExpression ? asPureExpressionData(view, nodeIndex).value : - undefined; } function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) { @@ -574,13 +591,9 @@ function callViewAction(view: ViewData, action: ViewAction) { } } -enum QueryAction { - CheckAndUpdate, - CheckNoChanges -} - function execQueriesAction( - view: ViewData, queryFlags: NodeFlags, staticDynamicQueryFlag: NodeFlags, action: QueryAction) { + view: ViewData, queryFlags: NodeFlags, staticDynamicQueryFlag: NodeFlags, + checkType: CheckType) { if (!(view.def.nodeFlags & queryFlags) || !(view.def.nodeFlags & staticDynamicQueryFlag)) { return; } @@ -589,11 +602,11 @@ function execQueriesAction( const nodeDef = view.def.nodes[i]; if ((nodeDef.flags & queryFlags) && (nodeDef.flags & staticDynamicQueryFlag)) { Services.setCurrentNode(view, nodeDef.index); - switch (action) { - case QueryAction.CheckAndUpdate: + switch (checkType) { + case CheckType.CheckAndUpdate: checkAndUpdateQuery(view, nodeDef); break; - case QueryAction.CheckNoChanges: + case CheckType.CheckNoChanges: checkNoChangesQuery(view, nodeDef); break; } diff --git a/modules/@angular/core/src/view/view_attach.ts b/modules/@angular/core/src/view/view_attach.ts index c7e1e01ed3..1cd9d9858c 100644 --- a/modules/@angular/core/src/view/view_attach.ts +++ b/modules/@angular/core/src/view/view_attach.ts @@ -6,15 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {dirtyParentQueries} from './query'; -import {ElementData, NodeData, NodeDef, NodeFlags, NodeType, ViewData, asElementData, asProviderData, asTextData} from './types'; +import {ElementData, NodeData, NodeDef, NodeFlags, NodeType, Services, ViewData, asElementData, asProviderData, asTextData} from './types'; import {RenderNodeAction, declaredViewContainer, isComponentView, renderNode, rootRenderNodes, visitProjectedRenderNodes, visitRootRenderNodes} from './util'; -export function attachEmbeddedView(elementData: ElementData, viewIndex: number, view: ViewData) { +export function attachEmbeddedView( + parentView: ViewData, elementData: ElementData, viewIndex: number, view: ViewData) { let embeddedViews = elementData.embeddedViews; if (viewIndex == null) { viewIndex = embeddedViews.length; } + view.viewContainerParent = parentView; addToArray(embeddedViews, viewIndex, view); const dvcElementData = declaredViewContainer(view); if (dvcElementData && dvcElementData !== elementData) { @@ -25,7 +26,7 @@ export function attachEmbeddedView(elementData: ElementData, viewIndex: number, projectedViews.push(view); } - dirtyParentQueries(view); + Services.dirtyParentQueries(view); const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null; renderAttachEmbeddedView(elementData, prevView, view); @@ -40,6 +41,7 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex: number): return null; } const view = embeddedViews[viewIndex]; + view.viewContainerParent = undefined; removeFromArray(embeddedViews, viewIndex); const dvcElementData = declaredViewContainer(view); @@ -48,9 +50,9 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex: number): removeFromArray(projectedViews, projectedViews.indexOf(view)); } - dirtyParentQueries(view); + Services.dirtyParentQueries(view); - renderDetachEmbeddedView(elementData, view); + renderDetachView(view); return view; } @@ -68,9 +70,9 @@ export function moveEmbeddedView( // Note: Don't need to change projectedViews as the order in there // as always invalid... - dirtyParentQueries(view); + Services.dirtyParentQueries(view); - renderDetachEmbeddedView(elementData, view); + renderDetachView(view); const prevView = newViewIndex > 0 ? embeddedViews[newViewIndex - 1] : null; renderAttachEmbeddedView(elementData, prevView, view); @@ -87,9 +89,8 @@ function renderAttachEmbeddedView(elementData: ElementData, prevView: ViewData, visitRootRenderNodes(view, RenderNodeAction.InsertBefore, parentNode, nextSibling, undefined); } -function renderDetachEmbeddedView(elementData: ElementData, view: ViewData) { - const parentNode = view.renderer.parentNode(elementData.renderElement); - visitRootRenderNodes(view, RenderNodeAction.RemoveChild, parentNode, null, undefined); +export function renderDetachView(view: ViewData) { + visitRootRenderNodes(view, RenderNodeAction.RemoveChild, null, null, undefined); } function addToArray(arr: any[], index: number, value: any) { diff --git a/modules/@angular/core/test/application_ref_spec.ts b/modules/@angular/core/test/application_ref_spec.ts index c77a9f4122..34c4fe2c2e 100644 --- a/modules/@angular/core/test/application_ref_spec.ts +++ b/modules/@angular/core/test/application_ref_spec.ts @@ -78,7 +78,7 @@ export function main() { it('should throw when reentering tick', inject([ApplicationRef], (ref: ApplicationRef_) => { const view = jasmine.createSpyObj('view', ['detach', 'attachToAppRef']); const viewRef = jasmine.createSpyObj( - 'viewRef', ['detectChanges', 'detachFromContainer', 'attachToAppRef']); + 'viewRef', ['detectChanges', 'detachFromAppRef', 'attachToAppRef']); viewRef.internalView = view; view.ref = viewRef; try { diff --git a/modules/@angular/core/test/view/embedded_view_spec.ts b/modules/@angular/core/test/view/embedded_view_spec.ts index 8fd5b94f55..5f3e6866aa 100644 --- a/modules/@angular/core/test/view/embedded_view_spec.ts +++ b/modules/@angular/core/test/view/embedded_view_spec.ts @@ -66,8 +66,8 @@ export function main() { const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]); - attachEmbeddedView(viewContainerData, 0, childView0); - attachEmbeddedView(viewContainerData, 1, childView1); + attachEmbeddedView(parentView, viewContainerData, 0, childView0); + attachEmbeddedView(parentView, viewContainerData, 1, childView1); // 2 anchors + 2 elements const rootChildren = getDOM().childNodes(rootNodes[0]); @@ -96,8 +96,8 @@ export function main() { const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]); - attachEmbeddedView(viewContainerData, 0, childView0); - attachEmbeddedView(viewContainerData, 1, childView1); + attachEmbeddedView(parentView, viewContainerData, 0, childView0); + attachEmbeddedView(parentView, viewContainerData, 1, childView1); moveEmbeddedView(viewContainerData, 0, 1); @@ -118,7 +118,7 @@ export function main() { ])); const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[0]); - attachEmbeddedView(asElementData(parentView, 0), 0, childView0); + attachEmbeddedView(parentView, asElementData(parentView, 0), 0, childView0); const rootNodes = rootRenderNodes(parentView); expect(rootNodes.length).toBe(3); @@ -146,7 +146,7 @@ export function main() { const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); - attachEmbeddedView(asElementData(parentView, 1), 0, childView0); + attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0); Services.checkAndUpdateView(parentView); @@ -180,7 +180,7 @@ export function main() { const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); - attachEmbeddedView(asElementData(parentView, 1), 0, childView0); + attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0); Services.destroyView(parentView); expect(log).toEqual(['ngOnDestroy']); diff --git a/modules/@angular/core/test/view/ng_content_spec.ts b/modules/@angular/core/test/view/ng_content_spec.ts index a1322e6ac2..69761f4e5f 100644 --- a/modules/@angular/core/test/view/ng_content_spec.ts +++ b/modules/@angular/core/test/view/ng_content_spec.ts @@ -114,7 +114,7 @@ export function main() { const componentView = asElementData(view, 0).componentView; const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]); - attachEmbeddedView(asElementData(componentView, 1), 0, view0); + attachEmbeddedView(view, asElementData(componentView, 1), 0, view0); expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(3); expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0]))[1]) .toBe(asTextData(view, 2).renderText); diff --git a/modules/@angular/core/test/view/query_spec.ts b/modules/@angular/core/test/view/query_spec.ts index bcfcad9d59..b56bd92afd 100644 --- a/modules/@angular/core/test/view/query_spec.ts +++ b/modules/@angular/core/test/view/query_spec.ts @@ -156,7 +156,7 @@ export function main() { ])); const childView = Services.createEmbeddedView(view, view.def.nodes[3]); - attachEmbeddedView(asElementData(view, 3), 0, childView); + attachEmbeddedView(view, asElementData(view, 3), 0, childView); Services.checkAndUpdateView(view); // queries on parent elements of anchors @@ -185,7 +185,7 @@ export function main() { const childView = Services.createEmbeddedView(view, view.def.nodes[3]); // attach at a different place than the one where the template was defined - attachEmbeddedView(asElementData(view, 7), 0, childView); + attachEmbeddedView(view, asElementData(view, 7), 0, childView); Services.checkAndUpdateView(view); @@ -215,12 +215,13 @@ export function main() { expect(qs.a.length).toBe(0); const childView = Services.createEmbeddedView(view, view.def.nodes[3]); - attachEmbeddedView(asElementData(view, 3), 0, childView); + attachEmbeddedView(view, asElementData(view, 3), 0, childView); Services.checkAndUpdateView(view); expect(qs.a.length).toBe(1); detachEmbeddedView(asElementData(view, 3), 0); + Services.checkAndUpdateView(view); expect(qs.a.length).toBe(0); @@ -245,7 +246,7 @@ export function main() { const compView = asElementData(view, 0).componentView; const childView = Services.createEmbeddedView(compView, compView.def.nodes[1]); - attachEmbeddedView(asElementData(compView, 1), 0, childView); + attachEmbeddedView(view, asElementData(compView, 1), 0, childView); Services.checkAndUpdateView(view); expect(comp.a.length).toBe(1); @@ -381,7 +382,7 @@ export function main() { Services.checkNoChangesView(view); const childView = Services.createEmbeddedView(view, view.def.nodes[3]); - attachEmbeddedView(asElementData(view, 3), 0, childView); + attachEmbeddedView(view, asElementData(view, 3), 0, childView); let err: any; try {