mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(core): prevent animation events from being cleaned up on destroy (#63414)
This will allow manually subscribed animation events to still fire when using `animate.leave`. Otherwise they were being cleaned up before the animations happened. fixes: #63391 PR Close #63414
This commit is contained in:
parent
9096d45377
commit
802dbcc2a0
5 changed files with 33 additions and 13 deletions
|
|
@ -163,23 +163,33 @@ export function listenToDomEvent(
|
|||
stashEventListenerImpl(lView, target, eventName, wrappedListener);
|
||||
|
||||
const cleanupFn = renderer.listen(target as RElement, eventName, wrappedListener);
|
||||
const idxOrTargetGetter = eventTargetResolver
|
||||
? (_lView: LView) => eventTargetResolver(unwrapRNode(_lView[tNode.index]))
|
||||
: tNode.index;
|
||||
|
||||
storeListenerCleanup(
|
||||
idxOrTargetGetter,
|
||||
tView,
|
||||
lView,
|
||||
eventName,
|
||||
wrappedListener,
|
||||
cleanupFn,
|
||||
false,
|
||||
);
|
||||
// We skip cleaning up animation event types to ensure leaving animation events can be used.
|
||||
// These events should be automatically garbage collected anyway after the element is
|
||||
// removed from the DOM.
|
||||
if (!isAnimationEventType(eventName)) {
|
||||
const idxOrTargetGetter = eventTargetResolver
|
||||
? (_lView: LView) => eventTargetResolver(unwrapRNode(_lView[tNode.index]))
|
||||
: tNode.index;
|
||||
|
||||
storeListenerCleanup(
|
||||
idxOrTargetGetter,
|
||||
tView,
|
||||
lView,
|
||||
eventName,
|
||||
wrappedListener,
|
||||
cleanupFn,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
return hasCoalesced;
|
||||
}
|
||||
|
||||
function isAnimationEventType(eventName: string): boolean {
|
||||
return eventName.startsWith('animation') || eventName.startsWith('transition');
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility function that checks if a given element has already an event handler registered for an
|
||||
* event with a specified name. The TView.cleanup data structure is used to find out which events
|
||||
|
|
|
|||
|
|
@ -47,14 +47,20 @@ describe('Animation', () => {
|
|||
`;
|
||||
|
||||
it('should delay element removal when an animation is specified', fakeAsync(() => {
|
||||
const logSpy = jasmine.createSpy('logSpy');
|
||||
@Component({
|
||||
selector: 'test-cmp',
|
||||
styles: styles,
|
||||
template: '<div>@if (show()) {<p animate.leave="fade">I should fade</p>}</div>',
|
||||
template:
|
||||
'<div>@if (show()) {<p animate.leave="fade" (animationend)="logMe($event)">I should fade</p>}</div>',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
class TestComponent {
|
||||
show = signal(true);
|
||||
|
||||
logMe(event: AnimationEvent) {
|
||||
logSpy();
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({animationsEnabled: true});
|
||||
|
|
@ -76,6 +82,7 @@ describe('Animation', () => {
|
|||
new AnimationEvent('animationend', {animationName: 'fade-out'}),
|
||||
);
|
||||
expect(fixture.nativeElement.outerHTML).not.toContain('class="fade"');
|
||||
expect(logSpy).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should remove right away when animations are disabled', fakeAsync(() => {
|
||||
|
|
|
|||
|
|
@ -696,6 +696,7 @@
|
|||
"invokeHostBindingsInCreationMode",
|
||||
"isAbstractControlOptions",
|
||||
"isAngularZoneProperty",
|
||||
"isAnimationEventType",
|
||||
"isAnimationProp",
|
||||
"isApplicationBootstrapConfig",
|
||||
"isArrayLike",
|
||||
|
|
|
|||
|
|
@ -697,6 +697,7 @@
|
|||
"invokeDirectivesHostBindings",
|
||||
"invokeHostBindingsInCreationMode",
|
||||
"isAngularZoneProperty",
|
||||
"isAnimationEventType",
|
||||
"isAnimationProp",
|
||||
"isApplicationBootstrapConfig",
|
||||
"isArrayLike",
|
||||
|
|
|
|||
|
|
@ -791,6 +791,7 @@
|
|||
"invokeHostBindingsInCreationMode",
|
||||
"isActiveMatchOptions",
|
||||
"isAngularZoneProperty",
|
||||
"isAnimationEventType",
|
||||
"isAnimationProp",
|
||||
"isApplicationBootstrapConfig",
|
||||
"isArrayLike",
|
||||
|
|
|
|||
Loading…
Reference in a new issue