( lView: LView, tNode: TNode, value: string | AnimationClassBindingFn, ngZone: NgZone, )
| 92 | } |
| 93 | |
| 94 | export function runEnterAnimation( |
| 95 | lView: LView, |
| 96 | tNode: TNode, |
| 97 | value: string | AnimationClassBindingFn, |
| 98 | ngZone: NgZone, |
| 99 | ): void { |
| 100 | const nativeElement = getNativeByTNode(tNode, lView) as HTMLElement; |
| 101 | |
| 102 | ngDevMode && assertElementNodes(nativeElement, 'animate.enter'); |
| 103 | |
| 104 | const renderer = lView[RENDERER]; |
| 105 | |
| 106 | // Retrieve the actual class list from the value. This will resolve any resolver functions from |
| 107 | // bindings. |
| 108 | const activeClasses = getClassListFromValue(value); |
| 109 | const cleanupFns: VoidFunction[] = []; |
| 110 | let hasCompleted = false; |
| 111 | |
| 112 | // In the case where multiple animations are happening on the element, we need |
| 113 | // to get the longest animation to ensure we don't complete animations early. |
| 114 | // This also allows us to setup cancellation of animations in progress if the |
| 115 | // gets removed early. |
| 116 | const handleEnterAnimationStart = (event: AnimationEvent | TransitionEvent) => { |
| 117 | // this early exit case is to prevent issues with bubbling events that are from child element animations |
| 118 | if (getEventTarget(event) !== nativeElement) return; |
| 119 | |
| 120 | const eventName = event instanceof AnimationEvent ? 'animationend' : 'transitionend'; |
| 121 | ngZone.runOutsideAngular(() => { |
| 122 | renderer.listen(nativeElement, eventName, handleEnterAnimationEnd); |
| 123 | }); |
| 124 | }; |
| 125 | |
| 126 | // When the longest animation ends, we can remove all the classes |
| 127 | const handleEnterAnimationEnd = (event: AnimationEvent | TransitionEvent) => { |
| 128 | // this early exit case is to prevent issues with bubbling events that are from child element animations |
| 129 | if (getEventTarget(event) !== nativeElement) return; |
| 130 | |
| 131 | if (isLongestAnimation(event, nativeElement)) { |
| 132 | hasCompleted = true; |
| 133 | } |
| 134 | enterAnimationEnd(event, nativeElement, renderer); |
| 135 | }; |
| 136 | |
| 137 | // We only need to add these event listeners if there are actual classes to apply |
| 138 | if (activeClasses && activeClasses.length > 0) { |
| 139 | ngZone.runOutsideAngular(() => { |
| 140 | cleanupFns.push(renderer.listen(nativeElement, 'animationstart', handleEnterAnimationStart)); |
| 141 | cleanupFns.push(renderer.listen(nativeElement, 'transitionstart', handleEnterAnimationStart)); |
| 142 | }); |
| 143 | |
| 144 | trackEnterClasses(nativeElement, activeClasses, cleanupFns); |
| 145 | |
| 146 | for (const klass of activeClasses) { |
| 147 | renderer.addClass(nativeElement, klass); |
| 148 | } |
| 149 | |
| 150 | // In the case that the classes added have no animations, we need to remove |
| 151 | // the classes right away. This could happen because someone is intentionally |
no test coverage detected
searching dependent graphs…