Reputation: 2480
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
Upvotes: 26
Views: 14791
Reputation: 146120
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 actually true
)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.
Upvotes: 0
Reputation: 1755
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'))
])
Upvotes: 29
Reputation: 298
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.
Upvotes: 3