@@ -141,7 +141,7 @@ export class AnimationTransitionNamespace {
141141 if ( ! triggersWithStates . hasOwnProperty ( name ) ) {
142142 addClass ( element , NG_TRIGGER_CLASSNAME ) ;
143143 addClass ( element , NG_TRIGGER_CLASSNAME + '-' + name ) ;
144- triggersWithStates [ name ] = null ;
144+ triggersWithStates [ name ] = DEFAULT_STATE_VALUE ;
145145 }
146146
147147 return ( ) => {
@@ -305,47 +305,87 @@ export class AnimationTransitionNamespace {
305305 }
306306 }
307307
308- private _destroyInnerNodes ( rootElement : any , context : any , animate : boolean = false ) {
308+ private _signalRemovalForInnerTriggers ( rootElement : any , context : any , animate : boolean = false ) {
309309 // emulate a leave animation for all inner nodes within this node.
310310 // If there are no animations found for any of the nodes then clear the cache
311311 // for the element.
312312 this . _engine . driver . query ( rootElement , NG_TRIGGER_SELECTOR , true ) . forEach ( elm => {
313313 const namespaces = this . _engine . fetchNamespacesByElement ( elm ) ;
314314 if ( namespaces . size ) {
315- namespaces . forEach ( ns => ns . removeNode ( elm , context , true ) ) ;
315+ namespaces . forEach ( ns => { ns . triggerLeaveAnimation ( elm , context , false , true ) ; } ) ;
316316 } else {
317317 this . clearElementCache ( elm ) ;
318318 }
319319 } ) ;
320320 }
321321
322- removeNode ( element : any , context : any , doNotRecurse ?: boolean ) : void {
323- const engine = this . _engine ;
324-
325- if ( ! doNotRecurse && element . childElementCount ) {
326- this . _destroyInnerNodes ( element , context , true ) ;
327- }
328-
329- const triggerStates = engine . statesByElement . get ( element ) ;
322+ triggerLeaveAnimation (
323+ element : any , context : any , destroyAfterComplete ?: boolean ,
324+ defaultToFallback ?: boolean ) : boolean {
325+ const triggerStates = this . _engine . statesByElement . get ( element ) ;
330326 if ( triggerStates ) {
331327 const players : TransitionAnimationPlayer [ ] = [ ] ;
332328 Object . keys ( triggerStates ) . forEach ( triggerName => {
333329 // this check is here in the event that an element is removed
334330 // twice (both on the host level and the component level)
335331 if ( this . _triggers [ triggerName ] ) {
336- const player = this . trigger ( element , triggerName , VOID_VALUE , false ) ;
332+ const player = this . trigger ( element , triggerName , VOID_VALUE , defaultToFallback ) ;
337333 if ( player ) {
338334 players . push ( player ) ;
339335 }
340336 }
341337 } ) ;
342338
343339 if ( players . length ) {
344- engine . markElementAsRemoved ( this . id , element , true , context ) ;
345- optimizeGroupPlayer ( players ) . onDone ( ( ) => engine . processLeaveNode ( element ) ) ;
346- return ;
340+ this . _engine . markElementAsRemoved ( this . id , element , true , context ) ;
341+ if ( destroyAfterComplete ) {
342+ optimizeGroupPlayer ( players ) . onDone ( ( ) => this . _engine . processLeaveNode ( element ) ) ;
343+ }
344+ return true ;
347345 }
348346 }
347+ return false ;
348+ }
349+
350+ prepareLeaveAnimationListeners ( element : any ) {
351+ const listeners = this . _elementListeners . get ( element ) ;
352+ if ( listeners ) {
353+ const visitedTriggers = new Set < string > ( ) ;
354+ listeners . forEach ( listener => {
355+ const triggerName = listener . name ;
356+ if ( visitedTriggers . has ( triggerName ) ) return ;
357+ visitedTriggers . add ( triggerName ) ;
358+
359+ const trigger = this . _triggers [ triggerName ] ;
360+ const transition = trigger . fallbackTransition ;
361+ const elementStates = this . _engine . statesByElement . get ( element ) ! ;
362+ const fromState = elementStates [ triggerName ] || DEFAULT_STATE_VALUE ;
363+ const toState = new StateValue ( VOID_VALUE ) ;
364+ const player = new TransitionAnimationPlayer ( this . id , triggerName , element ) ;
365+
366+ this . _engine . totalQueuedPlayers ++ ;
367+ this . _queue . push ( {
368+ element,
369+ triggerName,
370+ transition,
371+ fromState,
372+ toState,
373+ player,
374+ isFallbackTransition : true
375+ } ) ;
376+ } ) ;
377+ }
378+ }
379+
380+ removeNode ( element : any , context : any ) : void {
381+ const engine = this . _engine ;
382+
383+ if ( element . childElementCount ) {
384+ this . _signalRemovalForInnerTriggers ( element , context , true ) ;
385+ }
386+
387+ // this means that a * => VOID animation was detected and kicked off
388+ if ( this . triggerLeaveAnimation ( element , context , true ) ) return ;
349389
350390 // find the player that is animating and make sure that the
351391 // removal is delayed until that player has completed
@@ -376,33 +416,7 @@ export class AnimationTransitionNamespace {
376416 // during flush or will be picked up by a parent query. Either way
377417 // we need to fire the listeners for this element when it DOES get
378418 // removed (once the query parent animation is done or after flush)
379- const listeners = this . _elementListeners . get ( element ) ;
380- if ( listeners ) {
381- const visitedTriggers = new Set < string > ( ) ;
382- listeners . forEach ( listener => {
383- const triggerName = listener . name ;
384- if ( visitedTriggers . has ( triggerName ) ) return ;
385- visitedTriggers . add ( triggerName ) ;
386-
387- const trigger = this . _triggers [ triggerName ] ;
388- const transition = trigger . fallbackTransition ;
389- const elementStates = engine . statesByElement . get ( element ) ! ;
390- const fromState = elementStates [ triggerName ] || DEFAULT_STATE_VALUE ;
391- const toState = new StateValue ( VOID_VALUE ) ;
392- const player = new TransitionAnimationPlayer ( this . id , triggerName , element ) ;
393-
394- this . _engine . totalQueuedPlayers ++ ;
395- this . _queue . push ( {
396- element,
397- triggerName,
398- transition,
399- fromState,
400- toState,
401- player,
402- isFallbackTransition : true
403- } ) ;
404- } ) ;
405- }
419+ this . prepareLeaveAnimationListeners ( element ) ;
406420
407421 // whether or not a parent has an animation we need to delay the deferral of the leave
408422 // operation until we have more information (which we do after flush() has been called)
@@ -465,7 +479,7 @@ export class AnimationTransitionNamespace {
465479
466480 destroy ( context : any ) {
467481 this . players . forEach ( p => p . destroy ( ) ) ;
468- this . _destroyInnerNodes ( this . hostElement , context ) ;
482+ this . _signalRemovalForInnerTriggers ( this . hostElement , context ) ;
469483 }
470484
471485 elementContainsData ( element : any ) : boolean {
@@ -668,15 +682,15 @@ export class TransitionAnimationEngine {
668682 }
669683 }
670684
671- removeNode ( namespaceId : string , element : any , context : any , doNotRecurse ?: boolean ) : void {
685+ removeNode ( namespaceId : string , element : any , context : any ) : void {
672686 if ( ! isElementNode ( element ) ) {
673687 this . _onRemovalComplete ( element , context ) ;
674688 return ;
675689 }
676690
677691 const ns = namespaceId ? this . _fetchNamespace ( namespaceId ) : null ;
678692 if ( ns ) {
679- ns . removeNode ( element , context , doNotRecurse ) ;
693+ ns . removeNode ( element , context ) ;
680694 } else {
681695 this . markElementAsRemoved ( namespaceId , element , false , context ) ;
682696 }
@@ -708,37 +722,39 @@ export class TransitionAnimationEngine {
708722
709723 destroyInnerAnimations ( containerElement : any ) {
710724 let elements = this . driver . query ( containerElement , NG_TRIGGER_SELECTOR , true ) ;
711- elements . forEach ( element => {
712- const players = this . playersByElement . get ( element ) ;
713- if ( players ) {
714- players . forEach ( player => {
715- // special case for when an element is set for destruction, but hasn't started.
716- // in this situation we want to delay the destruction until the flush occurs
717- // so that any event listeners attached to the player are triggered.
718- if ( player . queued ) {
719- player . markedForDestroy = true ;
720- } else {
721- player . destroy ( ) ;
722- }
723- } ) ;
724- }
725- const stateMap = this . statesByElement . get ( element ) ;
726- if ( stateMap ) {
727- Object . keys ( stateMap ) . forEach ( triggerName => stateMap [ triggerName ] = DELETED_STATE_VALUE ) ;
728- }
729- } ) ;
725+ elements . forEach ( element => this . destroyActiveAnimationsForElement ( element ) ) ;
730726
731727 if ( this . playersByQueriedElement . size == 0 ) return ;
732728
733729 elements = this . driver . query ( containerElement , NG_ANIMATING_SELECTOR , true ) ;
734- if ( elements . length ) {
735- elements . forEach ( element => {
736- const players = this . playersByQueriedElement . get ( element ) ;
737- if ( players ) {
738- players . forEach ( player => player . finish ( ) ) ;
730+ elements . forEach ( element => this . finishActiveQueriedAnimationOnElement ( element ) ) ;
731+ }
732+
733+ destroyActiveAnimationsForElement ( element : any ) {
734+ const players = this . playersByElement . get ( element ) ;
735+ if ( players ) {
736+ players . forEach ( player => {
737+ // special case for when an element is set for destruction, but hasn't started.
738+ // in this situation we want to delay the destruction until the flush occurs
739+ // so that any event listeners attached to the player are triggered.
740+ if ( player . queued ) {
741+ player . markedForDestroy = true ;
742+ } else {
743+ player . destroy ( ) ;
739744 }
740745 } ) ;
741746 }
747+ const stateMap = this . statesByElement . get ( element ) ;
748+ if ( stateMap ) {
749+ Object . keys ( stateMap ) . forEach ( triggerName => stateMap [ triggerName ] = DELETED_STATE_VALUE ) ;
750+ }
751+ }
752+
753+ finishActiveQueriedAnimationOnElement ( element : any ) {
754+ const players = this . playersByQueriedElement . get ( element ) ;
755+ if ( players ) {
756+ players . forEach ( player => player . finish ( ) ) ;
757+ }
742758 }
743759
744760 whenRenderingDone ( ) : Promise < any > {
0 commit comments