Skip to content

Commit 58b94e6

Browse files
matskokara
authored andcommitted
feat(animations): expose element and params within transition matchers (#22693)
PR Close #22693
1 parent db56836 commit 58b94e6

File tree

11 files changed

+119
-20
lines changed

11 files changed

+119
-20
lines changed

packages/animations/browser/src/dsl/animation_ast.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export interface StateAst extends Ast<AnimationMetadataType.State> {
4646
}
4747

4848
export interface TransitionAst extends Ast<AnimationMetadataType.Transition> {
49-
matchers: ((fromState: string, toState: string) => boolean)[];
49+
matchers: ((fromState: string, toState: string, element: any, params: {[key: string]:
50+
any}) => boolean)[];
5051
animation: Ast<AnimationMetadataType>;
5152
queryCount: number;
5253
depCount: number;

packages/animations/browser/src/dsl/animation_transition_expr.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
export const ANY_STATE = '*';
9-
export declare type TransitionMatcherFn = (fromState: any, toState: any) => boolean;
9+
export declare type TransitionMatcherFn =
10+
(fromState: any, toState: any, element: any, params: {[key: string]: any}) => boolean;
1011

1112
export function parseTransitionExpr(
1213
transitionValue: string | TransitionMatcherFn, errors: string[]): TransitionMatcherFn[] {

packages/animations/browser/src/dsl/animation_transition_factory.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export class AnimationTransitionFactory {
2424
private _triggerName: string, public ast: TransitionAst,
2525
private _stateStyles: {[stateName: string]: AnimationStateStyles}) {}
2626

27-
match(currentState: any, nextState: any): boolean {
28-
return oneOrMoreTransitionsMatch(this.ast.matchers, currentState, nextState);
27+
match(currentState: any, nextState: any, element: any, params: {[key: string]: any}): boolean {
28+
return oneOrMoreTransitionsMatch(this.ast.matchers, currentState, nextState, element, params);
2929
}
3030

3131
buildStyles(stateName: string, params: {[key: string]: any}, errors: any[]) {
@@ -89,8 +89,9 @@ export class AnimationTransitionFactory {
8989
}
9090

9191
function oneOrMoreTransitionsMatch(
92-
matchFns: TransitionMatcherFn[], currentState: any, nextState: any): boolean {
93-
return matchFns.some(fn => fn(currentState, nextState));
92+
matchFns: TransitionMatcherFn[], currentState: any, nextState: any, element: any,
93+
params: {[key: string]: any}): boolean {
94+
return matchFns.some(fn => fn(currentState, nextState, element, params));
9495
}
9596

9697
export class AnimationStateStyles {

packages/animations/browser/src/dsl/animation_trigger.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ export class AnimationTrigger {
4747

4848
get containsQueries() { return this.ast.queryCount > 0; }
4949

50-
matchTransition(currentState: any, nextState: any): AnimationTransitionFactory|null {
51-
const entry = this.transitionFactories.find(f => f.match(currentState, nextState));
50+
matchTransition(currentState: any, nextState: any, element: any, params: {[key: string]: any}):
51+
AnimationTransitionFactory|null {
52+
const entry =
53+
this.transitionFactories.find(f => f.match(currentState, nextState, element, params));
5254
return entry || null;
5355
}
5456

packages/animations/browser/src/render/transition_animation_engine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,8 @@ export class AnimationTransitionNamespace {
248248
}
249249
});
250250

251-
let transition = trigger.matchTransition(fromState.value, toState.value);
251+
let transition =
252+
trigger.matchTransition(fromState.value, toState.value, element, toState.params);
252253
let isFallbackTransition = false;
253254
if (!transition) {
254255
if (!defaultToFallback) return;

packages/animations/browser/test/dsl/animation_trigger_spec.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ import {makeTrigger} from '../shared';
103103
it('should null when no results are found', () => {
104104
const result = makeTrigger('name', [transition('a => b', animate(1111))]);
105105

106-
const trigger = result.matchTransition('b', 'a');
106+
const trigger = result.matchTransition('b', 'a', {}, {});
107107
expect(trigger).toBeFalsy();
108108
});
109109

@@ -226,7 +226,8 @@ function buildTransition(
226226
trigger: AnimationTrigger, element: any, fromState: any, toState: any,
227227
fromOptions?: AnimationOptions, toOptions?: AnimationOptions): AnimationTransitionInstruction|
228228
null {
229-
const trans = trigger.matchTransition(fromState, toState) !;
229+
const params = toOptions && toOptions.params || {};
230+
const trans = trigger.matchTransition(fromState, toState, element, params) !;
230231
if (trans) {
231232
const driver = new MockAnimationDriver();
232233
return trans.build(

packages/animations/src/animation_metadata.ts

+37-2
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ export interface AnimationStateMetadata extends AnimationMetadata {
115115
* @experimental Animation support is experimental.
116116
*/
117117
export interface AnimationTransitionMetadata extends AnimationMetadata {
118-
expr: string|((fromState: string, toState: string) => boolean);
118+
expr: string|
119+
((fromState: string, toState: string, element?: any,
120+
params?: {[key: string]: any}) => boolean);
119121
animation: AnimationMetadata|AnimationMetadata[];
120122
options: AnimationOptions|null;
121123
}
@@ -294,6 +296,38 @@ export interface AnimationStaggerMetadata extends AnimationMetadata {
294296
* <div [@myAnimationTrigger]="myStatusExp">...</div>
295297
* ```
296298
*
299+
* ### Using an inline function
300+
* The `transition` animation method also supports reading an inline function which can decide
301+
* if its associated animation should be run.
302+
*
303+
* ```
304+
* // this method will be run each time the `myAnimationTrigger`
305+
* // trigger value changes...
306+
* function myInlineMatcherFn(fromState: string, toState: string, element: any, params: {[key:
307+
string]: any}): boolean {
308+
* // notice that `element` and `params` are also available here
309+
* return toState == 'yes-please-animate';
310+
* }
311+
*
312+
* @Component({
313+
* selector: 'my-component',
314+
* templateUrl: 'my-component-tpl.html',
315+
* animations: [
316+
* trigger('myAnimationTrigger', [
317+
* transition(myInlineMatcherFn, [
318+
* // the animation sequence code
319+
* ]),
320+
* ])
321+
* ]
322+
* })
323+
* class MyComponent {
324+
* myStatusExp = "yes-please-animate";
325+
* }
326+
* ```
327+
*
328+
* The inline method will be run each time the trigger
329+
* value changes
330+
*
297331
* ## Disable Animations
298332
* A special animation control binding called `@.disabled` can be placed on an element which will
299333
then disable animations for any inner animation triggers situated within the element as well as
@@ -844,7 +878,8 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
844878
* @experimental Animation support is experimental.
845879
*/
846880
export function transition(
847-
stateChangeExpr: string | ((fromState: string, toState: string) => boolean),
881+
stateChangeExpr: string | ((fromState: string, toState: string, element?: any,
882+
params?: {[key: string]: any}) => boolean),
848883
steps: AnimationMetadata | AnimationMetadata[],
849884
options: AnimationOptions | null = null): AnimationTransitionMetadata {
850885
return {type: AnimationMetadataType.Transition, expr: stateChangeExpr, animation: steps, options};

packages/core/src/animation/animation_metadata_wrapped.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ export interface AnimationStateMetadata extends AnimationMetadata {
3838
* @deprecated This symbol has moved. Please Import from @angular/animations instead!
3939
*/
4040
export interface AnimationTransitionMetadata extends AnimationMetadata {
41-
expr: string|((fromState: string, toState: string) => boolean);
41+
expr: string|
42+
((fromState: string, toState: string, element: any, params: {[key: string]: any}) => boolean);
4243
animation: AnimationMetadata|AnimationMetadata[];
4344
}
4445

packages/core/test/animation/animation_integration_spec.ts

+53-3
Original file line numberDiff line numberDiff line change
@@ -301,19 +301,24 @@ const DEFAULT_COMPONENT_ID = '1';
301301

302302
it('should allow a transition to use a function to determine what method to run', () => {
303303
let valueToMatch = '';
304-
const transitionFn =
305-
(fromState: string, toState: string) => { return toState == valueToMatch; };
304+
let capturedElement: any;
305+
const transitionFn = (fromState: string, toState: string, element: any) => {
306+
capturedElement = element;
307+
return toState == valueToMatch;
308+
};
306309

307310
@Component({
308311
selector: 'if-cmp',
309-
template: '<div [@myAnimation]="exp"></div>',
312+
template: '<div #element [@myAnimation]="exp"></div>',
310313
animations: [
311314
trigger('myAnimation', [transition(
312315
transitionFn,
313316
[style({opacity: 0}), animate(1234, style({opacity: 1}))])]),
314317
]
315318
})
316319
class Cmp {
320+
@ViewChild('element')
321+
element: any;
317322
exp: any = '';
318323
}
319324

@@ -323,11 +328,13 @@ const DEFAULT_COMPONENT_ID = '1';
323328
const cmp = fixture.componentInstance;
324329
valueToMatch = cmp.exp = 'something';
325330
fixture.detectChanges();
331+
const element = cmp.element.nativeElement;
326332

327333
let players = getLog();
328334
expect(players.length).toEqual(1);
329335
let [p1] = players;
330336
expect(p1.totalTime).toEqual(1234);
337+
expect(capturedElement).toEqual(element);
331338
resetLog();
332339

333340
valueToMatch = 'something-else';
@@ -338,6 +345,49 @@ const DEFAULT_COMPONENT_ID = '1';
338345
expect(players.length).toEqual(0);
339346
});
340347

348+
it('should allow a transition to use a function to determine what method to run and expose any parameter values',
349+
() => {
350+
const transitionFn =
351+
(fromState: string, toState: string, element: any, params: {[key: string]: any}) => {
352+
return params['doMatch'] == true;
353+
};
354+
355+
@Component({
356+
selector: 'if-cmp',
357+
template: '<div [@myAnimation]="{value:exp, params: {doMatch:doMatch}}"></div>',
358+
animations: [
359+
trigger(
360+
'myAnimation',
361+
[transition(
362+
transitionFn, [style({opacity: 0}), animate(3333, style({opacity: 1}))])]),
363+
]
364+
})
365+
class Cmp {
366+
doMatch = false;
367+
exp: any = '';
368+
}
369+
370+
TestBed.configureTestingModule({declarations: [Cmp]});
371+
372+
const fixture = TestBed.createComponent(Cmp);
373+
const cmp = fixture.componentInstance;
374+
cmp.doMatch = true;
375+
fixture.detectChanges();
376+
377+
let players = getLog();
378+
expect(players.length).toEqual(1);
379+
let [p1] = players;
380+
expect(p1.totalTime).toEqual(3333);
381+
resetLog();
382+
383+
cmp.doMatch = false;
384+
cmp.exp = 'this-wont-match';
385+
fixture.detectChanges();
386+
387+
players = getLog();
388+
expect(players.length).toEqual(0);
389+
});
390+
341391
it('should allow a state value to be `0`', () => {
342392
@Component({
343393
selector: 'if-cmp',

tools/public_api_guard/animations/animations.d.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,9 @@ export interface AnimationStyleMetadata extends AnimationMetadata {
173173
/** @experimental */
174174
export interface AnimationTransitionMetadata extends AnimationMetadata {
175175
animation: AnimationMetadata | AnimationMetadata[];
176-
expr: string | ((fromState: string, toState: string) => boolean);
176+
expr: string | ((fromState: string, toState: string, element?: any, params?: {
177+
[key: string]: any;
178+
}) => boolean);
177179
options: AnimationOptions | null;
178180
}
179181

@@ -241,7 +243,9 @@ export declare function style(tokens: '*' | {
241243
}>): AnimationStyleMetadata;
242244

243245
/** @experimental */
244-
export declare function transition(stateChangeExpr: string | ((fromState: string, toState: string) => boolean), steps: AnimationMetadata | AnimationMetadata[], options?: AnimationOptions | null): AnimationTransitionMetadata;
246+
export declare function transition(stateChangeExpr: string | ((fromState: string, toState: string, element?: any, params?: {
247+
[key: string]: any;
248+
}) => boolean), steps: AnimationMetadata | AnimationMetadata[], options?: AnimationOptions | null): AnimationTransitionMetadata;
245249

246250
/** @experimental */
247251
export declare function trigger(name: string, definitions: AnimationMetadata[]): AnimationTriggerMetadata;

tools/public_api_guard/core/core.d.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ export interface AnimationTransitionEvent {
9494
/** @deprecated */
9595
export interface AnimationTransitionMetadata extends AnimationMetadata {
9696
animation: AnimationMetadata | AnimationMetadata[];
97-
expr: string | ((fromState: string, toState: string) => boolean);
97+
expr: string | ((fromState: string, toState: string, element: any, params: {
98+
[key: string]: any;
99+
}) => boolean);
98100
}
99101

100102
/** @deprecated */

0 commit comments

Comments
 (0)