From d892aabf6ed0eff810fcd898525299a285f75fba Mon Sep 17 00:00:00 2001 From: kirjs Date: Wed, 1 Oct 2025 18:30:25 -0400 Subject: [PATCH] Revert "fix(core): Fixes animations in conjunction with content projection (#63776)" (#64189) This reverts commit 1cb16fddb58419d3bc2a5855471c67635eec7353. PR Close #64189 --- packages/core/src/animation/interfaces.ts | 25 +-- packages/core/src/animation/utils.ts | 24 +-- .../src/render3/instructions/animation.ts | 98 +++-------- .../render3/instructions/change_detection.ts | 12 ++ .../core/src/render3/node_manipulation.ts | 156 +++++------------ .../core/test/acceptance/animation_spec.ts | 23 +-- .../authoring/signal_inputs_spec.ts | 158 ------------------ .../acceptance/view_container_ref_spec.ts | 2 - .../bundle.golden_symbols.json | 4 +- .../bundling/defer/bundle.golden_symbols.json | 4 +- .../forms_reactive/bundle.golden_symbols.json | 4 +- .../bundle.golden_symbols.json | 4 +- .../hydration/bundle.golden_symbols.json | 4 +- .../router/bundle.golden_symbols.json | 4 +- .../bundle.golden_symbols.json | 4 +- 15 files changed, 95 insertions(+), 431 deletions(-) diff --git a/packages/core/src/animation/interfaces.ts b/packages/core/src/animation/interfaces.ts index d94ec0ae7e3..36a6fcfd6c9 100644 --- a/packages/core/src/animation/interfaces.ts +++ b/packages/core/src/animation/interfaces.ts @@ -18,27 +18,6 @@ export const ANIMATIONS_DISABLED = new InjectionToken( }, ); -export interface AnimationQueue { - queue: Set; - isScheduled: boolean; -} - -/** - * A [DI token](api/core/InjectionToken) for the queue of all animations. - */ -export const ANIMATION_QUEUE = new InjectionToken( - typeof ngDevMode !== 'undefined' && ngDevMode ? 'AnimationQueue' : '', - { - providedIn: 'root', - factory: () => { - return { - queue: new Set(), - isScheduled: false, - }; - }, - }, -); - /** * The event type for when `animate.enter` and `animate.leave` are used with function * callbacks. @@ -99,10 +78,10 @@ export interface LongestAnimation { export interface AnimationLViewData { // Enter animations that apply to nodes in this view - enter?: Map; + enter?: Function[]; // Leave animations that apply to nodes in this view - leave?: Map; + leave?: (() => Promise)[]; // Leave animations that apply to nodes in this view // We chose to use unknown instead of PromiseSettledResult to avoid requiring the type diff --git a/packages/core/src/animation/utils.ts b/packages/core/src/animation/utils.ts index 15a9c9b5492..986fa7570ed 100644 --- a/packages/core/src/animation/utils.ts +++ b/packages/core/src/animation/utils.ts @@ -162,17 +162,17 @@ export function trackLeavingNodes(tNode: TNode, el: HTMLElement): void { /** * Retrieves the list of specified enter animations from the lView */ -export function getLViewEnterAnimations(lView: LView): Map { +export function getLViewEnterAnimations(lView: LView): Function[] { const animationData = (lView[ANIMATIONS] ??= {}); - return (animationData.enter ??= new Map()); + return (animationData.enter ??= []); } /** * Retrieves the list of specified leave animations from the lView */ -export function getLViewLeaveAnimations(lView: LView): Map { +export function getLViewLeaveAnimations(lView: LView): Function[] { const animationData = (lView[ANIMATIONS] ??= {}); - return (animationData.leave ??= new Map()); + return (animationData.leave ??= []); } /** @@ -244,18 +244,8 @@ export function isLongestAnimation( } /** - * Stores a given animation function in the LView's animation map for later execution - * - * @param animations Either the enter or leave animation map from the LView - * @param tNode The TNode the animation is associated with - * @param fn The animation function to be called later + * Determines if a given tNode is a content projection root node. */ -export function addAnimationToLView( - animations: Map, - tNode: TNode, - fn: Function, -) { - const animationFns = animations.get(tNode.index) ?? []; - animationFns.push(fn); - animations.set(tNode.index, animationFns); +export function isTNodeContentProjectionRoot(tNode: TNode): boolean { + return Array.isArray(tNode.projection); } diff --git a/packages/core/src/render3/instructions/animation.ts b/packages/core/src/render3/instructions/animation.ts index 432d63726bb..a409ad08e43 100644 --- a/packages/core/src/render3/instructions/animation.ts +++ b/packages/core/src/render3/instructions/animation.ts @@ -7,13 +7,12 @@ */ import { - ANIMATION_QUEUE, AnimationCallbackEvent, AnimationFunction, MAX_ANIMATION_TIMEOUT, } from '../../animation/interfaces'; import {getLView, getCurrentTNode} from '../state'; -import {RENDERER, INJECTOR, CONTEXT, LView, ANIMATIONS} from '../interfaces/view'; +import {RENDERER, INJECTOR, CONTEXT, LView} from '../interfaces/view'; import {getNativeByTNode} from '../util/view_utils'; import {performanceMarkFeature} from '../../util/performance'; import {Renderer} from '../interfaces/renderer'; @@ -21,11 +20,8 @@ import {NgZone} from '../../zone'; import {determineLongestAnimation, allLeavingAnimations} from '../../animation/longest_animation'; import {TNode} from '../interfaces/node'; import {promiseWithResolvers} from '../../util/promise_with_resolvers'; -import {Injector} from '../../di'; -import {afterEveryRender} from '../after_render/hooks'; import { - addAnimationToLView, areAnimationsDisabled, areAnimationSupported, assertAnimationTypes, @@ -70,16 +66,12 @@ export function ɵɵanimateEnter(value: string | Function): typeof ɵɵanimateEn cancelLeavingNodes(tNode, lView); - addAnimationToLView(getLViewEnterAnimations(lView), tNode, () => - runEnterAnimation(lView, tNode, value), - ); - - queueEnterAnimations(lView); + getLViewEnterAnimations(lView).push(() => runEnterAnimation(lView, tNode, value)); return ɵɵanimateEnter; // For chaining } -export function runEnterAnimation(lView: LView, tNode: TNode, value: string | Function) { +export function runEnterAnimation(lView: LView, tNode: TNode, value: string | Function): void { const nativeElement = getNativeByTNode(tNode, lView) as HTMLElement; ngDevMode && assertElementNodes(nativeElement, 'animate.enter'); @@ -102,7 +94,7 @@ export function runEnterAnimation(lView: LView, tNode: TNode, value: string | Fu const eventName = event instanceof AnimationEvent ? 'animationend' : 'transitionend'; ngZone.runOutsideAngular(() => { - renderer.listen(nativeElement, eventName, handleEnterAnimationEnd); + cleanupFns.push(renderer.listen(nativeElement, eventName, handleEnterAnimationEnd)); }); }; @@ -126,7 +118,6 @@ export function runEnterAnimation(lView: LView, tNode: TNode, value: string | Fu for (const klass of activeClasses) { renderer.addClass(nativeElement, klass); } - // In the case that the classes added have no animations, we need to remove // the classes right away. This could happen because someone is intentionally // preventing an animation via selector specificity. @@ -191,11 +182,7 @@ export function ɵɵanimateEnterListener(value: AnimationFunction): typeof ɵɵa cancelLeavingNodes(tNode, lView); - addAnimationToLView(getLViewEnterAnimations(lView), tNode, () => - runEnterAnimationFunction(lView, tNode, value), - ); - - queueEnterAnimations(lView); + getLViewEnterAnimations(lView).push(() => runEnterAnimationFunction(lView, tNode, value)); return ɵɵanimateEnterListener; } @@ -203,7 +190,7 @@ export function ɵɵanimateEnterListener(value: AnimationFunction): typeof ɵɵa /** * runs enter animations when a custom function is provided */ -function runEnterAnimationFunction(lView: LView, tNode: TNode, value: AnimationFunction): void { +function runEnterAnimationFunction(lView: LView, tNode: TNode, value: AnimationFunction) { const nativeElement = getNativeByTNode(tNode, lView) as HTMLElement; ngDevMode && assertElementNodes(nativeElement, 'animate.enter'); @@ -237,16 +224,19 @@ export function ɵɵanimateLeave(value: string | Function): typeof ɵɵanimateLe const tNode = getCurrentTNode()!; - addAnimationToLView(getLViewLeaveAnimations(lView), tNode, () => - runLeaveAnimations(lView, tNode, value), + getLViewLeaveAnimations(lView).push(() => + runLeaveAnimations(lView, tNode, value, animationsDisabled), ); - enableAnimationQueueScheduler(lView[INJECTOR]); - return ɵɵanimateLeave; // For chaining } -function runLeaveAnimations(lView: LView, tNode: TNode, value: string | Function): Promise { +function runLeaveAnimations( + lView: LView, + tNode: TNode, + value: string | Function, + animationsDisabled: boolean, +): Promise { const {promise, resolve} = promiseWithResolvers(); const nativeElement = getNativeByTNode(tNode, lView) as Element; @@ -263,6 +253,7 @@ function runLeaveAnimations(lView: LView, tNode: TNode, value: string | Function tNode, activeClasses, renderer, + animationsDisabled, ngZone, resolve, ); @@ -282,11 +273,17 @@ function animateLeaveClassRunner( tNode: TNode, classList: string[], renderer: Renderer, + animationsDisabled: boolean, ngZone: NgZone, resolver: VoidFunction, ) { + if (animationsDisabled) { + longestAnimations.delete(el); + resolver(); + return; + } + cancelAnimationsIfRunning(el, renderer); - const cleanupFns: Function[] = []; const handleOutAnimationEnd = (event: AnimationEvent | TransitionEvent | CustomEvent) => { // this early exit case is to prevent issues with bubbling events that are from child element animations @@ -310,14 +307,11 @@ function animateLeaveClassRunner( } } resolver(); - for (const fn of cleanupFns) { - fn(); - } }; ngZone.runOutsideAngular(() => { - cleanupFns.push(renderer.listen(el, 'animationend', handleOutAnimationEnd)); - cleanupFns.push(renderer.listen(el, 'transitionend', handleOutAnimationEnd)); + renderer.listen(el, 'animationend', handleOutAnimationEnd); + renderer.listen(el, 'transitionend', handleOutAnimationEnd); }); trackLeavingNodes(tNode, el); for (const item of classList) { @@ -332,9 +326,6 @@ function animateLeaveClassRunner( if (!longestAnimations.has(el)) { clearLeavingNodes(tNode, el); resolver(); - for (const fn of cleanupFns) { - fn(); - } } }); }); @@ -368,11 +359,7 @@ export function ɵɵanimateLeaveListener(value: AnimationFunction): typeof ɵɵa const tNode = getCurrentTNode()!; allLeavingAnimations.add(lView); - addAnimationToLView(getLViewLeaveAnimations(lView), tNode, () => - runLeaveAnimationFunction(lView, tNode, value), - ); - - enableAnimationQueueScheduler(lView[INJECTOR]); + getLViewLeaveAnimations(lView).push(() => runLeaveAnimationFunction(lView, tNode, value)); return ɵɵanimateLeaveListener; // For chaining } @@ -429,38 +416,3 @@ function runLeaveAnimationFunction( // Ensure cleanup if the LView is destroyed before the animation runs. return promise; } - -function queueEnterAnimations(lView: LView) { - enableAnimationQueueScheduler(lView[INJECTOR]); - const enterAnimations = lView[ANIMATIONS]?.enter; - if (enterAnimations) { - const animationQueue = lView[INJECTOR].get(ANIMATION_QUEUE); - for (const [_, animateFns] of enterAnimations) { - for (const animateFn of animateFns) { - animationQueue.queue.add(animateFn); - } - } - } -} - -function enableAnimationQueueScheduler(injector: Injector) { - const animationQueue = injector.get(ANIMATION_QUEUE); - // We only need to schedule the animation queue runner once per application. - if (!animationQueue.isScheduled) { - afterEveryRender( - () => { - runQueuedAnimations(injector); - }, - {injector}, - ); - animationQueue.isScheduled = true; - } -} - -function runQueuedAnimations(injector: Injector) { - const animationQueue = injector.get(ANIMATION_QUEUE); - for (let animateFn of animationQueue.queue) { - animateFn(); - } - animationQueue.queue.clear(); -} diff --git a/packages/core/src/render3/instructions/change_detection.ts b/packages/core/src/render3/instructions/change_detection.ts index ec853d9c1f1..53941e04ce3 100644 --- a/packages/core/src/render3/instructions/change_detection.ts +++ b/packages/core/src/render3/instructions/change_detection.ts @@ -25,6 +25,7 @@ import {ComponentTemplate, HostBindingsFunction, RenderFlags} from '../interface import { CONTEXT, EFFECTS_TO_SCHEDULE, + ANIMATIONS, ENVIRONMENT, FLAGS, InitPhaseState, @@ -231,6 +232,7 @@ export function refreshView( if (templateFn !== null) { executeTemplate(tView, lView, templateFn, RenderFlags.Update, context); } + runEnterAnimations(lView); const hooksInitPhaseCompleted = (flags & LViewFlags.InitPhaseStateMask) === InitPhaseState.InitPhaseCompleted; @@ -372,6 +374,16 @@ export function refreshView( } } +function runEnterAnimations(lView: LView) { + const animationData = lView[ANIMATIONS]; + if (animationData?.enter) { + for (const animateFn of animationData.enter) { + animateFn(); + } + animationData.enter = undefined; + } +} + /** * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes * them by executing an associated template function. diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 38d772062c7..08a6f87ec9a 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -75,15 +75,12 @@ import { TVIEW, TView, TViewType, - INJECTOR, } from './interfaces/view'; import {assertTNodeType} from './node_assert'; import {profiler} from './profiler'; import {ProfilerEvent} from './profiler_types'; import {getLViewParent, getNativeByTNode, unwrapRNode} from './util/view_utils'; import {allLeavingAnimations} from '../animation/longest_animation'; -import {ANIMATION_QUEUE} from '../animation/interfaces'; -import {Injector} from '../di'; const enum WalkTNodeTreeAction { /** node create in the native environment. Run on initial creation. */ @@ -102,21 +99,6 @@ const enum WalkTNodeTreeAction { Destroy = 3, } -function maybeQueueEnterAnimation( - parentLView: LView | undefined, - parent: RElement | null, - tNode: TNode, - injector: Injector, -): void { - const enterAnimations = parentLView?.[ANIMATIONS]?.enter; - if (parent !== null && enterAnimations && enterAnimations.has(tNode.index)) { - const animationQueue = injector.get(ANIMATION_QUEUE); - for (const animateFn of enterAnimations.get(tNode.index)!) { - animationQueue.queue.add(animateFn); - } - } -} - /** * NOTE: for performance reasons, the possible actions are inlined within the function instead of * being passed as an argument. @@ -124,10 +106,8 @@ function maybeQueueEnterAnimation( function applyToElementOrContainer( action: WalkTNodeTreeAction, renderer: Renderer, - injector: Injector, parent: RElement | null, lNodeToHandle: RNode | LContainer | LView, - tNode: TNode, beforeNode?: RNode | null, parentLView?: LView, ) { @@ -149,50 +129,33 @@ function applyToElementOrContainer( lNodeToHandle = lNodeToHandle[HOST]!; } const rNode: RNode = unwrapRNode(lNodeToHandle); + if (action === WalkTNodeTreeAction.Create && parent !== null) { - maybeQueueEnterAnimation(parentLView, parent, tNode, injector); if (beforeNode == null) { nativeAppendChild(renderer, parent, rNode); } else { nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true); } } else if (action === WalkTNodeTreeAction.Insert && parent !== null) { - maybeQueueEnterAnimation(parentLView, parent, tNode, injector); nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true); } else if (action === WalkTNodeTreeAction.Detach) { - runLeaveAnimationsWithCallback( - parentLView, - tNode, - injector, - (nodeHasLeaveAnimations: boolean) => { - // the nodeHasLeaveAnimations indicates to the renderer that the element needs to - // be removed synchronously and sets the requireSynchronousElementRemoval flag in - // the renderer. - nativeRemoveNode(renderer, rNode, isComponent, nodeHasLeaveAnimations); - }, - ); + runLeaveAnimationsWithCallback(parentLView, (nodeHasLeaveAnimations: boolean) => { + // the nodeHasLeaveAnimations indicates to the renderer that the element needs to + // be removed synchronously and sets the requireSynchronousElementRemoval flag in + // the renderer. + nativeRemoveNode(renderer, rNode, isComponent, nodeHasLeaveAnimations); + }); } else if (action === WalkTNodeTreeAction.Destroy) { - runLeaveAnimationsWithCallback(parentLView, tNode, injector, () => { + runLeaveAnimationsWithCallback(parentLView, () => { renderer.destroyNode!(rNode); }); } if (lContainer != null) { - applyContainer(renderer, action, injector, lContainer, tNode, parent, beforeNode); + applyContainer(renderer, action, lContainer, parent, beforeNode); } } } -function addToAnimationQueue(injector: Injector, animationFns: Function | Function[]) { - const animationQueue = injector.get(ANIMATION_QUEUE); - if (Array.isArray(animationFns)) { - for (const animateFn of animationFns) { - animationQueue.queue.add(animateFn); - } - } else { - animationQueue.queue.add(animationFns); - } -} - /** * Removes all DOM elements associated with a view. * @@ -390,42 +353,34 @@ function cleanUpView(tView: TView, lView: LView): void { } } -function runLeaveAnimationsWithCallback( - lView: LView | undefined, - tNode: TNode, - injector: Injector, - callback: Function, -) { - const animations = lView?.[ANIMATIONS]; - if (animations == null) return callback(false); - - // this is solely for move operations to prevent leave animations from running - // on the moved nodes, which would have deleted the node. - if (animations.skipLeaveAnimations) { - animations.skipLeaveAnimations = false; - return callback(false); - } - - addToAnimationQueue(injector, () => { - const leaveAnimationMap = animations.leave!; - const leaveAnimations = leaveAnimationMap.get(tNode.index); - const runningAnimations = []; - if (leaveAnimations) { - for (let index = 0; index < leaveAnimations.length; index++) { - const animationFn = leaveAnimations[index]; - runningAnimations.push(animationFn()); - } - } - animations.running = Promise.allSettled(runningAnimations); - runAfterLeaveAnimations(lView!, callback); - }); +function hasLeaveAnimations(lView: LView | undefined): boolean { + return lView !== undefined && lView[ANIMATIONS] !== null && lView[ANIMATIONS].leave !== undefined; } -function runAfterLeaveAnimations(lView: LView, callback: Function) { - const runningAnimations = lView[ANIMATIONS]?.running; - if (runningAnimations) { - runningAnimations.then(() => { - lView[ANIMATIONS]!.running = undefined; +function runLeaveAnimationsWithCallback(lView: LView | undefined, callback: Function) { + if (lView && lView[ANIMATIONS] && lView[ANIMATIONS].leave) { + if (lView[ANIMATIONS].skipLeaveAnimations) { + lView[ANIMATIONS].skipLeaveAnimations = false; + } else { + const leaveAnimations = lView[ANIMATIONS].leave; + const runningAnimations = []; + for (let index = 0; index < leaveAnimations.length; index++) { + const animateFn = leaveAnimations[index]; + runningAnimations.push(animateFn()); + } + lView[ANIMATIONS].running = Promise.allSettled(runningAnimations); + lView[ANIMATIONS].leave = undefined; + } + } + runAfterLeaveAnimations(lView, callback); +} + +function runAfterLeaveAnimations(lView: LView | undefined, callback: Function) { + if (lView && lView[ANIMATIONS] && lView[ANIMATIONS].running) { + lView[ANIMATIONS].running.then(() => { + if (lView[ANIMATIONS] && lView[ANIMATIONS].running) { + lView[ANIMATIONS].running = undefined; + } allLeavingAnimations.delete(lView); callback(true); }); @@ -821,7 +776,6 @@ function applyNodes( ) { while (tNode != null) { ngDevMode && assertTNodeForLView(tNode, lView); - const injector = lView[INJECTOR]; // Let declarations don't have corresponding DOM nodes so we skip over them. if (tNode.type === TNodeType.LetDeclaration) { @@ -848,10 +802,8 @@ function applyNodes( applyToElementOrContainer( action, renderer, - injector, parentRElement, rawSlotValue, - tNode, beforeNode, lView, ); @@ -859,24 +811,13 @@ function applyNodes( const nextRNode = icuContainerIterate(tNode as TIcuContainerNode, lView); let rNode: RNode | null; while ((rNode = nextRNode())) { - applyToElementOrContainer( - action, - renderer, - injector, - parentRElement, - rNode, - tNode, - beforeNode, - lView, - ); + applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode, lView); } applyToElementOrContainer( action, renderer, - injector, parentRElement, rawSlotValue, - tNode, beforeNode, lView, ); @@ -894,10 +835,8 @@ function applyNodes( applyToElementOrContainer( action, renderer, - injector, parentRElement, rawSlotValue, - tNode, beforeNode, lView, ); @@ -1017,16 +956,7 @@ function applyProjectionRecursive( // This should be refactored and cleaned up. for (let i = 0; i < nodeToProjectOrRNodes.length; i++) { const rNode = nodeToProjectOrRNodes[i]; - applyToElementOrContainer( - action, - renderer, - lView[INJECTOR], - parentRElement, - rNode, - tProjectionNode, - beforeNode, - lView, - ); + applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode, lView); } } else { let nodeToProject: TNode | null = nodeToProjectOrRNodes; @@ -1064,9 +994,7 @@ function applyProjectionRecursive( function applyContainer( renderer: Renderer, action: WalkTNodeTreeAction, - injector: Injector, lContainer: LContainer, - tNode: TNode, parentRElement: RElement | null, beforeNode: RNode | null | undefined, ) { @@ -1084,15 +1012,7 @@ function applyContainer( // don't see a reason why they should be different, but they are. // // If they are we need to process the second anchor as well. - applyToElementOrContainer( - action, - renderer, - injector, - parentRElement, - anchor, - tNode, - beforeNode, - ); + applyToElementOrContainer(action, renderer, parentRElement, anchor, beforeNode); } for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { const lView = lContainer[i] as LView; diff --git a/packages/core/test/acceptance/animation_spec.ts b/packages/core/test/acceptance/animation_spec.ts index 32761bd2010..333fe6ee200 100644 --- a/packages/core/test/acceptance/animation_spec.ts +++ b/packages/core/test/acceptance/animation_spec.ts @@ -400,7 +400,9 @@ describe('Animation', () => { expect(fixture.nativeElement.outerHTML).not.toContain('class="fade"'); cmp.show.set(false); fixture.detectChanges(); + tickAnimationFrames(1); expect(cmp.show()).toBeFalsy(); + fixture.detectChanges(); expect(fixture.nativeElement.outerHTML).toContain('class="fade"'); fixture.detectChanges(); fadeCmp.nativeElement.dispatchEvent( @@ -441,7 +443,9 @@ describe('Animation', () => { expect(fixture.nativeElement.outerHTML).not.toContain('class="fade"'); cmp.show.set(false); fixture.detectChanges(); + tickAnimationFrames(1); expect(cmp.show()).toBeFalsy(); + fixture.detectChanges(); expect(fixture.nativeElement.outerHTML).toContain('class="fade"'); fixture.detectChanges(); fadeCmp.nativeElement.dispatchEvent( @@ -805,25 +809,6 @@ describe('Animation', () => { } `; - it('should apply classes on entry when animation is specified with no control flow', fakeAsync(() => { - @Component({ - selector: 'test-cmp', - styles: styles, - template: '

I should slide in

', - encapsulation: ViewEncapsulation.None, - }) - class TestComponent { - @ViewChild('el', {read: ElementRef}) el!: ElementRef; - } - TestBed.configureTestingModule({animationsEnabled: true}); - - const fixture = TestBed.createComponent(TestComponent); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - tickAnimationFrames(1); - expect(cmp.el.nativeElement.outerHTML).toContain('class="slide-in"'); - })); - it('should apply classes on entry when animation is specified', fakeAsync(() => { @Component({ selector: 'test-cmp', diff --git a/packages/core/test/acceptance/authoring/signal_inputs_spec.ts b/packages/core/test/acceptance/authoring/signal_inputs_spec.ts index 3a33b4fb21b..8559214a2b4 100644 --- a/packages/core/test/acceptance/authoring/signal_inputs_spec.ts +++ b/packages/core/test/acceptance/authoring/signal_inputs_spec.ts @@ -12,14 +12,9 @@ import { Directive, effect, EventEmitter, - inject, input, Output, - signal, - TemplateRef, - viewChild, ViewChild, - ViewContainerRef, } from '@angular/core'; import {SIGNAL} from '../../../primitives/signals'; import {fakeAsync, TestBed, tick} from '../../../testing'; @@ -366,158 +361,5 @@ describe('signal inputs', () => { tick(); expect(childCmp.nativeElement.className).not.toContain('fade-in'); })); - - it('should support content projection', fakeAsync(() => { - const animateStyles = ` - .fade-in { - animation: fade 1ms forwards; - } - .fade-out { - animation: fade 2ms reverse; - } - @keyframes fade { - from { - opacity: 0; - } - to { - opacity: 1; - } - } - - `; - - @Component({ - selector: 'app-container', - encapsulation: ViewEncapsulation.None, - template: ` - - - - - - `, - }) - class Container { - private readonly viewContainerRef = inject(ViewContainerRef); - - protected readonly template = viewChild.required>('template'); - - protected state = signal(false); - - protected toggle() { - this.state() ? this.destroy() : this.create(); - } - - protected create() { - this.state.set(true); - this.viewContainerRef.createEmbeddedView(this.template()); - } - - protected destroy() { - this.state.set(false); - this.viewContainerRef.clear(); - } - } - - @Component({ - selector: 'app-content', - template: ` - - `, - styles: animateStyles, - host: { - 'animate.leave': 'fade-out', - 'animate.enter': 'fade-in', - }, - encapsulation: ViewEncapsulation.None, - }) - class Content {} - - @Component({ - selector: 'test-cmp', - template: ` - - This content will be animated on enter/leave - - `, - imports: [Container, Content], - encapsulation: ViewEncapsulation.None, - }) - class TestComponent {} - TestBed.configureTestingModule({animationsEnabled: true}); - - const fixture = TestBed.createComponent(TestComponent); - const button = fixture.nativeElement.querySelector('button'); - - fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('app-content')).toBeNull(); - expect(button).not.toBeNull(); - - button.click(); - - // show first time - fixture.detectChanges(); - tickAnimationFrames(1); - const enterAppContent = fixture.nativeElement.querySelector('app-content'); - - expect(enterAppContent).not.toBeNull(); - expect(enterAppContent.className).toEqual('fade-in'); - enterAppContent.dispatchEvent(new AnimationEvent('animationstart')); - enterAppContent.dispatchEvent(new AnimationEvent('animationend', {animationName: 'fade'})); - tick(); - expect(enterAppContent.className).not.toEqual('fade-in'); - expect(fixture.debugElement.query(By.css('app-content'))).not.toBeNull(); - - button.click(); - - // hide first time - fixture.detectChanges(); - tickAnimationFrames(1); - const leaveAppContent = fixture.nativeElement.querySelector('app-content'); - - expect(fixture.nativeElement.outerHTML).toContain('app-content class="fade-out"'); - expect(leaveAppContent.className).toEqual('fade-out'); - leaveAppContent.dispatchEvent(new AnimationEvent('animationstart')); - leaveAppContent.dispatchEvent(new AnimationEvent('animationend', {animationName: 'fade'})); - fixture.detectChanges(); - tickAnimationFrames(1); - - expect(fixture.debugElement.query(By.css('app-content'))).toBeNull(); - - button.click(); - - // show second time - fixture.detectChanges(); - fixture.changeDetectorRef.markForCheck(); - tickAnimationFrames(1); - const enterAppContent2 = fixture.nativeElement.querySelector('app-content'); - - expect(enterAppContent2).not.toBeNull(); - expect(enterAppContent2.className).toEqual('fade-in'); - const fadeInEl = fixture.nativeElement.querySelector('.fade-in'); - fadeInEl.dispatchEvent(new AnimationEvent('animationstart')); - fadeInEl.dispatchEvent(new AnimationEvent('animationend', {animationName: 'fade'})); - fixture.detectChanges(); - tickAnimationFrames(1); - - expect(fixture.nativeElement.querySelector('app-content').className).not.toEqual('fade-in'); - expect(fixture.debugElement.query(By.css('app-content'))).not.toBeNull(); - - button.click(); - - // hide second time - fixture.detectChanges(); - tickAnimationFrames(1); - const leaveAppContent2 = fixture.nativeElement.querySelector('app-content'); - - expect(fixture.nativeElement.outerHTML).toContain('app-content class="fade-out"'); - expect(leaveAppContent2.className).toEqual('fade-out'); - leaveAppContent2.dispatchEvent(new AnimationEvent('animationstart')); - leaveAppContent2.dispatchEvent(new AnimationEvent('animationend', {animationName: 'fade'})); - fixture.detectChanges(); - tickAnimationFrames(1); - - expect(fixture.debugElement.query(By.css('app-content'))).toBeNull(); - })); }); }); diff --git a/packages/core/test/acceptance/view_container_ref_spec.ts b/packages/core/test/acceptance/view_container_ref_spec.ts index 47f02ef9fd4..cae99f769a9 100644 --- a/packages/core/test/acceptance/view_container_ref_spec.ts +++ b/packages/core/test/acceptance/view_container_ref_spec.ts @@ -48,7 +48,6 @@ import {ComponentFixture, TestBed, TestComponentRenderer} from '../../testing'; import {clearTranslations, loadTranslations} from '@angular/localize'; import {By, DomSanitizer} from '@angular/platform-browser'; import {expect} from '@angular/private/testing/matchers'; -import {ANIMATION_QUEUE} from '../../src/animation/interfaces'; describe('ViewContainerRef', () => { /** @@ -1414,7 +1413,6 @@ describe('ViewContainerRef', () => { {provide: Sanitizer, useValue: TestBed.inject(DomSanitizer)}, {provide: ErrorHandler, useValue: TestBed.inject(ErrorHandler)}, {provide: RendererFactory2, useValue: TestBed.inject(RendererFactory2)}, - {provide: ANIMATION_QUEUE, useValue: TestBed.inject(ANIMATION_QUEUE)}, ], }) class MyAppModule {} diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json index be2b1c80a5a..6fd5a68cf6e 100644 --- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json @@ -5,7 +5,6 @@ "ANIMATIONS", "ANIMATION_MODULE_TYPE", "ANIMATION_PREFIX", - "ANIMATION_QUEUE", "ANY_STATE", "APP_BOOTSTRAP_LISTENER", "APP_ID", @@ -322,7 +321,6 @@ "addAfterRenderSequencesForView", "addClass", "addServerStyles", - "addToAnimationQueue", "addToEndOfViewTree", "allLeavingAnimations", "allocExpando", @@ -682,7 +680,6 @@ "markViewForRefresh", "markedFeatures", "matchTemplateAttribute", - "maybeQueueEnterAnimation", "maybeReturnReactiveLViewConsumer", "maybeWrapInNotSelector", "mergeHostAttribute", @@ -782,6 +779,7 @@ "roundOffset", "runAfterLeaveAnimations", "runEffectsInView", + "runEnterAnimations", "runInInjectionContext", "runLeaveAnimationsWithCallback", "runPlatformInitializers", diff --git a/packages/core/test/bundling/defer/bundle.golden_symbols.json b/packages/core/test/bundling/defer/bundle.golden_symbols.json index 2d6e2740dbb..ec9109c2f6b 100644 --- a/packages/core/test/bundling/defer/bundle.golden_symbols.json +++ b/packages/core/test/bundling/defer/bundle.golden_symbols.json @@ -60,7 +60,6 @@ "lazy": [ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", - "ANIMATION_QUEUE", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_INITIALIZER", @@ -292,7 +291,6 @@ "addAfterRenderSequencesForView", "addDepsToRegistry", "addLViewToLContainer", - "addToAnimationQueue", "addToArray", "addToEndOfViewTree", "addViewToDOM", @@ -607,7 +605,6 @@ "markViewForRefresh", "markedFeatures", "matchTemplateAttribute", - "maybeQueueEnterAnimation", "maybeReturnReactiveLViewConsumer", "maybeWrapInNotSelector", "mergeHostAttribute", @@ -677,6 +674,7 @@ "retrieveHydrationInfo", "runAfterLeaveAnimations", "runEffectsInView", + "runEnterAnimations", "runInInjectionContext", "runLeaveAnimationsWithCallback", "runPlatformInitializers", diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index 8388aa4ba65..3b3ee6e9967 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -3,7 +3,6 @@ "main": [ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", - "ANIMATION_QUEUE", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -347,7 +346,6 @@ "addAfterRenderSequencesForView", "addLViewToLContainer", "addServerStyles", - "addToAnimationQueue", "addToArray", "addToEndOfViewTree", "addValidators", @@ -789,7 +787,6 @@ "matchTemplateAttribute", "maxLengthValidator", "maxValidator", - "maybeQueueEnterAnimation", "maybeReturnReactiveLViewConsumer", "maybeUnwrapEmpty", "maybeUnwrapFn", @@ -902,6 +899,7 @@ "retrieveHydrationInfo", "runAfterLeaveAnimations", "runEffectsInView", + "runEnterAnimations", "runInInjectionContext", "runLeaveAnimationsWithCallback", "runPlatformInitializers", diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index 52a38be16ba..9a243ee5ee4 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -3,7 +3,6 @@ "main": [ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", - "ANIMATION_QUEUE", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -351,7 +350,6 @@ "addAfterRenderSequencesForView", "addLViewToLContainer", "addServerStyles", - "addToAnimationQueue", "addToArray", "addToEndOfViewTree", "addValidators", @@ -788,7 +786,6 @@ "markViewForRefresh", "markedFeatures", "matchTemplateAttribute", - "maybeQueueEnterAnimation", "maybeReturnReactiveLViewConsumer", "maybeUnwrapEmpty", "maybeUnwrapFn", @@ -900,6 +897,7 @@ "retrieveHydrationInfo", "runAfterLeaveAnimations", "runEffectsInView", + "runEnterAnimations", "runInInjectionContext", "runLeaveAnimationsWithCallback", "runPlatformInitializers", diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json index 824f5eef5cf..69ba78b4f8b 100644 --- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json @@ -5,7 +5,6 @@ "AFTER_RENDER_SEQUENCES_TO_ADD", "ALLOWED_METHODS", "ANIMATIONS", - "ANIMATION_QUEUE", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -288,7 +287,6 @@ "activeConsumer", "addAfterRenderSequencesForView", "addServerStyles", - "addToAnimationQueue", "addToEndOfViewTree", "allLeavingAnimations", "allocExpando", @@ -638,7 +636,6 @@ "markViewDirty", "markViewForRefresh", "markedFeatures", - "maybeQueueEnterAnimation", "maybeReturnReactiveLViewConsumer", "maybeWrapInNotSelector", "mergeHostAttribute", @@ -718,6 +715,7 @@ "retrieveTransferredState", "runAfterLeaveAnimations", "runEffectsInView", + "runEnterAnimations", "runInInjectionContext", "runLeaveAnimationsWithCallback", "runPlatformInitializers", diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 02961d4a791..e1d8697ecb1 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -3,7 +3,6 @@ "main": [ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", - "ANIMATION_QUEUE", "APP_BASE_HREF", "APP_BOOTSTRAP_LISTENER", "APP_ID", @@ -397,7 +396,6 @@ "addEmptyPathsToChildrenIfNeeded", "addLViewToLContainer", "addServerStyles", - "addToAnimationQueue", "addToArray", "addToEndOfViewTree", "addViewToDOM", @@ -898,7 +896,6 @@ "matchWithChecks", "materializeViewResults", "matrixParamsMatch", - "maybeQueueEnterAnimation", "maybeResolveResources", "maybeReturnReactiveLViewConsumer", "maybeSchedule", @@ -1017,6 +1014,7 @@ "runCanLoadGuards", "runCanMatchGuards", "runEffectsInView", + "runEnterAnimations", "runInInjectionContext", "runLeaveAnimationsWithCallback", "runPlatformInitializers", diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json index 13e175c0dd1..815afaeddea 100644 --- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json +++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json @@ -3,7 +3,6 @@ "main": [ "AFTER_RENDER_SEQUENCES_TO_ADD", "ANIMATIONS", - "ANIMATION_QUEUE", "APP_BOOTSTRAP_LISTENER", "APP_ID", "APP_ID_ATTRIBUTE_NAME", @@ -232,7 +231,6 @@ "activeConsumer", "addAfterRenderSequencesForView", "addServerStyles", - "addToAnimationQueue", "addToEndOfViewTree", "allLeavingAnimations", "allocExpando", @@ -504,7 +502,6 @@ "markViewDirty", "markViewForRefresh", "markedFeatures", - "maybeQueueEnterAnimation", "maybeReturnReactiveLViewConsumer", "maybeWrapInNotSelector", "mergeHostAttribute", @@ -571,6 +568,7 @@ "retrieveHydrationInfo", "runAfterLeaveAnimations", "runEffectsInView", + "runEnterAnimations", "runInInjectionContext", "runLeaveAnimationsWithCallback", "runPlatformInitializers",