forked from angular/angular.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtemplate-syntax.jade
1675 lines (1413 loc) · 76.3 KB
/
template-syntax.jade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
include ../_util-fns
// #docregion intro
:marked
Our Angular application manages what the user sees and does through the interaction of a Component class instance (the *component*) and its user-facing template.
Many of us are familiar with the component/template duality from our experience with model-view-controller (MVC) or model-view-viewmodel (MVVM). In Angular, the component plays the part of the controller/viewmodel, and the template represents the view.
Let’s find out what it takes to write a template for our view. We’ll cover these basic elements of template syntax:
* [HTML](#html)
* [Interpolation](#interpolation)
* [Template expressions](#template-expressions)
* [Template statements](#template-statements)
* [Binding syntax](#binding-syntax)
* [Property binding](#property-binding)
* [Attribute, class, and style bindings](#other-bindings)
* [Event binding](#event-binding)
* [Two-way data binding with `NgModel`](#ngModel)
* [Built-in directives](#directives)
* [NgClass](#ngClass)
* [NgStyle](#ngStyle)
* [NgIf](#ngIf)
* [NgSwitch](#ngSwitch)
* [NgFor](#ngFor)
* [* and <template>](#star-template)
* [Local template variables](#local-vars)
* [Input and output properties](#inputs-outputs)
* [Template expression operators](#expression-operators)
// #enddocregion intro
:marked
[Run the live example](/resources/live-examples/template-syntax/ts/plnkr.html)
// #docregion html-1
.l-main-section
:marked
## HTML
HTML is the language of the Angular template. Our [QuickStart](../quickstart.html) application had a template that was pure HTML:
// #enddocregion html-1
+makeExample('template-syntax/ts/app/app.component.html', 'my-first-app')(format=".")
// #docregion html-2
:marked
Almost all HTML syntax is valid template syntax. The `<script>` element is a notable exception; it is forbidden, eliminating the risk of script injection attacks. (In practice, `<script>` is simply ignored.)
Some legal HTML doesn’t make much sense in a template. The `<html>`, `<body>`, and `<base>` elements have no useful role in our repertoire. Pretty much everything else is fair game.
We can extend the HTML vocabulary of our templates with components and directives that appear as new elements and attributes. And we are about to learn how to get and set DOM values dynamically through data binding.
Let’s turn to the first form of data binding — interpolation — to see how much richer template HTML can be.
// #enddocregion html-2
// #docregion interpolation-1
.l-main-section
:marked
## Interpolation
We met the double-curly braces of interpolation, `{{` and `}}`, early in our Angular education.
// #enddocregion interpolation-1
+makeExample('template-syntax/ts/app/app.component.html', 'first-interpolation')(format=".")
// #docregion interpolation-2
:marked
We use interpolation to weave calculated strings into the text between HTML element tags and within attribute assignments.
// #enddocregion interpolation-2
+makeExample('template-syntax/ts/app/app.component.html', 'title+image')(format=".")
// #docregion interpolation-3
:marked
The material between the braces is often the name of a component property. Angular replaces that name with the
string value of the corresponding component property. In this example, Angular evaluates the `title` and `heroImageUrl` properties
and "fills in the blanks", displaying first a bold application title and then a heroic image.
More generally, the material between the braces is a **template expression** that Angular first **evaluates**
and then **converts to a string**. The following interpolation illustrates the point by adding the two numbers within braces:
// #enddocregion interpolation-3
+makeExample('template-syntax/ts/app/app.component.html', 'sum-1')(format=".")
// #docregion interpolation-4
:marked
The expression can invoke methods of the host component, as we do here with `getVal()`:
// #enddocregion interpolation-4
+makeExample('template-syntax/ts/app/app.component.html', 'sum-2')(format=".")
// #docregion interpolation-5
:marked
Angular evaluates all expressions in double curly braces, converts the expression results to strings, and concatenates them with neighboring literal strings. Finally,
it assigns this composite interpolated result to an **element or directive property**.
We appear to be inserting the result between element tags and assigning to attributes.
It's convenient to think so, and we rarely suffer for this mistake.
But it is not literally true. Interpolation is a special syntax that Angular converts into a
[property binding](#property-binding), as we'll explain below.
But first, let's take a closer look at template expressions and statements.
// #enddocregion interpolation-5
// #docregion template-expressions-1
<a id="template-expressions"></a>
.l-main-section
:marked
## Template expressions
A template **expression** produces a value.
Angular executes the expression and assigns it to a property of a binding target;
the target might be an HTML element, a component, or a directive.
We put a template expression within the interpolation braces when we wrote `{{1 + 1}}`.
We’ll see template expressions again in the [property binding](#property-binding) section,
appearing in quotes to the right of the `=` symbol as in `[property]="expression"`.
// #enddocregion template-expressions-1
// #docregion template-expressions-2
:marked
We write template expressions in a language that looks like JavaScript.
Many JavaScript expressions are legal template expressions, but not all.
JavaScript expressions that have or promote side effects are prohibited,
including:
* assignments (`=`, `+=`, `-=`)
* the `new` operator
* chaining expressions with `;` or `,`
* increment and decrement operators (`++` and `--`)
Other notable differences from JavaScript syntax include:
* no support for the bitwise operators `|` and `&`
* new [template expression operators](#expression-operators), such as `|` and `?.`
// #enddocregion template-expressions-2
// #docregion template-expressions-context
:marked
<a id="expression-context"></a>
### Expression context
Perhaps more surprising, template expressions cannot refer to anything in the global namespace.
They can’t refer to `window` or `document`. They can’t call `console.log` or `Math.max`.
They are restricted to referencing members of the expression context.
The *expression context* is typically the **component instance**, which is
the source of binding values.
When we see *title* wrapped in double-curly braces, <code>{{title}}</code>,
we know that `title` is a property of the data-bound component.
When we see *isUnchanged* in `[disabled]="isUnchanged"`,
we know we are referring to that component's `isUnchanged` property.
The component itself is usually the expression *context*, in which case
the template expression usually references that component.
The expression context can include objects other than the component.
A [local template variable](#local-vars) is one such alternative context object.
// #enddocregion template-expressions-context
// #docregion template-expressions-guidelines
:marked
<a id="no-side-effects"></a>
### Expression guidelines
Template expressions can make or break an application.
Please follow these guidelines:
* [No visible side effects](#no-visible-side-effects)
* [Quick execution](#quick-execution)
* [Simplicity](#simplicity)
* [Idempotence](#idempotence)
The only exceptions to these guidelines should be in specific circumstances that you thoroughly understand.
#### No visible side effects
A template expression should not change any application state other than the value of the
target property.
This rule is essential to Angular's "unidirectional data flow" policy.
We should never worry that reading a component value might change some other displayed value.
The view should be stable throughout a single rendering pass.
#### Quick execution
Angular executes template expressions more often than we think.
They can be called after every keypress or mouse move.
Expressions should finish quickly or the user experience may drag, especially on slower devices.
Consider caching values computed from other values when the computation is expensive.
#### Simplicity
Although it's possible to write quite complex template expressions, we really shouldn't.
A property name or method call should be the norm.
An occasional Boolean negation (`!`) is OK.
Otherwise, confine application and business logic to the component itself,
where it will be easier to develop and test.
#### Idempotence
An [idempotent](https://en.wikipedia.org/wiki/Idempotence) expression is ideal because
it is free of side effects and improves Angular's change detection performance.
In Angular terms, an idempotent expression always returns *exactly the same thing* until
one of its dependent values changes.
// #enddocregion template-expressions-guidelines
// #docregion template-expressions-guidelines-2
:marked
Dependent values should not change during a single turn of the event loop.
If an idempotent expression returns a string or a number, it returns the same string or number
when called twice in a row. If the expression returns an object (including a `Date` or `Array`),
it returns the same object *reference* when called twice in a row.
// #enddocregion template-expressions-guidelines-2
// #docregion template-statements-1
<a id="template-statements"></a>
.l-main-section
:marked
## Template statements
A template **statement** responds to an **event** raised by a binding target
such as an element, component, or directive.
We’ll see template statements in the [event binding](#event-binding) section,
appearing in quotes to the right of the `=` symbol as in `(event)="statement"`.
A template statement *has a side effect*.
It's how we update application state from user input.
There would be no point to responding to an event otherwise.
.l-sub-section
:marked
Responding to events is the other side of Angular's "unidirectional data flow".
We're free to change anything, anywhere, during this turn of the event loop.
// #enddocregion template-statements-1
// #docregion template-statements-2
:marked
Like template expressions, template *statements* use a language that looks like JavaScript.
The template statement parser is different than the template expression parser and
specifically supports both basic assignment (`=`) and chaining expressions with semicolons (`;`) and commas (`,`).
However, certain JavaScript syntax is not allowed:
* the `new` operator
* increment and decrement operators, `++` and `--`
* operator assignment, such as `+=` and `-=`
* the bitwise operators `|` and `&`
* the [template expression operators](#expression-operators)
// #enddocregion template-statements-2
// #docregion template-statements-3
:marked
### Statement context
As with expressions, statements cannot refer to anything in the global namespace.
They can’t refer to `window` or `document`. They can’t call `console.log` or `Math.max`.
Statements are restricted to referencing members of the statement context.
The statement context is typically the **component instance** to which we are binding an event.
The *onSave* in `(click)="onSave()"` is sure to be a method of the data-bound component instance.
The statement context may include an object other than the component.
A [local template variable](#local-vars) is one such alternative context object.
We'll frequently see the reserved `$event` symbol in event binding statements,
representing the "message" or "payload" of the raised event.
### Statement guidelines
As with expressions, avoid writing complex template statements.
A method call or simple property assignment should be the norm.
Now that we have a feel for template expressions and statements,
we’re ready to learn about the varieties of data binding syntax beyond interpolation.
// #enddocregion template-statements-3
// #docregion binding-syntax-1
.l-main-section
:marked
<a id="binding-syntax"></a>
## Binding syntax: An overview
Data binding is a mechanism for coordinating what users see with application data values.
While we could push values to and pull values from HTML,
the application is easier to write, read, and maintain if we turn these chores over to a binding framework.
We simply declare bindings between binding sources and target HTML elements and let the framework do the work.
Angular provides many kinds of data binding, and we’ll discuss each of them in this chapter.
First we'll take a high-level view of Angular data binding and its syntax.
We can group all bindings into three categories by the direction in which data flows.
Each category has its distinctive syntax:
table
tr
th Data direction
th Syntax
th Binding type
tr
td One-way<br>from data source<br>to view target
td
code-example(format="" ).
{{expression}}
[target] = "expression"
bind-target = "expression"
td.
Interpolation<br>
Property<br>
Attribute<br>
Class<br>
Style
tr
td One-way<br>from view target<br>to data source
td
code-example(format="" ).
(target) = "statement"
on-target = "statement"
td Event
tr
td Two-way
td
code-example(format="" ).
[(target)] = "expression"
bindon-target = "expression"
td Two-way
:marked
Binding types other than interpolation have a **target name** to the left of the equal sign,
either surrounded by punctuation (`[]`, `()`) or preceded by a prefix (`bind-`, `on-`, `bindon-`).
What is that target? Before we can answer that question, we must challenge ourselves to look at template HTML in a new way.
### A new mental model
With all the power of data binding and our ability to extend the HTML vocabulary
with custom markup, it is tempting to think of template HTML as *HTML Plus*.
Well, it *is* HTML Plus.
But it’s also significantly different than the HTML we’re used to.
We really need a new mental model.
In the normal course of HTML development, we create a visual structure with HTML elements, and
we modify those elements by setting element attributes with string constants.
// #enddocregion binding-syntax-1
+makeExample('template-syntax/ts/app/app.component.html', 'img+button')(format=".")
// #docregion binding-syntax-2
:marked
We still create a structure and initialize attribute values this way in Angular templates.
Then we learn to create new elements with components that encapsulate HTML
and drop them into our templates as if they were native HTML elements.
// #enddocregion binding-syntax-2
+makeExample('template-syntax/ts/app/app.component.html', 'hero-detail-1')(format=".")
// #docregion binding-syntax-3
:marked
That’s HTML Plus.
Now we start to learn about data binding. The first binding we meet might look like this:
// #enddocregion binding-syntax-3
+makeExample('template-syntax/ts/app/app.component.html', 'disabled-button-1')(format=".")
// #docregion binding-syntax-4
:marked
We’ll get to that peculiar bracket notation in a moment. Looking beyond it,
our intuition tells us that we’re binding to the button's `disabled` attribute and setting
it to the current value of the component’s `isUnchanged` property.
Our intuition is wrong! Our everyday HTML mental model is misleading us.
In fact, once we start data binding, we are no longer working with HTML *attributes*. We aren't setting attributes.
We are setting the *properties* of DOM elements, components, and directives.
// #enddocregion binding-syntax-4
// #docregion binding-syntax-attribute-vs-property
.l-sub-section
:marked
### HTML attribute vs. DOM property
The distinction between an HTML attribute and a DOM property is crucial to understanding how Angular binding works.
**Attributes are defined by HTML. Properties are defined by the DOM (Document Object Model).**
* A few HTML attributes have 1:1 mapping to properties. `id` is one example.
* Some HTML attributes don't have corresponding properties. `colspan` is one example.
* Some DOM properties don't have corresponding attributes. `textContent` is one example.
* Many HTML attributes appear to map to properties ... but not in the way we might think!
That last category can be especially confusing ... until we understand this general rule:
**Attributes *initialize* DOM properties and then they are done.
Property values can change; attribute values can't.**
For example, when the browser renders `<input type="text" value="Bob">`, it creates a
corresponding DOM node with a `value` property *initialized* to "Bob".
When the user enters "Sally" into the input box, the DOM element `value` *property* becomes "Sally".
But the HTML `value` *attribute* remains unchanged as we discover if we ask the input element
about that attribute: `input.getAttribute('value') // returns "Bob"`
The HTML attribute `value` specifies the *initial* value; the DOM `value` property is the *current* value.
The `disabled` attribute is another peculiar example. A button's `disabled` *property* is
`false` by default so the button is enabled.
When we add the `disabled` *attribute*, its presence alone initializes the button's `disabled` *property* to `true`
so the button is disabled.
Adding and removing the `disabled` *attribute* disables and enables the button. The value of the *attribute* is irrelevant,
which is why we cannot enable a button by writing `<button disabled="false">Still Disabled</button>`.
Setting the button's `disabled` *property* (say, with an Angular binding) disables or enables the button.
The value of the *property* matters.
**The HTML attribute and the DOM property are not the same thing, even when they have the same name.**
// #enddocregion binding-syntax-attribute-vs-property
// #docregion binding-syntax-5
:marked
This is so important, we’ll say it again.
**Template binding works with *properties* and *events*, not *attributes*.**
// #enddocregion binding-syntax-5
// #docregion binding-syntax-world-without-attributes
.callout.is-helpful
header A world without attributes
:marked
In the world of Angular 2, the only role of attributes is to initialize element and directive state.
When we data bind, we're dealing exclusively with element and directive properties and events.
Attributes effectively disappear.
// #enddocregion binding-syntax-world-without-attributes
// #docregion binding-syntax-targets
:marked
With this model firmly in mind, let's learn about binding targets.
### Binding targets
The **target of a data binding** is something in the DOM.
Depending on the binding type, the target can be an
(element | component | directive) property, an
(element | component | directive) event, or (rarely) an attribute name.
The following table summarizes:
// #enddocregion binding-syntax-targets
// If you update this table, UPDATE it in Dart & JS, too.
<div width="90%">
table
tr
th Binding type
th Target
th Examples
tr
td Property
td.
Element property<br>
Component property<br>
Directive property
td
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-syntax-1')(format=".")
tr
td Event
td.
Element event<br>
Component event<br>
Directive event
td
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-syntax-1')(format=".")
tr
td Two-way
td.
Event and property
td
+makeExample('template-syntax/ts/app/app.component.html', '2-way-binding-syntax-1')(format=".")
tr
td Attribute
td.
Attribute
(the exception)
td
+makeExample('template-syntax/ts/app/app.component.html', 'attribute-binding-syntax-1')(format=".")
tr
td Class
td.
<code>class</code> property
td
+makeExample('template-syntax/ts/app/app.component.html', 'class-binding-syntax-1')(format=".")
tr
td Style
td.
<code>style</code> property
td
+makeExample('template-syntax/ts/app/app.component.html', 'style-binding-syntax-1')(format=".")
</div>
// #docregion binding-syntax-end
:marked
Let’s descend from the architectural clouds and look at each of these binding types in concrete detail.
// #enddocregion binding-syntax-end
// #docregion property-binding-1
.l-main-section
:marked
## Property binding
We write a template **property binding** when we want to set a property of a view element to the value of
a [template expression](#template-expressions).
The most common property binding sets an element property to a component property value. An example is
binding the `src` property of an image element to a component’s `heroImageUrl` property:
// #enddocregion property-binding-1
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-1')(format=".")
// #docregion property-binding-2
:marked
Another example is disabling a button when the component says that it `isUnchanged`:
// #enddocregion property-binding-2
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-2')(format=".")
// #docregion property-binding-3
:marked
Another is setting a property of a directive:
// #enddocregion property-binding-3
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-3')(format=".")
// #docregion property-binding-4
:marked
Yet another is setting the model property of a custom component (a great way
for parent and child components to communicate):
// #enddocregion property-binding-4
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-4')(format=".")
// #docregion property-binding-5
:marked
### One-way *in*
People often describe property binding as *one-way data binding* because it flows a value in one direction,
from a component’s data property into a target element property.
We cannot use property binding to pull values *out* of the target element.
We can't bind to a property of the target element to read it. We can only set it.
// #enddocregion property-binding-5
// #docregion property-binding-6
.l-sub-section
:marked
Nor can we use property binding to *call* a method on the target element.
If the element raises events we can listen to them with an [event binding](#event-binding).
If we must read a target element property or call one of its methods,
we'll need a different technique.
See the API reference for
[viewChild](/docs/ts/latest/api/core/ViewChild-var.html) and
[contentChild](/docs/ts/latest/api/core/ContentChild-var.html).
// #enddocregion property-binding-6
// TODO (global): once we have api docs everywhere, change /docs/ts/latest/ to ../
// #docregion property-binding-7
:marked
### Binding target
A name between enclosing square brackets identifies the target property. The target property in the following code is the image element’s `src` property.
// #enddocregion property-binding-7
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-1')(format=".")
// #docregion property-binding-8
:marked
Some people prefer the `bind-` prefix alternative, known as the *canonical form*:
// #enddocregion property-binding-8
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-5')(format=".")
// #docregion property-binding-9
:marked
The target name is always the name of a property, even when it appears to be the name of something else. We see `src` and may think it’s the name of an attribute. No. It’s the name of an image element property.
Element properties may be the more common targets,
but Angular looks first to see if the name is a property of a known directive,
as it is in the following example:
// #enddocregion property-binding-9
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-3')(format=".")
// #docregion property-binding-10
.l-sub-section
:marked
Technically, Angular is matching the name to a directive [input](#inputs-outputs),
one of the property names listed in the directive’s `inputs` array or a property decorated with `@Input()`.
Such inputs map to the directive’s own properties.
:marked
If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error.
### Avoid side effects
As we've already discussed, evaluation of a template expression should have no visible side effects. The expression language itself does its part to keep us safe. We can’t assign a value to anything in a property binding expression nor use the increment and decorator operators.
Of course, our expression might invoke a property or method that has side effects. Angular has no way of knowing that or stopping us.
The expression could call something like `getFoo()`. Only we know what `getFoo()` does.
If `getFoo()` changes something and we happen to be binding to that something, we risk an unpleasant experience. Angular may or may not display the changed value. Angular may detect the change and throw a warning error. Our general advice: stick to data properties and to methods that return values and do no more.
### Return the proper type
The template expression should evaluate to the type of value expected by the target property.
Return a string if the target property expects a string.
Return a number if the target property expects a number.
Return an object if the target property expects an object.
The `hero` property of the `HeroDetail` component expects a `Hero` object, which is exactly what we’re sending in the property binding:
// #enddocregion property-binding-10
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-4')(format=".")
// #docregion property-binding-11
:marked
### Remember the brackets
The brackets tell Angular to evaluate the template expression.
If we forget the brackets, Angular treats the string as a constant and *initializes the target property* with that string.
It does *not* evaluate the string!
Don't make the following mistake:
// #enddocregion property-binding-11
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-6')(format=".")
// #docregion property-binding-12
a(id="one-time-initialization")
:marked
### One-time string initialization
We *should* omit the brackets when all of the following are true:
* The target property accepts a string value.
* The string is a fixed value that we can bake into the template.
* This initial value never changes.
We routinely initialize attributes this way in standard HTML, and it works
just as well for directive and component property initialization.
The following example initializes the `prefix` property of the `HeroDetailComponent` to a fixed string,
not a template expression. Angular sets it and forgets it.
// #enddocregion property-binding-12
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-7')(format=".")
// #docregion property-binding-13
:marked
The `[hero]` binding, on the other hand, remains a live binding to the component's `currentHero` property.
### Property binding or interpolation?
We often have a choice between interpolation and property binding. The following binding pairs do the same thing:
// #enddocregion property-binding-13
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation')(format=".")
// #docregion property-binding-14
:marked
Interpolation is a convenient alternative for property binding in many cases.
In fact, Angular translates those interpolations into the corresponding property bindings
before rendering the view.
There is no technical reason to prefer one form to the other.
We lean toward readability, which tends to favor interpolation.
We suggest establishing coding style rules for the organization and choosing the form that
both conforms to the rules and feels most natural for the task at hand.
// #enddocregion property-binding-14
// #docregion other-bindings-1
.l-main-section
:marked
<a id="other-bindings"></a>
## Attribute, class, and style bindings
The template syntax provides specialized one-way bindings for scenarios less well suited to property binding.
### Attribute binding
We can set the value of an attribute directly with an **attribute binding**.
// #enddocregion other-bindings-1
// #docregion other-bindings-2
.l-sub-section
:marked
This is the only exception to the rule that a binding sets a target property. This is the only binding that creates and sets an attribute.
:marked
We have stressed throughout this chapter that setting an element property with a property binding is always preferred to setting the attribute with a string. Why does Angular offer attribute binding?
**We must use attribute binding when there is no element property to bind.**
Consider the [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA),
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG), and
table span attributes. They are pure attributes.
They do not correspond to element properties, and they do not set element properties.
There are no property targets to bind to.
We become painfully aware of this fact when we try to write something like this:
code-example(language="html").
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
:marked
We get this error:
code-example(format="", language="html").
Template parse errors:
Can't bind to 'colspan' since it isn't a known native property
:marked
As the message says, the `<td>` element does not have a `colspan` property.
It has the "colspan" *attribute*, but
interpolation and property binding can set only *properties*, not attributes.
We need attribute bindings to create and bind to such attributes.
Attribute binding syntax resembles property binding.
Instead of an element property between brackets, we start with the prefix **`attr`**,
followed by a dot (`.`) and the name of the attribute. We then set the attribute
value, using an expression that resolves to a string.
Here we bind `[attr.colspan]` to a calculated value:
// #enddocregion other-bindings-2
+makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-colspan')(format=".")
// #docregion other-bindings-3
:marked
Here's how the table renders:
<table border="1px">
<tr><td colspan="2">One-Two</td></tr>
<tr><td>Five</td><td>Six</td></tr>
</table>
One of the primary use cases for attribute binding
is to set ARIA attributes, as in this example:
// #enddocregion other-bindings-3
+makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-aria')(format=".")
// #docregion other-bindings-class-1
:marked
### Class binding
We can add and remove CSS class names from an element’s `class` attribute with
a **class binding**.
Class binding syntax resembles property binding.
Instead of an element property between brackets, we start with the prefix `class`,
optionally followed by a dot (`.`) and the name of a CSS class: `[class.class-name]`.
The following examples show how to add and remove the application's "special" class
with class bindings. Here's how we set the attribute without binding:
// #enddocregion other-bindings-class-1
+makeExample('template-syntax/ts/app/app.component.html', 'class-binding-1')(format=".")
// #docregion other-bindings-class-2
:marked
We can replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding.
// #enddocregion other-bindings-class-2
+makeExample('template-syntax/ts/app/app.component.html', 'class-binding-2')(format=".")
// #docregion other-bindings-class-3
:marked
Finally, we can bind to a specific class name.
Angular adds the class when the template expression evaluates to something truthy.
It removes the class when the expression is falsey.
// #enddocregion other-bindings-class-3
+makeExample('template-syntax/ts/app/app.component.html', 'class-binding-3')(format=".")
// #docregion other-bindings-class-4
.l-sub-section
:marked
While this is a fine way to toggle a single class name,
we generally prefer the [NgClass directive](#ngClass) for managing multiple class names at the same time.
// #enddocregion other-bindings-class-4
// #docregion other-bindings-style-1
:marked
### Style binding
We can set inline styles with a **style binding**.
Style binding syntax resembles property binding.
Instead of an element property between brackets, we start with the prefix `style`,
followed by a dot (`.`) and the name of a CSS style property: `[style.style-property]`.
// #enddocregion other-bindings-style-1
+makeExample('template-syntax/ts/app/app.component.html', 'style-binding-1')(format=".")
// #docregion other-bindings-style-2
:marked
Some style binding styles have unit extension. Here we conditionally set the font size in “em” and “%” units .
// #enddocregion other-bindings-style-2
+makeExample('template-syntax/ts/app/app.component.html', 'style-binding-2')(format=".")
// #docregion other-bindings-style-3
.l-sub-section
:marked
While this is a fine way to set a single style,
we generally prefer the [NgStyle directive](#ngStyle) when setting several inline styles at the same time.
// #enddocregion other-bindings-style-3
// #docregion event-binding-1
.l-main-section
:marked
## Event binding
The bindings we’ve met so far flow data in one direction: *from the component to an element*.
Users don’t just stare at the screen. They enter text into input boxes. They pick items from lists.
They click buttons. Such user actions may result in a flow of data in the opposite direction:
*from an element to the component*.
The only way to know about a user action is to listen for certain events such as
keystrokes, mouse movements, clicks, and touches.
We declare our interest in user actions through Angular event binding.
Event binding syntax consists of a **target event** within parentheses on the left of an equal sign, and a quoted
[template statement](#template-statements) on the right.
The following event binding listens for the button’s click event, calling
the component's `onSave()` method whenever a click occurs:
// #enddocregion event-binding-1
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".")
// #docregion event-binding-2
:marked
### Target event
A **name between enclosing parentheses** — for example, `(click)` —
identifies the target event. In the following example, the target is the button’s click event.
// #enddocregion event-binding-2
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".")
// #docregion event-binding-3
:marked
Some people prefer the `on-` prefix alternative, known as the *canonical form*:
// #enddocregion event-binding-3
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-2')(format=".")
// #docregion event-binding-4
:marked
Element events may be the more common targets, but Angular looks first to see if the name matches an event property
of a known directive, as it does in the following example:
// #enddocregion event-binding-4
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-3')(format=".")
// #docregion event-binding-5
:marked
If the name fails to match an element event or an output property of a known directive,
Angular reports an “unknown directive” error.
### $event and event handling statements
In an event binding, Angular sets up an event handler for the target event.
When the event is raised, the handler executes the template statement.
The template statement typically involves a receiver that wants to do something
in response to the event, such as take a value from the HTML control and store it
in a model.
The binding conveys information about the event, including data values, through
an **event object named `$event`**.
The shape of the event object is determined by the target event itself.
If the target event is a native DOM element event, the `$event` is a
[DOM event object]( https://developer.mozilla.org/en-US/docs/Web/Events),
with properties such as `target` and `target.value`.
Consider this example:
// #enddocregion event-binding-5
+makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".")
// #docregion event-binding-6
:marked
We’re binding the input box `value` to a `firstName` property, and we’re listening for changes by binding to the input box’s `input` event.
When the user makes changes, the `input` event is raised, and the binding executes the statement within a context that includes the DOM event object, `$event`.
To update the `firstName` property, we must get the changed text by following
the path `$event.target.value`.
If the event belongs to a directive (remember: components are directives), `$event` has whatever shape the directive chose to produce.
<a id="eventemitter"></a>
<a id="custom-event"></a>
### Custom events with EventEmitter
Directives typically raise custom events with an Angular [EventEmitter](/docs/ts/latest/api/core/EventEmitter-class.html).
A directive creates an `EventEmitter` and exposes it as a property.
The directive calls `EventEmitter.emit(payload)` to fire an event, passing in a message payload that can be anything.
Parent directives listen for the event by binding to this property and accessing the payload through the `$event` object.
Consider a `HeroDetailComponent` that presents hero information and responds to user actions.
Although the `HeroDetailComponent` has a delete button it doesn't know how to delete the hero itself.
The best it can do is raise an event reporting the user's delete request.
Here are the pertinent excerpts from that `HeroDetailComponent`:
// #enddocregion event-binding-6
+makeExample('template-syntax/ts/app/hero-detail.component.ts',
'template-1', 'HeroDetailComponent.ts (template)')(format=".")
+makeExample('template-syntax/ts/app/hero-detail.component.ts',
'deleteRequest', 'HeroDetailComponent.ts (delete logic)')(format=".")
// #docregion event-binding-7
:marked
The component defines a `deleteRequest` property that returns an `EventEmitter`.
When the user clicks *delete*, the component invokes the `delete()` method
which tells the `EventEmitter` to emit a `Hero` object.
Now imagine a hosting parent component that binds to the `HeroDetailComponent`'s `deleteRequest` event.
// #enddocregion event-binding-7
+makeExample('template-syntax/ts/app/app.component.html',
'event-binding-to-component')(format=".")
// #docregion event-binding-8
:marked
When the `deleteRequest` event fires, Angular calls the parent component's `deleteHero` method,
passing the *hero-to-delete* (emitted by `HeroDetail`) in the `$event` variable.
### Template statements have side effects
The `deleteHero` method has a side effect: It deletes a hero.
Template statement side effects are not just OK, they are expected.
Deleting the hero updates the model, perhaps triggering other changes
including queries and saves to a remote server.
These changes percolate through the system and are ultimately displayed in this and other views.
It's all good.
// #enddocregion event-binding-8
//
:marked
### Event bubbling and propagation [TODO: reinstate this section when it becomes true]
Angular invokes the event-handling statement if the event is raised by the current element or one of its child elements.
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-bubbling')(format=".")
:marked
Many DOM events, both [native](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Overview_of_Events_and_Handlers ) and [custom](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events ), bubble up their ancestor tree of DOM elements until an event handler along the way prevents further propagation.
.l-sub-section
:marked
`EventEmitter` events don’t bubble.
:marked
The result of an event binding statement determines whether
[event propagation](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Examples#Example_5:_Event_Propagation)
continues or stops with the current element.
Event propagation stops if the binding statement returns a falsey value (as does a method with no return value).
Clicking the button in the next example triggers a save;
the click doesn't make it to the outer `<div>` so the div's save handler is not called.
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-no-propagation')(format=".")
:marked
Propagation continues if the statement returns a truthy value. In the next example, the click is heard by both the button
and the outer `<div>`, causing a double save.
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-propagation')(format=".")
// #docregion ngModel-1
.l-main-section
:marked
<a name="ngModel"></a>
## Two-way binding with NgModel
When developing data entry forms, we often want to both display a data property and update that property when the user makes changes.
The `NgModel` directive serves that purpose, as in this example:
// #enddocregion ngModel-1
+makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".")
// #docregion ngModel-2
.callout.is-important
header
:marked
<span style="font-family:consolas; monospace">[()]</span> = banana in a box
:marked
To remember that the parentheses go inside the brackets, visualize a *banana in a box*.
:marked
Alternatively, we can use the canonical prefix form:
// #enddocregion ngModel-2
+makeExample('template-syntax/ts/app/app.component.html', 'NgModel-2')(format=".")
// #docregion ngModel-3
:marked
There’s a story behind this construction, a story that builds on the property and event binding techniques we learned previously.
We could have achieved the same result as `NgModel` with separate bindings to
the `<input>` element's `value` property and `input` event.
// #enddocregion ngModel-3
+makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".")
// #docregion ngModel-4
:marked
That’s cumbersome. Who can remember what element property to set and what event reports user changes?
How do we extract the currently displayed text from the input box so we can update the data property?
Who wants to look that up each time?
That `ngModel` directive hides these onerous details. It wraps the element’s `value` property, listens to the `input` event,
and raises its own event with the changed text ready for us to consume.
// #enddocregion ngModel-4
+makeExample('template-syntax/ts/app/app.component.html', 'NgModel-3')(format=".")
// #docregion ngModel-5
:marked
That’s an improvement, but it could be better.
We shouldn't have to mention the data property twice. Angular should be able to read the component’s data property and set it
with a single declaration — which it can with the `[( )]` syntax:
// #enddocregion ngModel-5
+makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".")
// #docregion ngModel-6
.l-sub-section
:marked
Internally, Angular maps the term `ngModel` to an `ngModel` input property and an
`ngModelChange` output property.
That’s a specific example of a more general pattern in which Angular matches `[(x)]` to an `x` input property
for property binding and an `xChange` output property for event binding.
:marked
Is `[(ngModel)]` all we need? Is there ever a reason to fall back to its expanded form?
Well, `NgModel` can only set the target property.
What if we need to do something more or something different when the user changes the value?
Then we need to use the expanded form.
Let's try something silly like forcing the input value to uppercase:
// #enddocregion ngModel-6
+makeExample('template-syntax/ts/app/app.component.html', 'NgModel-4')(format=".")
// #docregion ngModel-7
:marked
Here are all variations in action, including the uppercase version:
figure.image-display
img(src='/resources/images/devguide/template-syntax/ng-model-anim.gif' alt="NgModel variations")
// #enddocregion ngModel-7
// #docregion directives-1
.l-main-section
:marked
<a name="directives"></a>
## Built-in directives
Earlier versions of Angular included over seventy built-in directives.
The community contributed many more, and countless private directives
have been created for internal applications.
We don’t need many of those directives in Angular 2.
Quite often we can achieve the same results with the more capable and expressive Angular 2 binding system.
Why create a directive to handle a click when we can write a simple binding such as this?
// #enddocregion directives-1
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".")
// #docregion directives-2
:marked
We still benefit from directives that simplify complex tasks.
Angular still ships with built-in directives; just not as many.
We'll write our own directives, just not as many.
This segment reviews some of the most frequently used built-in directives.
// #enddocregion directives-2
// #docregion directives-ngClass-1
<a id="ngClass"></a>
.l-main-section
:marked
### NgClass
We typically control how elements appear