Skip to content
Permalink
Browse files

feat(animations): expose `element` and `params` within transition mat…

…chers (#22693)

PR Close #22693
  • Loading branch information...
matsko authored and kara committed Mar 13, 2018
1 parent db56836 commit 58b94e6f5e934bb3589b5ae17cb1dc8953f8d5db
@@ -46,7 +46,8 @@ export interface StateAst extends Ast<AnimationMetadataType.State> {
}

export interface TransitionAst extends Ast<AnimationMetadataType.Transition> {
matchers: ((fromState: string, toState: string) => boolean)[];
matchers: ((fromState: string, toState: string, element: any, params: {[key: string]:
any}) => boolean)[];
animation: Ast<AnimationMetadataType>;
queryCount: number;
depCount: number;
@@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
export const ANY_STATE = '*';
export declare type TransitionMatcherFn = (fromState: any, toState: any) => boolean;
export declare type TransitionMatcherFn =
(fromState: any, toState: any, element: any, params: {[key: string]: any}) => boolean;

export function parseTransitionExpr(
transitionValue: string | TransitionMatcherFn, errors: string[]): TransitionMatcherFn[] {
@@ -24,8 +24,8 @@ export class AnimationTransitionFactory {
private _triggerName: string, public ast: TransitionAst,
private _stateStyles: {[stateName: string]: AnimationStateStyles}) {}

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

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

function oneOrMoreTransitionsMatch(
matchFns: TransitionMatcherFn[], currentState: any, nextState: any): boolean {
return matchFns.some(fn => fn(currentState, nextState));
matchFns: TransitionMatcherFn[], currentState: any, nextState: any, element: any,
params: {[key: string]: any}): boolean {
return matchFns.some(fn => fn(currentState, nextState, element, params));
}

export class AnimationStateStyles {
@@ -47,8 +47,10 @@ export class AnimationTrigger {

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

matchTransition(currentState: any, nextState: any): AnimationTransitionFactory|null {
const entry = this.transitionFactories.find(f => f.match(currentState, nextState));
matchTransition(currentState: any, nextState: any, element: any, params: {[key: string]: any}):
AnimationTransitionFactory|null {
const entry =
this.transitionFactories.find(f => f.match(currentState, nextState, element, params));
return entry || null;
}

@@ -248,7 +248,8 @@ export class AnimationTransitionNamespace {
}
});

let transition = trigger.matchTransition(fromState.value, toState.value);
let transition =
trigger.matchTransition(fromState.value, toState.value, element, toState.params);
let isFallbackTransition = false;
if (!transition) {
if (!defaultToFallback) return;
@@ -103,7 +103,7 @@ import {makeTrigger} from '../shared';
it('should null when no results are found', () => {
const result = makeTrigger('name', [transition('a => b', animate(1111))]);

const trigger = result.matchTransition('b', 'a');
const trigger = result.matchTransition('b', 'a', {}, {});
expect(trigger).toBeFalsy();
});

@@ -226,7 +226,8 @@ function buildTransition(
trigger: AnimationTrigger, element: any, fromState: any, toState: any,
fromOptions?: AnimationOptions, toOptions?: AnimationOptions): AnimationTransitionInstruction|
null {
const trans = trigger.matchTransition(fromState, toState) !;
const params = toOptions && toOptions.params || {};
const trans = trigger.matchTransition(fromState, toState, element, params) !;
if (trans) {
const driver = new MockAnimationDriver();
return trans.build(
@@ -115,7 +115,9 @@ export interface AnimationStateMetadata extends AnimationMetadata {
* @experimental Animation support is experimental.
*/
export interface AnimationTransitionMetadata extends AnimationMetadata {
expr: string|((fromState: string, toState: string) => boolean);
expr: string|
((fromState: string, toState: string, element?: any,
params?: {[key: string]: any}) => boolean);
animation: AnimationMetadata|AnimationMetadata[];
options: AnimationOptions|null;
}
@@ -294,6 +296,38 @@ export interface AnimationStaggerMetadata extends AnimationMetadata {
* <div [@myAnimationTrigger]="myStatusExp">...</div>
* ```
*
* ### Using an inline function
* The `transition` animation method also supports reading an inline function which can decide
* if its associated animation should be run.
*
* ```
* // this method will be run each time the `myAnimationTrigger`
* // trigger value changes...
* function myInlineMatcherFn(fromState: string, toState: string, element: any, params: {[key:
string]: any}): boolean {
* // notice that `element` and `params` are also available here
* return toState == 'yes-please-animate';
* }
*
* @Component({
* selector: 'my-component',
* templateUrl: 'my-component-tpl.html',
* animations: [
* trigger('myAnimationTrigger', [
* transition(myInlineMatcherFn, [
* // the animation sequence code
* ]),
* ])
* ]
* })
* class MyComponent {
* myStatusExp = "yes-please-animate";
* }
* ```
*
* The inline method will be run each time the trigger
* value changes
*
* ## Disable Animations
* A special animation control binding called `@.disabled` can be placed on an element which will
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
* @experimental Animation support is experimental.
*/
export function transition(
stateChangeExpr: string | ((fromState: string, toState: string) => boolean),
stateChangeExpr: string | ((fromState: string, toState: string, element?: any,
params?: {[key: string]: any}) => boolean),
steps: AnimationMetadata | AnimationMetadata[],
options: AnimationOptions | null = null): AnimationTransitionMetadata {
return {type: AnimationMetadataType.Transition, expr: stateChangeExpr, animation: steps, options};
@@ -38,7 +38,8 @@ export interface AnimationStateMetadata extends AnimationMetadata {
* @deprecated This symbol has moved. Please Import from @angular/animations instead!
*/
export interface AnimationTransitionMetadata extends AnimationMetadata {
expr: string|((fromState: string, toState: string) => boolean);
expr: string|
((fromState: string, toState: string, element: any, params: {[key: string]: any}) => boolean);
animation: AnimationMetadata|AnimationMetadata[];
}

@@ -301,19 +301,24 @@ const DEFAULT_COMPONENT_ID = '1';

it('should allow a transition to use a function to determine what method to run', () => {
let valueToMatch = '';
const transitionFn =
(fromState: string, toState: string) => { return toState == valueToMatch; };
let capturedElement: any;
const transitionFn = (fromState: string, toState: string, element: any) => {
capturedElement = element;
return toState == valueToMatch;
};

@Component({
selector: 'if-cmp',
template: '<div [@myAnimation]="exp"></div>',
template: '<div #element [@myAnimation]="exp"></div>',
animations: [
trigger('myAnimation', [transition(
transitionFn,
[style({opacity: 0}), animate(1234, style({opacity: 1}))])]),
]
})
class Cmp {
@ViewChild('element')
element: any;
exp: any = '';
}

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

let players = getLog();
expect(players.length).toEqual(1);
let [p1] = players;
expect(p1.totalTime).toEqual(1234);
expect(capturedElement).toEqual(element);
resetLog();

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

it('should allow a transition to use a function to determine what method to run and expose any parameter values',
() => {
const transitionFn =
(fromState: string, toState: string, element: any, params: {[key: string]: any}) => {
return params['doMatch'] == true;
};

@Component({
selector: 'if-cmp',
template: '<div [@myAnimation]="{value:exp, params: {doMatch:doMatch}}"></div>',
animations: [
trigger(
'myAnimation',
[transition(
transitionFn, [style({opacity: 0}), animate(3333, style({opacity: 1}))])]),
]
})
class Cmp {
doMatch = false;
exp: any = '';
}

TestBed.configureTestingModule({declarations: [Cmp]});

const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.doMatch = true;
fixture.detectChanges();

let players = getLog();
expect(players.length).toEqual(1);
let [p1] = players;
expect(p1.totalTime).toEqual(3333);
resetLog();

cmp.doMatch = false;
cmp.exp = 'this-wont-match';
fixture.detectChanges();

players = getLog();
expect(players.length).toEqual(0);
});

it('should allow a state value to be `0`', () => {
@Component({
selector: 'if-cmp',
@@ -173,7 +173,9 @@ export interface AnimationStyleMetadata extends AnimationMetadata {
/** @experimental */
export interface AnimationTransitionMetadata extends AnimationMetadata {
animation: AnimationMetadata | AnimationMetadata[];
expr: string | ((fromState: string, toState: string) => boolean);
expr: string | ((fromState: string, toState: string, element?: any, params?: {
[key: string]: any;
}) => boolean);
options: AnimationOptions | null;
}

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

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

/** @experimental */
export declare function trigger(name: string, definitions: AnimationMetadata[]): AnimationTriggerMetadata;
@@ -94,7 +94,9 @@ export interface AnimationTransitionEvent {
/** @deprecated */
export interface AnimationTransitionMetadata extends AnimationMetadata {
animation: AnimationMetadata | AnimationMetadata[];
expr: string | ((fromState: string, toState: string) => boolean);
expr: string | ((fromState: string, toState: string, element: any, params: {
[key: string]: any;
}) => boolean);
}

/** @deprecated */

0 comments on commit 58b94e6

Please sign in to comment.
You can’t perform that action at this time.