fix(ivy): ensure animation @bindings work for {key:value} and empty bindings (#28026)

PR Close #28026
This commit is contained in:
Matias Niemelä
2019-01-09 13:40:13 -08:00
committed by Andrew Kushnir
parent 0136274f33
commit 94c0b7a362
3 changed files with 314 additions and 306 deletions

View File

@ -688,10 +688,19 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const instruction = mapBindingToInstruction(input.type);
if (input.type === BindingType.Animation) {
const value = input.value.visit(this._valueConverter);
// setProperty without a value doesn't make any sense
if (value.name || value.value) {
const bindingName = prepareSyntheticPropertyName(input.name);
// animation bindings can be presented in the following formats:
// 1j [@binding]="fooExp"
// 2. [@binding]="{value:fooExp, params:{...}}"
// 3. [@binding]
// 4. @binding
// only formats 1. and 2. include the actual binding of a value to
// an expression and therefore only those should be the only two that
// are allowed. The check below ensures that a binding with no expression
// does not get an empty `elementProperty` instruction created for it.
const hasValue = value && (value instanceof LiteralPrimitive) ? !!value.value : true;
if (hasValue) {
this.allocateBindingSlots(value);
const bindingName = prepareSyntheticPropertyName(input.name);
this.updateInstruction(input.sourceSpan, R3.elementProperty, () => {
return [
o.literal(elementIndex), o.literal(bindingName),

View File

@ -373,12 +373,12 @@ const DEFAULT_COMPONENT_ID = '1';
expect(players.length).toEqual(0);
});
fixmeIvy('unknown').it(
'should allow a transition to use a function to determine what method to run and expose any parameter values',
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; };
(fromState: string, toState: string, element: any, params: {[key: string]: any}) => {
return params['doMatch'] == true;
};
@Component({
selector: 'if-cmp',
@ -1567,8 +1567,9 @@ const DEFAULT_COMPONENT_ID = '1';
}
});
fixmeIvy('FW-932: Animation @triggers are not reported to the renderer in Ivy as they are in VE').it(
'should animate removals of nodes to the `void` state for each animation trigger, but treat all auto styles as pre styles',
fixmeIvy(
'FW-932: Animation @triggers are not reported to the renderer in Ivy as they are in VE')
.it('should animate removals of nodes to the `void` state for each animation trigger, but treat all auto styles as pre styles',
() => {
@Component({
selector: 'ani-cmp',
@ -1576,9 +1577,10 @@ const DEFAULT_COMPONENT_ID = '1';
<div *ngIf="exp" class="ng-if" [@trig1]="exp2" @trig2></div>
`,
animations: [
trigger('trig1', [transition(
'state => void', [animate(1000, style({opacity: 0}))])]),
trigger(
'trig1', [transition('state => void', [animate(1000, style({opacity: 0}))])]),
trigger('trig2', [transition(':leave', [animate(1000, style({width: '0px'}))])])
'trig2', [transition(':leave', [animate(1000, style({width: '0px'}))])])
]
})
class Cmp {
@ -1926,8 +1928,7 @@ const DEFAULT_COMPONENT_ID = '1';
expect(p.contains(c2)).toBeTruthy();
});
fixmeIvy('unknown').it(
'should detect trigger changes based on object.value properties', () => {
it('should detect trigger changes based on object.value properties', () => {
@Component({
selector: 'ani-cmp',
template: `
@ -1968,8 +1969,9 @@ const DEFAULT_COMPONENT_ID = '1';
expect(players[0].duration).toEqual(5678);
});
fixmeIvy('FW-932: Animation @triggers are not reported to the renderer in Ivy as they are in VE').it(
'should not render animations when the object expression value is the same as it was previously',
fixmeIvy(
'FW-932: Animation @triggers are not reported to the renderer in Ivy as they are in VE')
.it('should not render animations when the object expression value is the same as it was previously',
() => {
@Component({
selector: 'ani-cmp',
@ -2012,8 +2014,7 @@ const DEFAULT_COMPONENT_ID = '1';
expect(players.length).toEqual(0);
});
fixmeIvy('unknown').it(
'should update the final state styles when params update even if the expression hasn\'t changed',
it('should update the final state styles when params update even if the expression hasn\'t changed',
fakeAsync(() => {
@Component({
selector: 'ani-cmp',
@ -2105,8 +2106,7 @@ const DEFAULT_COMPONENT_ID = '1';
]);
});
fixmeIvy('unknown').it(
'should retain substituted styles on the element once the animation is complete if referenced in the final state',
it('should retain substituted styles on the element once the animation is complete if referenced in the final state',
fakeAsync(() => {
@Component({
selector: 'ani-cmp',

View File

@ -2444,8 +2444,7 @@ import {HostListener} from '../../src/metadata/directives';
expect(element.innerText.trim()).toMatch(/this\s+child/mg);
}));
fixmeIvy('unknown').it(
'should only mark outermost *directive nodes :enter and :leave when inserts and removals occur',
it('should only mark outermost *directive nodes :enter and :leave when inserts and removals occur',
() => {
@Component({
selector: 'ani-cmp',