Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(animations): expose element and params within transition matchers #22693

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/animations/browser/src/dsl/animation_ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[]) {
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 4 additions & 2 deletions packages/animations/browser/src/dsl/animation_trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});

Expand Down Expand Up @@ -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(
Expand Down
39 changes: 37 additions & 2 deletions packages/animations/src/animation_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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};
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/animation/animation_metadata_wrapped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
}

Expand Down
56 changes: 53 additions & 3 deletions packages/core/test/animation/animation_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '';
}

Expand All @@ -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';
Expand All @@ -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',
Expand Down
8 changes: 6 additions & 2 deletions tools/public_api_guard/animations/animations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion tools/public_api_guard/core/core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down