fix(animations): always trigger animations after the change detection check (#12713)

This patch ensures that animations are run outside of change detection
thus allowing for start and done callbacks to modify application data
without causing a cycle loop.

Closes #12713
This commit is contained in:
Matias Niemelä 2016-11-04 15:15:27 -07:00 committed by vikerman
parent 2a3f4d7b17
commit 383f23b578
2 changed files with 43 additions and 2 deletions

View File

@ -17,6 +17,15 @@ export function queueAnimation(player: AnimationPlayer) {
/** @internal */ /** @internal */
export function triggerQueuedAnimations() { export function triggerQueuedAnimations() {
// this code is wrapped into a single promise such that the
// onStart and onDone player callbacks are triggered outside
// of the digest cycle of animations
if (_queuedAnimations.length) {
Promise.resolve(null).then(_triggerAnimations);
}
}
function _triggerAnimations() {
for (var i = 0; i < _queuedAnimations.length; i++) { for (var i = 0; i < _queuedAnimations.length; i++) {
var player = _queuedAnimations[i]; var player = _queuedAnimations[i];
player.play(); player.play();

View File

@ -1151,7 +1151,7 @@ function declareTests({useJit}: {useJit: boolean}) {
describe('animation output events', () => { describe('animation output events', () => {
it('should fire the associated animation output expression when the animation starts even if no animation is fired', it('should fire the associated animation output expression when the animation starts even if no animation is fired',
() => { fakeAsync(() => {
TestBed.overrideComponent(DummyIfCmp, { TestBed.overrideComponent(DummyIfCmp, {
set: { set: {
template: ` template: `
@ -1175,16 +1175,18 @@ function declareTests({useJit}: {useJit: boolean}) {
cmp.exp = 'one'; cmp.exp = 'one';
fixture.detectChanges(); fixture.detectChanges();
flushMicrotasks();
expect(calls).toEqual(1); expect(calls).toEqual(1);
expect(isAnimationRunning).toEqual(false); expect(isAnimationRunning).toEqual(false);
cmp.exp = 'two'; cmp.exp = 'two';
fixture.detectChanges(); fixture.detectChanges();
flushMicrotasks();
expect(calls).toEqual(2); expect(calls).toEqual(2);
expect(isAnimationRunning).toEqual(true); expect(isAnimationRunning).toEqual(true);
}); }));
it('should fire the associated animation output expression when the animation ends even if no animation is fired', it('should fire the associated animation output expression when the animation ends even if no animation is fired',
fakeAsync(() => { fakeAsync(() => {
@ -1296,6 +1298,36 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(eventData2.totalTime).toEqual(0); expect(eventData2.totalTime).toEqual(0);
})); }));
it('should successfully update the component view when an animation start callback is fired',
fakeAsync(() => {
TestBed.overrideComponent(DummyIfCmp, {
set: {
template: `
<div [@trigger]="exp" (@trigger.start)="exp2='look at me'">{{ exp2 }}</div>
`,
animations: [
trigger(
'trigger',
[transition(
'* => *',
[animate('1s 750ms', style({})), animate('2000ms 0ms', style({}))])]),
]
}
});
let fixture = TestBed.createComponent(DummyIfCmp);
var cmp = fixture.componentInstance;
cmp.exp2 = 'ignore me';
cmp.exp = 'one';
fixture.detectChanges();
flushMicrotasks();
fixture.detectChanges();
var container = fixture.nativeElement;
expect(getDOM().getInnerHTML(container)).toContain('look at me');
}));
it('should throw an error if an animation output is referenced is not defined within the component', it('should throw an error if an animation output is referenced is not defined within the component',
() => { () => {
TestBed.overrideComponent(DummyIfCmp, { TestBed.overrideComponent(DummyIfCmp, {