'Angular 2 Animation - boolean trigger?
I'm trying to trigger a transition bound to a boolean property, but this doesn't seem to fire.
Here is a cut down version of my animation trigger
trigger(
'trueFalseAnimation', [
transition('* => true', [
style({backgroundColor: '#00f7ad'}),
animate('2500ms', style({backgroundColor: '#fff'}))
]),
transition('* => false', [
style({backgroundColor: '#ff0000'}),
animate('2500ms', style({backgroundColor: '#fff'}))
])
]
)
HTML:
<div [@trueFalseAnimation]="model.someProperty">Content here</div>
To test:
ngOnInit() {
setTimeout(() => {
this.model.someProperty = true;
setTimeOut(() => {
this.model.someProperty = false;
}, 5000);
}, 1000)
}
The trigger never happens when the someProperty
changes.
As a quick test I changed the trigger to use a string and it works
trigger(
'trueFalseAnimation', [
transition('* => Success', [
style({backgroundColor: '#00f7ad'}),
animate('2500ms', style({backgroundColor: '#fff'}))
]),
transition('* => Failed', [
style({backgroundColor: '#ff0000'}),
animate('2500ms', style({backgroundColor: '#fff'}))
])
]
)
To test:
ngOnInit() {
setTimeout(() => {
this.model.someProperty = "Success";
setTimeOut(() => {
this.model.someProperty = "Failed";
}, 5000);
}, 1000)
}
The second example works just fine
My questions are
- Are boolean supported as triggers?
- If yes to #1 what am I doing wrong?
Solution 1:[1]
- It seems not. I saw that an an issue (12337) has already been raised for this, but there has been no update so far.
- One other alternative is to use 1 and 0 instead of true and false.
trigger('isVisibleChanged', [
state('true' , style({ opacity: 1, transform: 'scale(1.0)' })),
state('false', style({ opacity: 0, transform: 'scale(0.0)' })),
transition('1 => 0', animate('300ms')),
transition('0 => 1', animate('900ms'))
])
Solution 2:[2]
I'm having the same issue. Not sure if boolean are supported as triggers, but the workaround I found was to define a string property with a getter to return the boolean value as string. Something like this:
get somePropertyStr():string {
return this.someProperty.toString();
}
Then you should bind your animation to that somePropertyStr
property.
Once again, this is an ugly workaround, best thing would be able to use the boolean value.
Solution 3:[3]
Edit: As some other answers allude to, the behavior has changed. If you're interested here is the relevant Angular source code:
const TRUE_BOOLEAN_VALUES = new Set<string>(['true', '1']);
const FALSE_BOOLEAN_VALUES = new Set<string>(['false', '0']);
function makeLambdaFromStates(lhs: string, rhs: string): TransitionMatcherFn {
const LHS_MATCH_BOOLEAN = TRUE_BOOLEAN_VALUES.has(lhs) || FALSE_BOOLEAN_VALUES.has(lhs);
const RHS_MATCH_BOOLEAN = TRUE_BOOLEAN_VALUES.has(rhs) || FALSE_BOOLEAN_VALUES.has(rhs);
return (fromState: any, toState: any): boolean => {
let lhsMatch = lhs == ANY_STATE || lhs == fromState;
let rhsMatch = rhs == ANY_STATE || rhs == toState;
if (!lhsMatch && LHS_MATCH_BOOLEAN && typeof fromState === 'boolean') {
lhsMatch = fromState ? TRUE_BOOLEAN_VALUES.has(lhs) : FALSE_BOOLEAN_VALUES.has(lhs);
}
if (!rhsMatch && RHS_MATCH_BOOLEAN && typeof toState === 'boolean') {
rhsMatch = toState ? TRUE_BOOLEAN_VALUES.has(rhs) : FALSE_BOOLEAN_VALUES.has(rhs);
}
return lhsMatch && rhsMatch;
};
}
It's a little tricky to follow unless you're debugging it yourself but basically the important thing to notice is that fromState
and toState
are the values (when there is a change) that you set on the animation in the template: eg. [@animation]="animationState"
So you can see they explicitly allow them to be booleans (typeof fromState === 'boolean'
).
The reason I got caught out (and found my way back to my own old answer) is that Angular's AnimationEvent
defines them as strings:
/**
* The name of the state from which the animation is triggered.
*/
fromState: string;
/**
* The name of the state in which the animation completes.
*/
toState: string;
So therefore on completion of an animation where your state is set to a boolean (eg. true
/ false
and NOT 'true'
/ 'false'
you'll run into a typing issue.
The actual value of event.fromState
may be true
so
event.fromState == true
gives a compiler error ('string' and 'boolean' have no type overlap)event.fromState == 'true'
will NEVER be true (since the value is actuallytrue
)
So you'd need to either use:
if (<any>(event.toState) == true)
OR
if ((event.toState as string | boolean) == true)
Old answer:
A state is defined as being a string, so we need to stick to that.
The simplest - but ickiest - way based on your code is this
<div [@trueFalseAnimation]="model.someProperty?.toString()">Content here</div>
But this is pretty awful, so this is probably better
<div [@trueFalseAnimation]="model.someProperty ? 'active' : 'inactive'">Content here</div>
<div [@trueFalseAnimation]="model.someProperty ? 'visible' : 'hidden'">Content here</div>
<div [@trueFalseAnimation]="model.someProperty ? 'up' : 'down'">Content here</div>
<div [@trueFalseAnimation]="model.someProperty ? 'left' : 'right'">Content here</div>
The best advice here would be to use a state that corresponds to what it really means. What does true and false really mean in this context anyway?
I considered making a pipe to convert a boolean, but the only benefit of that would be to make sure you're being consistent with your state strings.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | ScottL |
Solution 2 | OvSleep |
Solution 3 |