Skip to content

Commit 7f4954b

Browse files
committed
fix(animations): change trigger binding syntax to function as a property binding []
Animation triggers can now be set via template bindings `[]` BREAKING CHANGE: animation trigger expressions within the template that are assigned as an element attribute (e.g. `@prop`) are deprecated. Please use the Angular2 property binding syntax (e.g. `[@prop]`) when assigning properties. ```ts // this is now deprecated <div @trigger="expression"></div> // do this instead <div [@trigger]="expression"></div> ```
1 parent f1fc1dc commit 7f4954b

File tree

7 files changed

+79
-36
lines changed

7 files changed

+79
-36
lines changed

modules/@angular/compiler-cli/integrationtest/src/animate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {AUTO_STYLE, Component, animate, state, style, transition, trigger} from
2323
<button (click)="setAsClosed()">Closed</button>
2424
<button (click)="setAsSomethingElse()">Something Else</button>
2525
<hr />
26-
<div @openClose="stateExpression">
26+
<div [@openClose]="stateExpression">
2727
Look at this box
2828
</div>
2929
`

modules/@angular/compiler/src/template_parser.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -466,8 +466,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
466466
if (isPresent(bindParts)) {
467467
hasBinding = true;
468468
if (isPresent(bindParts[1])) { // match: bind-prop
469-
this._parseProperty(
470-
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
469+
this._parsePropertyOrAnimation(
470+
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps,
471+
targetAnimationProps);
471472

472473
} else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden"
473474
var identifier = bindParts[8];
@@ -500,23 +501,31 @@ class TemplateParseVisitor implements HtmlAstVisitor {
500501
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
501502

502503
} else if (isPresent(bindParts[6])) { // match: bindon-prop
503-
this._parseProperty(
504-
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
504+
this._parsePropertyOrAnimation(
505+
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps,
506+
targetAnimationProps);
505507
this._parseAssignmentEvent(
506508
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
507509

508510
} else if (isPresent(bindParts[7])) { // match: animate-name
511+
if (attrName[0] == '@' && isPresent(attrValue) && attrValue.length > 0) {
512+
this._reportError(
513+
`Assigning animation triggers via @prop="exp" attributes with an expression is deprecated. Use [@prop]="exp" instead!`,
514+
attr.sourceSpan, ParseErrorLevel.WARNING);
515+
}
509516
this._parseAnimation(
510517
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetAnimationProps);
511518
} else if (isPresent(bindParts[9])) { // match: [(expr)]
512-
this._parseProperty(
513-
bindParts[9], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
519+
this._parsePropertyOrAnimation(
520+
bindParts[9], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps,
521+
targetAnimationProps);
514522
this._parseAssignmentEvent(
515523
bindParts[9], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
516524

517525
} else if (isPresent(bindParts[10])) { // match: [expr]
518-
this._parseProperty(
519-
bindParts[10], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
526+
this._parsePropertyOrAnimation(
527+
bindParts[10], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps,
528+
targetAnimationProps);
520529

521530
} else if (isPresent(bindParts[11])) { // match: (event)
522531
this._parseEvent(
@@ -555,12 +564,18 @@ class TemplateParseVisitor implements HtmlAstVisitor {
555564
targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
556565
}
557566

558-
private _parseProperty(
567+
private _parsePropertyOrAnimation(
559568
name: string, expression: string, sourceSpan: ParseSourceSpan,
560-
targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[]) {
561-
this._parsePropertyAst(
562-
name, this._parseBinding(expression, sourceSpan), sourceSpan, targetMatchableAttrs,
563-
targetProps);
569+
targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[],
570+
targetAnimationProps: BoundElementPropertyAst[]) {
571+
if (name[0] == '@') {
572+
this._parseAnimation(
573+
name.substr(1), expression, sourceSpan, targetMatchableAttrs, targetAnimationProps);
574+
} else {
575+
this._parsePropertyAst(
576+
name, this._parseBinding(expression, sourceSpan), sourceSpan, targetMatchableAttrs,
577+
targetProps);
578+
}
564579
}
565580

566581
private _parseAnimation(

modules/@angular/compiler/test/template_parser_spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,34 @@ export function main() {
282282
[BoundElementPropertyAst, PropertyBindingType.Animation, 'something', 'value2', null]
283283
]);
284284
});
285+
286+
it('should parse bound properties via @ and not report them as attributes and also report a deprecation warning',
287+
() => {
288+
expect(humanizeTplAst(parse('<div @something="value2">', []))).toEqual([
289+
[ElementAst, 'div'],
290+
[
291+
BoundElementPropertyAst, PropertyBindingType.Animation, 'something', 'value2', null
292+
]
293+
]);
294+
295+
expect(console.warnings).toEqual([[
296+
'Template parse warnings:',
297+
`Assigning animation triggers via @prop="exp" attributes with an expression is deprecated. Use [@prop]="exp" instead! ("<div [ERROR ->]@something="value2">"): TestComp@0:5`
298+
].join('\n')]);
299+
});
300+
301+
it('should not issue a warning when an animation property is bound without an expression',
302+
() => {
303+
humanizeTplAst(parse('<div @something>', []));
304+
expect(console.warnings.length).toEqual(0);
305+
});
306+
307+
it('should parse bound properties via [@] and not report them as attributes', () => {
308+
expect(humanizeTplAst(parse('<div [@something]="value2">', []))).toEqual([
309+
[ElementAst, 'div'],
310+
[BoundElementPropertyAst, PropertyBindingType.Animation, 'something', 'value2', null]
311+
]);
312+
});
285313
});
286314

287315
describe('events', () => {

modules/@angular/core/src/animation/metadata.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
520520
*
521521
* ```html
522522
* <!-- somewhere inside of my-component-tpl.html -->
523-
* <div @myAnimationTrigger="myStatusExp">...</div>
523+
* <div [@myAnimationTrigger]="myStatusExp">...</div>
524524
* ```
525525
*
526526
* #### The final `animate` call
@@ -569,7 +569,7 @@ export function transition(stateChangeExpr: string, steps: AnimationMetadata | A
569569
* {@link ComponentMetadata#animations-anchor animations section}. An animation trigger can
570570
* be placed on an element within a template by referencing the name of the
571571
* trigger followed by the expression value that the trigger is bound to
572-
* (in the form of `@triggerName="expression"`.
572+
* (in the form of `[@triggerName]="expression"`.
573573
*
574574
* ### Usage
575575
*
@@ -601,7 +601,7 @@ export function transition(stateChangeExpr: string, steps: AnimationMetadata | A
601601
*
602602
* ```html
603603
* <!-- somewhere inside of my-component-tpl.html -->
604-
* <div @myAnimationTrigger="myStatusExp">...</div>
604+
* <div [@myAnimationTrigger]="myStatusExp">...</div>
605605
* ```
606606
*
607607
* ### Example ([live demo](http://plnkr.co/edit/Kez8XGWBxWue7qP7nNvF?p=preview))

modules/@angular/core/test/animation/animation_integration_spec.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function declareTests({useJit}: {useJit: boolean}) {
5656
[TestComponentBuilder, AnimationDriver],
5757
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
5858
makeAnimationCmp(
59-
tcb, '<div *ngIf="exp" @myAnimation="exp"></div>',
59+
tcb, '<div *ngIf="exp" [@myAnimation]="exp"></div>',
6060
trigger(
6161
'myAnimation',
6262
[transition(
@@ -82,7 +82,7 @@ function declareTests({useJit}: {useJit: boolean}) {
8282
[TestComponentBuilder, AnimationDriver],
8383
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
8484
makeAnimationCmp(
85-
tcb, '<div *ngIf="exp" @myAnimation="exp"></div>',
85+
tcb, '<div *ngIf="exp" [@myAnimation]="exp"></div>',
8686
trigger(
8787
'myAnimation',
8888
[transition(
@@ -152,7 +152,7 @@ function declareTests({useJit}: {useJit: boolean}) {
152152
[TestComponentBuilder, AnimationDriver],
153153
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
154154
tcb = tcb.overrideTemplate(DummyIfCmp, `
155-
<div @myAnimation *ngIf="exp"></div>
155+
<div [@myAnimation] *ngIf="exp"></div>
156156
`);
157157
tcb.overrideAnimations(DummyIfCmp, [trigger(
158158
'myAnimation',
@@ -565,8 +565,8 @@ function declareTests({useJit}: {useJit: boolean}) {
565565
[TestComponentBuilder, AnimationDriver],
566566
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
567567
tcb = tcb.overrideTemplate(DummyIfCmp, `
568-
<div @rotate="exp"></div>
569-
<div @rotate="exp2"></div>
568+
<div [@rotate]="exp"></div>
569+
<div [@rotate]="exp2"></div>
570570
`);
571571
tcb.overrideAnimations(
572572
DummyIfCmp,
@@ -629,7 +629,7 @@ function declareTests({useJit}: {useJit: boolean}) {
629629
[TestComponentBuilder, AnimationDriver],
630630
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
631631
makeAnimationCmp(
632-
tcb, '<div class="my-if" *ngIf="exp" @myAnimation></div>',
632+
tcb, '<div class="my-if" *ngIf="exp" [@myAnimation]></div>',
633633
trigger(
634634
'myAnimation',
635635
[transition('* => void', [animate(1000, style({'opacity': 0}))])]),
@@ -661,7 +661,7 @@ function declareTests({useJit}: {useJit: boolean}) {
661661
[TestComponentBuilder, AnimationDriver],
662662
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
663663
makeAnimationCmp(
664-
tcb, '<div @myAnimation="exp"></div>',
664+
tcb, '<div [@myAnimation]="exp"></div>',
665665
trigger('myAnimation', [transition(
666666
'* => *',
667667
[
@@ -693,7 +693,7 @@ function declareTests({useJit}: {useJit: boolean}) {
693693
[TestComponentBuilder, AnimationDriver],
694694
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
695695
makeAnimationCmp(
696-
tcb, '<div @one="exp" @two="exp2"></div>',
696+
tcb, '<div [@one]="exp" [@two]="exp2"></div>',
697697
[
698698
trigger(
699699
'one',
@@ -754,7 +754,7 @@ function declareTests({useJit}: {useJit: boolean}) {
754754
[TestComponentBuilder, AnimationDriver],
755755
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
756756
makeAnimationCmp(
757-
tcb, '<div class="target" @status="exp"></div>',
757+
tcb, '<div class="target" [@status]="exp"></div>',
758758
[trigger(
759759
'status',
760760
[
@@ -784,7 +784,7 @@ function declareTests({useJit}: {useJit: boolean}) {
784784
[TestComponentBuilder, AnimationDriver],
785785
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
786786
makeAnimationCmp(
787-
tcb, '<div class="target" @status="exp"></div>',
787+
tcb, '<div class="target" [@status]="exp"></div>',
788788
[trigger(
789789
'status',
790790
[
@@ -841,7 +841,7 @@ function declareTests({useJit}: {useJit: boolean}) {
841841
[TestComponentBuilder, AnimationDriver],
842842
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
843843
makeAnimationCmp(
844-
tcb, '<div class="target" @status="exp"></div>',
844+
tcb, '<div class="target" [@status]="exp"></div>',
845845
[trigger(
846846
'status',
847847
[
@@ -868,7 +868,7 @@ function declareTests({useJit}: {useJit: boolean}) {
868868
[TestComponentBuilder, AnimationDriver],
869869
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
870870
makeAnimationCmp(
871-
tcb, '<div class="target" @status="exp"></div>',
871+
tcb, '<div class="target" [@status]="exp"></div>',
872872
[trigger(
873873
'status',
874874
[
@@ -897,7 +897,7 @@ function declareTests({useJit}: {useJit: boolean}) {
897897
[TestComponentBuilder, AnimationDriver],
898898
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
899899
makeAnimationCmp(
900-
tcb, '<div class="target" @status="exp"></div>',
900+
tcb, '<div class="target" [@status]="exp"></div>',
901901
[trigger(
902902
'status',
903903
[
@@ -958,7 +958,7 @@ function declareTests({useJit}: {useJit: boolean}) {
958958
[TestComponentBuilder, AnimationDriver],
959959
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
960960
makeAnimationCmp(
961-
tcb, '<div class="target" @status="exp"></div>',
961+
tcb, '<div class="target" [@status]="exp"></div>',
962962
[trigger(
963963
'status',
964964
[
@@ -1003,7 +1003,7 @@ function declareTests({useJit}: {useJit: boolean}) {
10031003
[TestComponentBuilder, AnimationDriver],
10041004
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
10051005
makeAnimationCmp(
1006-
tcb, '<div class="target" @status="exp"></div>',
1006+
tcb, '<div class="target" [@status]="exp"></div>',
10071007
[trigger(
10081008
'status',
10091009
[
@@ -1042,7 +1042,7 @@ function declareTests({useJit}: {useJit: boolean}) {
10421042
selector: 'if-cmp',
10431043
directives: [NgIf],
10441044
template: `
1045-
<div *ngIf="exp" @myAnimation="exp"></div>
1045+
<div *ngIf="exp" [@myAnimation]="exp"></div>
10461046
`
10471047
})
10481048
class DummyIfCmp {

modules/@angular/examples/core/animation/ts/dsl/animation_example.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import {Component, animate, state, style, transition, trigger} from '@angular/co
3535
<button (click)="expand()">Open</button>
3636
<button (click)="collapse()">Closed</button>
3737
<hr />
38-
<div class="toggle-container" @openClose="stateExpression">
38+
<div class="toggle-container" [@openClose]="stateExpression">
3939
Look at this box
4040
</div>
4141
`

modules/playground/src/animate/app/animate-app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ import {
2222
selector: 'animate-app',
2323
styleUrls: ['css/animate-app.css'],
2424
template: `
25-
<div @backgroundAnimation="bgStatus">
25+
<div [@backgroundAnimation]="bgStatus">
2626
<button (click)="state='start'">Start State</button>
2727
<button (click)="state='active'">Active State</button>
2828
|
2929
<button (click)="state='void'">Void State</button>
3030
<button (click)="state='default'">Unhandled (default) State</button>
3131
<button style="float:right" (click)="bgStatus='blur'">Blur Page</button>
3232
<hr />
33-
<div *ngFor="let item of items" class="box" @boxAnimation="state">
33+
<div *ngFor="let item of items" class="box" [@boxAnimation]="state">
3434
{{ item }}
3535
</div>
3636
</div>

0 commit comments

Comments
 (0)