fix(animations): not waiting for child animations to finish when removing parent in Ivy (#34702)
In #28162 we introduced an extra `removeNode` call for host elements which can cause the parent element to be removed before all child animations have finished. The issue is only in Ivy, because that the only place where we pass in the `isHostElement` flag. These changes fix the issue by not re-triggering the removal logic if the element has in-progress animations. Fixes #33597. PR Close #34702
This commit is contained in:
parent
15ae924035
commit
697f6a55a5
|
@ -437,11 +437,14 @@ export class AnimationTransitionNamespace {
|
||||||
if (containsPotentialParentTransition) {
|
if (containsPotentialParentTransition) {
|
||||||
engine.markElementAsRemoved(this.id, element, false, context);
|
engine.markElementAsRemoved(this.id, element, false, context);
|
||||||
} else {
|
} else {
|
||||||
// we do this after the flush has occurred such
|
const removalFlag = element[REMOVAL_FLAG];
|
||||||
// that the callbacks can be fired
|
if (!removalFlag || removalFlag === NULL_REMOVAL_STATE) {
|
||||||
engine.afterFlush(() => this.clearElementCache(element));
|
// we do this after the flush has occurred such
|
||||||
engine.destroyInnerAnimations(element);
|
// that the callbacks can be fired
|
||||||
engine._onRemovalComplete(element, context);
|
engine.afterFlush(() => this.clearElementCache(element));
|
||||||
|
engine.destroyInnerAnimations(element);
|
||||||
|
engine._onRemovalComplete(element, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -932,6 +932,53 @@ const DEFAULT_COMPONENT_ID = '1';
|
||||||
expect(fixture.debugElement.nativeElement.children.length).toBe(0);
|
expect(fixture.debugElement.nativeElement.children.length).toBe(0);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should wait for child animations before removing parent', fakeAsync(() => {
|
||||||
|
@Component({
|
||||||
|
template: '<child-cmp *ngIf="exp" @parentTrigger></child-cmp>',
|
||||||
|
animations: [trigger(
|
||||||
|
'parentTrigger', [transition(':leave', [group([query('@*', animateChild())])])])]
|
||||||
|
})
|
||||||
|
class ParentCmp {
|
||||||
|
exp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'child-cmp',
|
||||||
|
template: '<p @childTrigger>Hello there</p>',
|
||||||
|
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
|
// animationRenderer => nonAnimationRenderer
|
||||||
it('should trigger a leave animation when the outer components element binding updates on the host component element',
|
it('should trigger a leave animation when the outer components element binding updates on the host component element',
|
||||||
fakeAsync(() => {
|
fakeAsync(() => {
|
||||||
|
|
Loading…
Reference in New Issue