feat(animations): expose the element
value within transition events
This commit is contained in:
@ -294,8 +294,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
|
|
||||||
statements.push(new o.ReturnStatement(
|
statements.push(new o.ReturnStatement(
|
||||||
o.importExpr(createIdentifier(Identifiers.AnimationTransition)).instantiate([
|
o.importExpr(createIdentifier(Identifiers.AnimationTransition)).instantiate([
|
||||||
_ANIMATION_PLAYER_VAR, _ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR,
|
_ANIMATION_PLAYER_VAR, _ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_CURRENT_STATE_VAR,
|
||||||
_ANIMATION_TIME_VAR
|
_ANIMATION_NEXT_STATE_VAR, _ANIMATION_TIME_VAR
|
||||||
])));
|
])));
|
||||||
|
|
||||||
return o.fn(
|
return o.fn(
|
||||||
|
@ -5,20 +5,23 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import {ElementRef} from '../linker/element_ref';
|
||||||
|
|
||||||
import {AnimationPlayer} from './animation_player';
|
import {AnimationPlayer} from './animation_player';
|
||||||
import {AnimationTransitionEvent} from './animation_transition_event';
|
import {AnimationTransitionEvent} from './animation_transition_event';
|
||||||
|
|
||||||
export class AnimationTransition {
|
export class AnimationTransition {
|
||||||
constructor(
|
constructor(
|
||||||
private _player: AnimationPlayer, private _fromState: string, private _toState: string,
|
private _player: AnimationPlayer, private _element: ElementRef, private _fromState: string,
|
||||||
private _totalTime: number) {}
|
private _toState: string, private _totalTime: number) {}
|
||||||
|
|
||||||
private _createEvent(phaseName: string): AnimationTransitionEvent {
|
private _createEvent(phaseName: string): AnimationTransitionEvent {
|
||||||
return new AnimationTransitionEvent({
|
return new AnimationTransitionEvent({
|
||||||
fromState: this._fromState,
|
fromState: this._fromState,
|
||||||
toState: this._toState,
|
toState: this._toState,
|
||||||
totalTime: this._totalTime,
|
totalTime: this._totalTime,
|
||||||
phaseName: phaseName
|
phaseName: phaseName,
|
||||||
|
element: this._element
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import {ElementRef} from '../linker/element_ref';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of this class is returned as an event parameter when an animation
|
* An instance of this class is returned as an event parameter when an animation
|
||||||
@ -42,12 +43,19 @@ export class AnimationTransitionEvent {
|
|||||||
public toState: string;
|
public toState: string;
|
||||||
public totalTime: number;
|
public totalTime: number;
|
||||||
public phaseName: string;
|
public phaseName: string;
|
||||||
|
public element: ElementRef;
|
||||||
|
|
||||||
constructor({fromState, toState, totalTime, phaseName}:
|
constructor({fromState, toState, totalTime, phaseName, element}: {
|
||||||
{fromState: string, toState: string, totalTime: number, phaseName: string}) {
|
fromState: string,
|
||||||
|
toState: string,
|
||||||
|
totalTime: number,
|
||||||
|
phaseName: string,
|
||||||
|
element: any
|
||||||
|
}) {
|
||||||
this.fromState = fromState;
|
this.fromState = fromState;
|
||||||
this.toState = toState;
|
this.toState = toState;
|
||||||
this.totalTime = totalTime;
|
this.totalTime = totalTime;
|
||||||
this.phaseName = phaseName;
|
this.phaseName = phaseName;
|
||||||
|
this.element = new ElementRef(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import {AnimationTransitionEvent} from '../../src/animation/animation_transition
|
|||||||
import {AUTO_STYLE, animate, group, keyframes, sequence, state, style, transition, trigger} from '../../src/animation/metadata';
|
import {AUTO_STYLE, animate, group, keyframes, sequence, state, style, transition, trigger} from '../../src/animation/metadata';
|
||||||
import {Input} from '../../src/core';
|
import {Input} from '../../src/core';
|
||||||
import {isPresent} from '../../src/facade/lang';
|
import {isPresent} from '../../src/facade/lang';
|
||||||
|
import {ElementRef} from '../../src/linker/element_ref';
|
||||||
import {TestBed, fakeAsync, flushMicrotasks} from '../../testing';
|
import {TestBed, fakeAsync, flushMicrotasks} from '../../testing';
|
||||||
import {MockAnimationPlayer} from '../../testing/mock_animation_player';
|
import {MockAnimationPlayer} from '../../testing/mock_animation_player';
|
||||||
|
|
||||||
@ -1763,6 +1764,36 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
expect(doneCalls[3]).toEqual(1);
|
expect(doneCalls[3]).toEqual(1);
|
||||||
expect(doneCalls[4]).toEqual(1);
|
expect(doneCalls[4]).toEqual(1);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should expose the element associated with the animation within the callback event',
|
||||||
|
fakeAsync(() => {
|
||||||
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
|
set: {
|
||||||
|
template: `
|
||||||
|
<div *ngFor="let item of items"
|
||||||
|
(@trigger.start)="callback($event)"
|
||||||
|
@trigger class="target">{{ item }}</div>
|
||||||
|
`,
|
||||||
|
animations: [trigger('trigger', [transition('* => *', [animate(1000)])])]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(DummyIfCmp);
|
||||||
|
const cmp = fixture.componentInstance;
|
||||||
|
|
||||||
|
const elements: ElementRef[] = [];
|
||||||
|
cmp.callback = (e: AnimationTransitionEvent) => elements.push(e.element);
|
||||||
|
|
||||||
|
cmp.items = [0, 1, 2, 3, 4];
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
const targetElements =
|
||||||
|
<any[]>getDOM().querySelectorAll(fixture.nativeElement, '.target');
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
expect(elements[i].nativeElement).toBe(targetElements[i]);
|
||||||
|
}
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ng directives', () => {
|
describe('ng directives', () => {
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
import {AUTO_STYLE, AnimationTransitionEvent, Component, Injector, ViewChild, animate, state, style, transition, trigger} from '@angular/core';
|
import {AUTO_STYLE, AnimationTransitionEvent, Component, Injector, ViewChild, animate, state, style, transition, trigger} from '@angular/core';
|
||||||
import {DebugDomRootRenderer} from '@angular/core/src/debug/debug_renderer';
|
import {DebugDomRootRenderer} from '@angular/core/src/debug/debug_renderer';
|
||||||
|
import {ElementRef} from '@angular/core/src/linker/element_ref';
|
||||||
|
import {ViewChild} from '@angular/core/src/metadata/di';
|
||||||
import {RootRenderer} from '@angular/core/src/render/api';
|
import {RootRenderer} from '@angular/core/src/render/api';
|
||||||
import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
||||||
import {MockAnimationPlayer} from '@angular/core/testing/testing_internal';
|
import {MockAnimationPlayer} from '@angular/core/testing/testing_internal';
|
||||||
@ -203,24 +205,34 @@ export function main() {
|
|||||||
flushMicrotasks();
|
flushMicrotasks();
|
||||||
|
|
||||||
uiDriver.log.shift()['player'].finish();
|
uiDriver.log.shift()['player'].finish();
|
||||||
const [triggerOneStart, triggerOneDone] = log['one'];
|
|
||||||
expect(triggerOneStart)
|
|
||||||
.toEqual(new AnimationTransitionEvent(
|
|
||||||
{fromState: 'a', toState: 'b', totalTime: 500, phaseName: 'start'}));
|
|
||||||
|
|
||||||
expect(triggerOneDone)
|
const [triggerOneStart, triggerOneDone] = log['one'];
|
||||||
.toEqual(new AnimationTransitionEvent(
|
expect(triggerOneStart.fromState).toEqual('a');
|
||||||
{fromState: 'a', toState: 'b', totalTime: 500, phaseName: 'done'}));
|
expect(triggerOneStart.toState).toEqual('b');
|
||||||
|
expect(triggerOneStart.totalTime).toEqual(500);
|
||||||
|
expect(triggerOneStart.phaseName).toEqual('start');
|
||||||
|
expect(triggerOneStart.element instanceof ElementRef).toEqual(true);
|
||||||
|
|
||||||
|
expect(triggerOneDone.fromState).toEqual('a');
|
||||||
|
expect(triggerOneDone.toState).toEqual('b');
|
||||||
|
expect(triggerOneDone.totalTime).toEqual(500);
|
||||||
|
expect(triggerOneDone.phaseName).toEqual('done');
|
||||||
|
expect(triggerOneDone.element instanceof ElementRef).toEqual(true);
|
||||||
|
|
||||||
uiDriver.log.shift()['player'].finish();
|
uiDriver.log.shift()['player'].finish();
|
||||||
const [triggerTwoStart, triggerTwoDone] = log['two'];
|
|
||||||
expect(triggerTwoStart)
|
|
||||||
.toEqual(new AnimationTransitionEvent(
|
|
||||||
{fromState: 'c', toState: 'd', totalTime: 1000, phaseName: 'start'}));
|
|
||||||
|
|
||||||
expect(triggerTwoDone)
|
const [triggerTwoStart, triggerTwoDone] = log['two'];
|
||||||
.toEqual(new AnimationTransitionEvent(
|
expect(triggerTwoStart.fromState).toEqual('c');
|
||||||
{fromState: 'c', toState: 'd', totalTime: 1000, phaseName: 'done'}));
|
expect(triggerTwoStart.toState).toEqual('d');
|
||||||
|
expect(triggerTwoStart.totalTime).toEqual(1000);
|
||||||
|
expect(triggerTwoStart.phaseName).toEqual('start');
|
||||||
|
expect(triggerTwoStart.element instanceof ElementRef).toEqual(true);
|
||||||
|
|
||||||
|
expect(triggerTwoDone.fromState).toEqual('c');
|
||||||
|
expect(triggerTwoDone.toState).toEqual('d');
|
||||||
|
expect(triggerTwoDone.totalTime).toEqual(1000);
|
||||||
|
expect(triggerTwoDone.phaseName).toEqual('done');
|
||||||
|
expect(triggerTwoDone.element instanceof ElementRef).toEqual(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should handle .start and .done callbacks for mutliple elements that contain animations that are fired at the same time',
|
it('should handle .start and .done callbacks for mutliple elements that contain animations that are fired at the same time',
|
||||||
@ -228,7 +240,7 @@ export function main() {
|
|||||||
function logFactory(
|
function logFactory(
|
||||||
log: {[phaseName: string]: AnimationTransitionEvent},
|
log: {[phaseName: string]: AnimationTransitionEvent},
|
||||||
phaseName: string): (event: AnimationTransitionEvent) => any {
|
phaseName: string): (event: AnimationTransitionEvent) => any {
|
||||||
return (event: AnimationTransitionEvent) => { log[phaseName] = event; };
|
return (event: AnimationTransitionEvent) => log[phaseName] = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fixture = TestBed.createComponent(ContainerAnimationCmp);
|
const fixture = TestBed.createComponent(ContainerAnimationCmp);
|
||||||
@ -250,25 +262,37 @@ export function main() {
|
|||||||
|
|
||||||
uiDriver.log.shift()['player'].finish();
|
uiDriver.log.shift()['player'].finish();
|
||||||
|
|
||||||
expect(cmp1Log['start'])
|
const start1 = cmp1Log['start'];
|
||||||
.toEqual(new AnimationTransitionEvent(
|
expect(start1.fromState).toEqual('void');
|
||||||
{fromState: 'void', toState: 'off', totalTime: 500, phaseName: 'start'}));
|
expect(start1.toState).toEqual('off');
|
||||||
|
expect(start1.totalTime).toEqual(500);
|
||||||
|
expect(start1.phaseName).toEqual('start');
|
||||||
|
expect(start1.element instanceof ElementRef).toBe(true);
|
||||||
|
|
||||||
expect(cmp1Log['done'])
|
const done1 = cmp1Log['done'];
|
||||||
.toEqual(new AnimationTransitionEvent(
|
expect(done1.fromState).toEqual('void');
|
||||||
{fromState: 'void', toState: 'off', totalTime: 500, phaseName: 'done'}));
|
expect(done1.toState).toEqual('off');
|
||||||
|
expect(done1.totalTime).toEqual(500);
|
||||||
|
expect(done1.phaseName).toEqual('done');
|
||||||
|
expect(done1.element instanceof ElementRef).toBe(true);
|
||||||
|
|
||||||
// the * => on transition has two steps
|
// the * => on transition has two steps
|
||||||
uiDriver.log.shift()['player'].finish();
|
uiDriver.log.shift()['player'].finish();
|
||||||
uiDriver.log.shift()['player'].finish();
|
uiDriver.log.shift()['player'].finish();
|
||||||
|
|
||||||
expect(cmp2Log['start'])
|
const start2 = cmp2Log['start'];
|
||||||
.toEqual(new AnimationTransitionEvent(
|
expect(start2.fromState).toEqual('void');
|
||||||
{fromState: 'void', toState: 'on', totalTime: 1000, phaseName: 'start'}));
|
expect(start2.toState).toEqual('on');
|
||||||
|
expect(start2.totalTime).toEqual(1000);
|
||||||
|
expect(start2.phaseName).toEqual('start');
|
||||||
|
expect(start2.element instanceof ElementRef).toBe(true);
|
||||||
|
|
||||||
expect(cmp2Log['done'])
|
const done2 = cmp2Log['done'];
|
||||||
.toEqual(new AnimationTransitionEvent(
|
expect(done2.fromState).toEqual('void');
|
||||||
{fromState: 'void', toState: 'on', totalTime: 1000, phaseName: 'done'}));
|
expect(done2.toState).toEqual('on');
|
||||||
|
expect(done2.totalTime).toEqual(1000);
|
||||||
|
expect(done2.phaseName).toEqual('done');
|
||||||
|
expect(done2.element instanceof ElementRef).toBe(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should destroy the player when the animation is complete', fakeAsync(() => {
|
it('should destroy the player when the animation is complete', fakeAsync(() => {
|
||||||
@ -331,6 +355,7 @@ class ContainerAnimationCmp {
|
|||||||
selector: 'my-comp',
|
selector: 'my-comp',
|
||||||
template: `
|
template: `
|
||||||
<div *ngIf="state"
|
<div *ngIf="state"
|
||||||
|
#ref
|
||||||
[@myTrigger]="state"
|
[@myTrigger]="state"
|
||||||
(@myTrigger.start)="stateStartFn($event)"
|
(@myTrigger.start)="stateStartFn($event)"
|
||||||
(@myTrigger.done)="stateDoneFn($event)">...</div>
|
(@myTrigger.done)="stateDoneFn($event)">...</div>
|
||||||
@ -348,6 +373,8 @@ class AnimationCmp {
|
|||||||
state = 'off';
|
state = 'off';
|
||||||
stateStartFn = (event: AnimationTransitionEvent): any => {};
|
stateStartFn = (event: AnimationTransitionEvent): any => {};
|
||||||
stateDoneFn = (event: AnimationTransitionEvent): any => {};
|
stateDoneFn = (event: AnimationTransitionEvent): any => {};
|
||||||
|
|
||||||
|
@ViewChild('ref') public elmRef: ElementRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -355,10 +382,10 @@ class AnimationCmp {
|
|||||||
template: `
|
template: `
|
||||||
<div [@one]="oneTriggerState"
|
<div [@one]="oneTriggerState"
|
||||||
(@one.start)="callback('one', $event)"
|
(@one.start)="callback('one', $event)"
|
||||||
(@one.done)="callback('one', $event)">...</div>
|
(@one.done)="callback('one', $event)" #one>...</div>
|
||||||
<div [@two]="twoTriggerState"
|
<div [@two]="twoTriggerState"
|
||||||
(@two.start)="callback('two', $event)"
|
(@two.start)="callback('two', $event)"
|
||||||
(@two.done)="callback('two', $event)">...</div>
|
(@two.done)="callback('two', $event)" #two>...</div>
|
||||||
`,
|
`,
|
||||||
animations: [
|
animations: [
|
||||||
trigger(
|
trigger(
|
||||||
@ -378,5 +405,10 @@ class AnimationCmp {
|
|||||||
class MultiAnimationCmp {
|
class MultiAnimationCmp {
|
||||||
oneTriggerState: string;
|
oneTriggerState: string;
|
||||||
twoTriggerState: string;
|
twoTriggerState: string;
|
||||||
|
|
||||||
|
@ViewChild('one') public elmRef1: ElementRef;
|
||||||
|
|
||||||
|
@ViewChild('two') public elmRef2: ElementRef;
|
||||||
|
|
||||||
callback = (triggerName: string, event: AnimationTransitionEvent): any => {};
|
callback = (triggerName: string, event: AnimationTransitionEvent): any => {};
|
||||||
}
|
}
|
||||||
|
4
tools/public_api_guard/core/index.d.ts
vendored
4
tools/public_api_guard/core/index.d.ts
vendored
@ -108,15 +108,17 @@ export declare class AnimationStyleMetadata extends AnimationMetadata {
|
|||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare class AnimationTransitionEvent {
|
export declare class AnimationTransitionEvent {
|
||||||
|
element: ElementRef;
|
||||||
fromState: string;
|
fromState: string;
|
||||||
phaseName: string;
|
phaseName: string;
|
||||||
toState: string;
|
toState: string;
|
||||||
totalTime: number;
|
totalTime: number;
|
||||||
constructor({fromState, toState, totalTime, phaseName}: {
|
constructor({fromState, toState, totalTime, phaseName, element}: {
|
||||||
fromState: string;
|
fromState: string;
|
||||||
toState: string;
|
toState: string;
|
||||||
totalTime: number;
|
totalTime: number;
|
||||||
phaseName: string;
|
phaseName: string;
|
||||||
|
element: any;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user