diff --git a/packages/animations/browser/src/render/transition_animation_engine.ts b/packages/animations/browser/src/render/transition_animation_engine.ts index b3ec5bc052..f3ef0f80bc 100644 --- a/packages/animations/browser/src/render/transition_animation_engine.ts +++ b/packages/animations/browser/src/render/transition_animation_engine.ts @@ -437,11 +437,14 @@ export class AnimationTransitionNamespace { if (containsPotentialParentTransition) { engine.markElementAsRemoved(this.id, element, false, context); } else { - // we do this after the flush has occurred such - // that the callbacks can be fired - engine.afterFlush(() => this.clearElementCache(element)); - engine.destroyInnerAnimations(element); - engine._onRemovalComplete(element, context); + const removalFlag = element[REMOVAL_FLAG]; + if (!removalFlag || removalFlag === NULL_REMOVAL_STATE) { + // we do this after the flush has occurred such + // that the callbacks can be fired + engine.afterFlush(() => this.clearElementCache(element)); + engine.destroyInnerAnimations(element); + engine._onRemovalComplete(element, context); + } } } diff --git a/packages/core/test/animation/animation_integration_spec.ts b/packages/core/test/animation/animation_integration_spec.ts index 4087eff1f4..d2034fb6a2 100644 --- a/packages/core/test/animation/animation_integration_spec.ts +++ b/packages/core/test/animation/animation_integration_spec.ts @@ -932,6 +932,53 @@ const DEFAULT_COMPONENT_ID = '1'; expect(fixture.debugElement.nativeElement.children.length).toBe(0); })); + it('should wait for child animations before removing parent', fakeAsync(() => { + @Component({ + template: '', + animations: [trigger( + 'parentTrigger', [transition(':leave', [group([query('@*', animateChild())])])])] + }) + class ParentCmp { + exp = true; + } + + @Component({ + selector: 'child-cmp', + template: '

Hello there

', + animations: [trigger( + 'childTrigger', + [transition( + ':leave', [style({opacity: 1}), animate('200ms', style({opacity: 0}))])])] + }) + class ChildCmp { + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toBe(0); + + fixture.componentInstance.exp = false; + fixture.detectChanges(); + expect(fixture.nativeElement.children.length).toBe(1); + + engine.flush(); + expect(getLog().length).toBe(1); + + const player = getLog()[0]; + expect(player.keyframes).toEqual([ + {opacity: '1', offset: 0}, + {opacity: '0', offset: 1}, + ]); + + player.finish(); + flushMicrotasks(); + expect(fixture.nativeElement.children.length).toBe(0); + })); + // animationRenderer => nonAnimationRenderer it('should trigger a leave animation when the outer components element binding updates on the host component element', fakeAsync(() => {