@@ -136,6 +136,11 @@ export function runEnterAnimation(
136136
137137 // We only need to add these event listeners if there are actual classes to apply
138138 if ( activeClasses && activeClasses . length > 0 ) {
139+ let isCleanedUp = false ;
140+ cleanupFns . push ( ( ) => {
141+ isCleanedUp = true ;
142+ } ) ;
143+
139144 ngZone . runOutsideAngular ( ( ) => {
140145 cleanupFns . push ( renderer . listen ( nativeElement , 'animationstart' , handleEnterAnimationStart ) ) ;
141146 cleanupFns . push ( renderer . listen ( nativeElement , 'transitionstart' , handleEnterAnimationStart ) ) ;
@@ -152,14 +157,16 @@ export function runEnterAnimation(
152157 // preventing an animation via selector specificity.
153158 ngZone . runOutsideAngular ( ( ) => {
154159 requestAnimationFrame ( ( ) => {
155- if ( hasCompleted ) return ;
156- determineLongestAnimation ( nativeElement , longestAnimations , areAnimationSupported ) ;
157- if ( ! longestAnimations . has ( nativeElement ) ) {
158- for ( const klass of activeClasses ) {
159- renderer . removeClass ( nativeElement , klass ) ;
160+ setTimeout ( ( ) => {
161+ if ( hasCompleted || isCleanedUp ) return ;
162+ determineLongestAnimation ( nativeElement , longestAnimations , areAnimationSupported ) ;
163+ if ( ! longestAnimations . has ( nativeElement ) ) {
164+ for ( const klass of activeClasses ) {
165+ renderer . removeClass ( nativeElement , klass ) ;
166+ }
167+ cleanupEnterClassData ( nativeElement ) ;
160168 }
161- cleanupEnterClassData ( nativeElement ) ;
162- }
169+ } ) ;
163170 } ) ;
164171 } ) ;
165172 }
@@ -326,6 +333,14 @@ function animateLeaveClassRunner(
326333 const componentResolvers = getLViewLeaveAnimations ( lView ) . get ( tNode . index ) ?. resolvers ;
327334 let fallbackTimeoutId : number | undefined ;
328335 let hasCompleted = false ;
336+ let isCleanedUp = false ;
337+
338+ cleanupFns . push ( ( ) => {
339+ isCleanedUp = true ;
340+ if ( fallbackTimeoutId !== undefined ) {
341+ clearTimeout ( fallbackTimeoutId ) ;
342+ }
343+ } ) ;
329344
330345 const handleOutAnimationEnd = ( event : AnimationEvent | TransitionEvent | CustomEvent ) => {
331346 const target = getEventTarget ( event as Event ) ;
@@ -378,21 +393,22 @@ function animateLeaveClassRunner(
378393 // preventing an animation via selector specificity.
379394 ngZone . runOutsideAngular ( ( ) => {
380395 requestAnimationFrame ( ( ) => {
381- if ( hasCompleted ) return ;
382- determineLongestAnimation ( el , longestAnimations , areAnimationSupported ) ;
383- const longest = longestAnimations . get ( el ) ;
384- if ( ! longest ) {
385- clearLeavingNodes ( tNode , el ) ;
386- cleanupAfterLeaveAnimations ( componentResolvers , cleanupFns ) ;
387- clearLViewNodeAnimationResolvers ( lView , tNode ) ;
388- } else {
389- // Fallback cleanup if the browser drops the transitionend/animationend event
390- // entirely due to off-screen optimizations or rapid DOM teardown.
391- fallbackTimeoutId = setTimeout ( ( ) => {
392- handleOutAnimationEnd ( new CustomEvent ( 'animation-fallback' ) ) ;
393- } , longest . duration + 50 ) as unknown as number ;
394- cleanupFns . push ( ( ) => clearTimeout ( fallbackTimeoutId ) ) ;
395- }
396+ setTimeout ( ( ) => {
397+ if ( hasCompleted || isCleanedUp ) return ;
398+ determineLongestAnimation ( el , longestAnimations , areAnimationSupported ) ;
399+ const longest = longestAnimations . get ( el ) ;
400+ if ( ! longest ) {
401+ clearLeavingNodes ( tNode , el ) ;
402+ cleanupAfterLeaveAnimations ( componentResolvers , cleanupFns ) ;
403+ clearLViewNodeAnimationResolvers ( lView , tNode ) ;
404+ } else {
405+ // Fallback cleanup if the browser drops the transitionend/animationend event
406+ // entirely due to off-screen optimizations or rapid DOM teardown.
407+ fallbackTimeoutId = setTimeout ( ( ) => {
408+ handleOutAnimationEnd ( new CustomEvent ( 'animation-fallback' ) ) ;
409+ } , longest . duration + 50 ) as unknown as number ;
410+ }
411+ } ) ;
396412 } ) ;
397413 } ) ;
398414}
0 commit comments