From 0d3e314df056ed89c56b39b3d01bd56a25a7db9f Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Fri, 17 Mar 2017 11:04:30 -0700 Subject: [PATCH] fix(core): trigger host animations for elements that are removed. (#15251) Fixes #14813 Fixes #15193 --- .../src/view_compiler/view_compiler.ts | 6 +++--- packages/core/src/view/element.ts | 6 ++++-- packages/core/src/view/types.ts | 4 +++- packages/core/src/view/util.ts | 21 ++++++++++++------- .../animations/src/animation_renderer.ts | 10 ++++++++- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/packages/compiler/src/view_compiler/view_compiler.ts b/packages/compiler/src/view_compiler/view_compiler.ts index 0f2c31781e..450fba5bd2 100644 --- a/packages/compiler/src/view_compiler/view_compiler.ts +++ b/packages/compiler/src/view_compiler/view_compiler.ts @@ -1030,9 +1030,9 @@ function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveA o.literal(inputAst.securityContext) ]); case PropertyBindingType.Animation: - const bindingType = dirAst && dirAst.directive.isComponent ? - BindingFlags.TypeProperty | BindingFlags.SyntheticComponentHostProperty : - BindingFlags.TypeProperty; + const bindingType = BindingFlags.TypeProperty | + (dirAst && dirAst.directive.isComponent ? BindingFlags.SyntheticHostProperty : + BindingFlags.SyntheticProperty); return o.literalArr([ o.literal(bindingType), o.literal('@' + inputAst.name), o.literal(inputAst.securityContext) ]); diff --git a/packages/core/src/view/element.ts b/packages/core/src/view/element.ts index 3bba958a8a..848fa1bb5c 100644 --- a/packages/core/src/view/element.ts +++ b/packages/core/src/view/element.ts @@ -238,8 +238,10 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu setElementStyle(view, binding, renderNode, name, value); break; case BindingFlags.TypeProperty: - const bindView = - binding.flags & BindingFlags.SyntheticComponentHostProperty ? elData.componentView : view; + const bindView = (def.flags & NodeFlags.ComponentView && + binding.flags & BindingFlags.SyntheticHostProperty) ? + elData.componentView : + view; setElementProperty(bindView, binding, renderNode, name, value); break; } diff --git a/packages/core/src/view/types.ts b/packages/core/src/view/types.ts index f74c732e85..41810ff186 100644 --- a/packages/core/src/view/types.ts +++ b/packages/core/src/view/types.ts @@ -195,7 +195,9 @@ export const enum BindingFlags { TypeElementClass = 1 << 1, TypeElementStyle = 1 << 2, TypeProperty = 1 << 3, - SyntheticComponentHostProperty = 1 << 4, + SyntheticProperty = 1 << 4, + SyntheticHostProperty = 1 << 5, + CatSyntheticProperty = SyntheticProperty | SyntheticHostProperty, // mutually exclusive values... Types = TypeElementAttribute | TypeElementClass | TypeElementStyle | TypeProperty diff --git a/packages/core/src/view/util.ts b/packages/core/src/view/util.ts index 27ca1feb82..4e13eb7506 100644 --- a/packages/core/src/view/util.ts +++ b/packages/core/src/view/util.ts @@ -230,12 +230,7 @@ export function rootRenderNodes(view: ViewData): any[] { return renderNodes; } -export enum RenderNodeAction { - Collect, - AppendChild, - InsertBefore, - RemoveChild -} +export const enum RenderNodeAction {Collect, AppendChild, InsertBefore, RemoveChild} export function visitRootRenderNodes( view: ViewData, action: RenderNodeAction, parentNode: any, nextSibling: any, target: any[]) { @@ -298,7 +293,19 @@ function visitRenderNode( view, nodeDef.ngContent.index, action, parentNode, nextSibling, target); } else { const rn = renderNode(view, nodeDef); - execRenderNodeAction(view, rn, action, parentNode, nextSibling, target); + if (action === RenderNodeAction.RemoveChild && (nodeDef.flags & NodeFlags.ComponentView) && + (nodeDef.bindingFlags & BindingFlags.CatSyntheticProperty)) { + // Note: we might need to do both actions. + if (nodeDef.bindingFlags & (BindingFlags.SyntheticProperty)) { + execRenderNodeAction(view, rn, action, parentNode, nextSibling, target); + } + if (nodeDef.bindingFlags & (BindingFlags.SyntheticHostProperty)) { + const compView = asElementData(view, nodeDef.index).componentView; + execRenderNodeAction(compView, rn, action, parentNode, nextSibling, target); + } + } else { + execRenderNodeAction(view, rn, action, parentNode, nextSibling, target); + } if (nodeDef.flags & NodeFlags.EmbeddedViews) { const embeddedViews = asElementData(view, nodeDef.index).viewContainer._embeddedViews; for (let k = 0; k < embeddedViews.length; k++) { diff --git a/packages/platform-browser/animations/src/animation_renderer.ts b/packages/platform-browser/animations/src/animation_renderer.ts index 6da022fccb..02850265e2 100644 --- a/packages/platform-browser/animations/src/animation_renderer.ts +++ b/packages/platform-browser/animations/src/animation_renderer.ts @@ -90,7 +90,15 @@ export class AnimationRenderer implements Renderer2 { } removeChild(parent: any, oldChild: any): void { - this._engine.onRemove(oldChild, () => this.delegate.removeChild(parent, oldChild)); + this._engine.onRemove(oldChild, () => { + // Note: if an component element has a leave animation, and the component + // a host leave animation, the view engine will call `removeChild` for the parent + // component renderer as well as for the child component renderer. + // Therefore, we need to check if we already removed the element. + if (this.delegate.parentNode(oldChild)) { + this.delegate.removeChild(parent, oldChild); + } + }); this._queueFlush(); }