From 333ffd8d3203429670a4d6fc4674bcc9e38a8ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Mon, 17 Jul 2017 10:25:15 -0400 Subject: [PATCH] fix(animations): capture cancelled animation styles within grouped animations Closes #17170 --- .../src/render/transition_animation_engine.ts | 23 +++++- .../src/players/animation_group_player.ts | 8 ++ .../animation/animation_integration_spec.ts | 73 ++++++++++++++++++- 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/packages/animations/browser/src/render/transition_animation_engine.ts b/packages/animations/browser/src/render/transition_animation_engine.ts index a562eeefc3..1f41927a84 100644 --- a/packages/animations/browser/src/render/transition_animation_engine.ts +++ b/packages/animations/browser/src/render/transition_animation_engine.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {AUTO_STYLE, AnimationOptions, AnimationPlayer, NoopAnimationPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; +import {AUTO_STYLE, AnimationOptions, AnimationPlayer, NoopAnimationPlayer, ɵAnimationGroupPlayer as AnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; import {AnimationTimelineInstruction} from '../dsl/animation_timeline_instruction'; import {AnimationTransitionFactory} from '../dsl/animation_transition_factory'; @@ -1168,8 +1168,8 @@ export class TransitionAnimationEngine { if (details && details.removedBeforeQueried) return new NoopAnimationPlayer(); const isQueriedElement = element !== rootElement; - const previousPlayers = - (allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer()); + const previousPlayers = flattenGroupPlayers( + (allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer())); const preStyles = preStylesMap.get(element); const postStyles = postStylesMap.get(element); @@ -1464,3 +1464,20 @@ function removeNodesAfterAnimationDone( engine: TransitionAnimationEngine, element: any, players: AnimationPlayer[]) { optimizeGroupPlayer(players).onDone(() => engine.processLeaveNode(element)); } + +function flattenGroupPlayers(players: AnimationPlayer[]): AnimationPlayer[] { + const finalPlayers: AnimationPlayer[] = []; + _flattenGroupPlayersRecur(players, finalPlayers); + return finalPlayers; +} + +function _flattenGroupPlayersRecur(players: AnimationPlayer[], finalPlayers: AnimationPlayer[]) { + for (let i = 0; i < players.length; i++) { + const player = players[i]; + if (player instanceof AnimationGroupPlayer) { + _flattenGroupPlayersRecur(player.players, finalPlayers); + } else { + finalPlayers.push(player as AnimationPlayer); + } + } +} diff --git a/packages/animations/src/players/animation_group_player.ts b/packages/animations/src/players/animation_group_player.ts index a1ecff2463..d1b0aae86f 100644 --- a/packages/animations/src/players/animation_group_player.ts +++ b/packages/animations/src/players/animation_group_player.ts @@ -132,4 +132,12 @@ export class AnimationGroupPlayer implements AnimationPlayer { } get players(): AnimationPlayer[] { return this._players; } + + beforeDestroy(): void { + this.players.forEach(player => { + if (player.beforeDestroy) { + player.beforeDestroy(); + } + }); + } } diff --git a/packages/core/test/animation/animation_integration_spec.ts b/packages/core/test/animation/animation_integration_spec.ts index 52acb25d8a..4b51f0266d 100644 --- a/packages/core/test/animation/animation_integration_spec.ts +++ b/packages/core/test/animation/animation_integration_spec.ts @@ -799,6 +799,75 @@ export function main() { expect(p3.previousStyles).toEqual({}); }); + it('should provide the styling of previous players that are grouped', () => { + @Component({ + selector: 'ani-cmp', + template: ` +
+ `, + animations: [trigger( + 'myAnimation', + [ + transition( + '1 => 2', + [ + group([ + animate(500, style({'width': '100px'})), + animate(500, style({'height': '100px'})), + ]), + animate(500, keyframes([ + style({'opacity': '0'}), + style({'opacity': '1'}) + ])) + ]), + transition( + '2 => 3', + [ + style({'opacity': '0'}), + animate(500, style({'opacity': '1'})), + ]), + ])], + }) + class Cmp { + exp: any = false; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + fixture.detectChanges(); + engine.flush(); + + cmp.exp = '1'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + resetLog(); + + cmp.exp = '2'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(3); + resetLog(); + + cmp.exp = '3'; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(1); + const player = players[0] as MockAnimationPlayer; + const pp = player.previousPlayers as MockAnimationPlayer[]; + + expect(pp.length).toEqual(3); + expect(pp[0].currentSnapshot).toEqual({width: AUTO_STYLE}); + expect(pp[1].currentSnapshot).toEqual({height: AUTO_STYLE}); + expect(pp[2].currentSnapshot).toEqual({opacity: AUTO_STYLE}); + }); + it('should properly balance styles between states even if there are no destination state styles', () => { @Component({ @@ -1815,12 +1884,12 @@ export function main() { selector: 'my-cmp', template: `
-
- {{ item }} + {{ item }}
`,