Angular 2 Template Syntax
In the previous chapters, we have already been introduced to Angular's templates. In this article, we will specifically discuss the syntax usage of Angular.
The template plays the role of a view, simply put, it is the part displayed to the user.
- HTML
- Interpolation
- Template Expressions
- Template Statements
- Binding Syntax
- Property Binding
- HTML Attributes, Class, and Style Binding
- Event Binding
- Two-way Data Binding with NgModel
- Built-in Directives
- and <template>
- Template Reference Variables
- Input and Output Properties
- Template Expression Operators
HTML
HTML is the "language" of Angular templates. All HTML elements are supported except for the `<script>` element, which is disabled. For example:<h1>My First Angular App</h1>
Interpolation
The syntax for interpolation is: **{{ ... }}**. Interpolation can insert computed strings into HTML and can also be used as property values.<h3>
{{title}}
<img decoding="async" src="{{imageUrl}}" style="height:30px">
</h3>
Template Expressions
The **{{ ... }}** actually contains a template expression, which Angular evaluates and converts to a string output. The following example is the sum of two numbers:<!-- "The sum of 1 + 1 is 2" -->
<p>The sum of 1 + 1 is {{1 + 1}}</p>
We can use `getVal()` to get the value of this expression:
<div class="example">
<div class="example_code">
[mycode3 type="html"]
<!-- "4" -->
<p>{{1 + 1 + getVal()}}</p>
Template expressions are similar to JavaScript. Many JavaScript expressions are valid template expressions, but not all.
The following JavaScript expressions are prohibited:
- Assignment expressions (
=
,+=
,-=
...) - The
new
operator - Chained expressions with
;
or'
Increment and decrement operators (
++
and--
) Other notable differences from JavaScript syntax include:Bitwise operators (
|
and&
) are not supported- Template expression operators, such as
|
and?.
, are given new meanings
Property Binding
Template property binding sets the property of a view element to the value of a template expression. The most common form of property binding sets an element's property to the value of a property in the component. In this example, the `src` property of the `image` element is bound to the `imageUrl` property of the component:<img [src]="imageUrl">
Disable a button when the component is `isUnchanged` (unchanged):
<button [disabled]="isUnchanged">Button is disabled</button>
Set the property of a directive:
<div [ngClass]="classes">[ngClass] bound to classes property</div>
Set the property of a custom component (this is an important way for parent-child component communication):
<user-detail [user]="currentUser"></user-detail>
HTML Attribute, Class, and Style Binding
Template syntax provides specialized one-way data binding forms for scenarios where property binding is not suitable.Attribute Binding
When there is no property to bind on an element, use HTML attribute binding. Consider attributes like ARIA, SVG, and `colspan/rowspan` in tables. These are pure attributes; they do not have corresponding properties to bind to. The following example will throw an error:<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
We will get this error:
Template parse errors:
Can't bind to 'colspan' since it isn't a known native property
Template parse error: Cannot bind to 'colspan' as it is not a known native property
As the message suggests, the `<td>` element does not have a `colspan` property. However, interpolation and property binding can only set *properties*, not attributes. Therefore, HTML attribute binding is needed to create and bind to similar attributes.
HTML Attribute Binding syntax is similar to property binding, but the part inside the brackets is not an element's attribute name; instead, it consists of a prefix `attr.` followed by the name of the HTML attribute, and is set using an expression that evaluates to a string. For example:
<table border=1>
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
<tr><td>Five</td><td>Six</td></tr>
</table>
CSS Class Binding
With CSS class binding, we can add and remove CSS class names from an element's `class` attribute. CSS class binding syntax is similar to property binding. However, the part inside the brackets is not an element's attribute name; it includes a `class` prefix, followed by a dot (.), and then the CSS class name. The last two parts are optional. For example: **[class.class-name]**. The following example demonstrates how to add and remove the "special" class using CSS class binding:<!-- Standard HTML class setting -->
<div class="bad curly special">Bad curly special</div>
<!-- Resetting or overriding classes via binding -->
<div class="bad curly special" [class]="badCurly">Bad curly</div>
<!-- Adding or removing the special class via a property value -->
<div [class.special]="isSpecial">This style is special</div>
Style Binding
With style binding, you can set inline styles. Style binding syntax is similar to property binding, but the part inside the brackets is not an element's attribute name; it includes a `style.` prefix followed by the CSS property name, for example: `[style.style-property]`.<button [style.color] = "isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
<!-- Style binding with units -->
<button [style.font-size.em]="isSpecial ? 3 : 1" >Large</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
Style properties can be in kebab-case (font-size) or camelCase (fontSize).
Event Binding
In event binding, Angular listens for user actions such as keyboard events, mouse events, touch events, etc., and responds with the corresponding data flow from the view target to the data source. Event binding syntax consists of the **target event** inside parentheses on the left side of the equals sign and the **template statement** inside quotes on the right side. For example, the following event binding listens for the button's click event. Each click invokes the component's **onSave()** method.<button (click)="onSave()">Save</button>
The name inside the parentheses — such as (click) — identifies the target event. In this example, the target is the button's click event.
<button (click)="onSave()">Save</button>
You can also use the `on-` prefix:
<button on-click="onSave()">On Save</button>
Two-way Data Binding with NgModel
When developing data input forms, the desired outcome is to display the component's data in the form and update the component's data when the user makes changes. Here is an example of two-way binding using **[(NgModel)]**:<input [(ngModel)]="currentUser.firstName">
`[]` implements data flow from the component to the template, and `()` implements data flow from the template to the component. Combining both `[()]` achieves two-way binding.
Using the prefix syntax:
<input bindon-ngModel="currentUser.firstName">
Built-in Directives
Angular's built-in directives include NgClass, NgStyle, NgIf, NgFor, NgSwitch, etc.NgClass
Bind to NgClass to dynamically add or remove CSS classes.<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
This div is large.
</div>
NgStyle
Bind to NgStyle to dynamically set inline styles.setStyles() {
let styles = {
// CSS property names
'font-style': this.canSave ? 'italic' : 'normal', // italic
'font-weight': !this.isUnchanged ? 'bold' : 'normal', // normal
'font-size': this.isSpecial ? '24px' : '8px', // 24px
};
return styles;
}
By adding an NgStyle property binding and calling setStyles, we can set the styles of the element accordingly:
<div [ngStyle]="setStyles()">
This div has the styles italic, normal weight, and extra large (24px).
</div>
NgIf
By binding the NgIf directive to a truthy expression, you can add an element and its children to the DOM tree.<div *ngIf="currentUser">Hello, {{currentUser.firstName}}</div>
Conversely, binding to a falsy expression will remove the element and its children from the DOM tree. For example:
<!-- Since isActive is false, User Detail is not in the DOM tree -->
<user-detail *ngIf="isActive"></user-detail>
NgSwitch
When you need to display one element tree out of a set of possible trees based on a condition, you use NgSwitch. Angular will only add the selected element to the DOM. For example:<span [ngSwitch]="userName">
<span *ngSwitchCase="'Zhang San'">Zhang San</span>
<span *ngSwitchCase="'Li Si'">Li Si</span>
<span *ngSwitchCase="'Wang Wu'">Wang Wu</span>
<span *ngSwitchCase="'Zhao Liu'">Zhao Liu</span>
<span *ngSwitchDefault>Long Da</span>
</span>
Bind the parent directive `NgSwitch` to an expression that returns the switch value, which in this example is a string, but it can be any type of value. The parent directive `NgSwitch` controls a set of `<span>` child elements. Each `<span>` is either bound to an expression with a matching value or marked as the default case. At any time, at most one of these spans will be in the DOM. If the span's matching value equals the switch value, Angular will add that `<span>` to the DOM. If no span matches, Angular will add the default span to the DOM. Angular will remove and destroy all other spans.
Three cooperating directives:
ngSwitch: Bind to an expression that returns the switch value
ngSwitchCase: Bind to an expression that returns the matching value
ngSwitchDefault: An attribute used to mark the default element Note: Do not use
*
before ngSwitch; instead, use property binding, but*
is required before ngSwitchCase and ngSwitchDefault.
NgFor
When you need to display a list composed of multiple items, you use this directive. For example, the following applies NgFor to an HTML block:<div *ngFor="let user of users">{{user.fullName}}</div>
NgFor can also be applied to a component element, such as:
<user-detail *ngFor="let user of users" [user]="user"></user-detail>
The ngFor directive supports an optional index that increments from 0 to the length of the array during iteration.
You can capture this index with a template input variable and use it in the template. The following example captures the index into a variable named i:
<div *ngFor="let user of users; let i=index">{{i + 1}} - {{user.fullName}}</div>
NgForTrackBy
The ngFor directive can sometimes have poor performance, especially in large lists. Minor changes, removals, or additions to an item can lead to cascading DOM operations.For example, when refreshing the contact list by fetching new data from the server, the updated list may contain many (if not all) of the previously displayed contacts. However, Angular does not know which contacts were already present and ends up clearing the old list, discarding those DOM elements, and rebuilding a new list with fresh DOM elements.
To address this issue, you can use a tracking function to prevent such unnecessary operations. The tracking function informs Angular that two objects with the same `user.id` represent the same contact. For instance:
trackByUsers(index: number, user: User){return user.id}
Next, set the `NgForTrackBy` directive to this tracking function:
<div *ngFor="let user of users; trackBy:trackByUsers">({{user.id}}) {{user.fullName}}</div>
The tracking function does not eliminate all DOM changes. If the property used to determine if a contact is the same changes, it updates the DOM element; otherwise, it leaves the DOM element unchanged. This makes the list interface smoother and more responsive.
---
## Template Reference Variables
Template reference variables are references to DOM elements or directives within a template.
They can be used on native DOM elements as well as Angular components—in fact, they can work with any custom web components.
You can reference template variables on the same element, sibling elements, or any child elements.
Here are two examples of creating and using template reference variables:
<!-- phone references the input element and passes its value
to the event handler -->
<input #phone placeholder="phone number">
<button (click)="callPhone(phone.value)">Call</button>
<!-- fax references the input element and passes its value
to the event handler -->
<input ref-fax placeholder="fax number">
<button (click)="callFax(fax.value)">Fax</button>
```
The (#) prefix for "phone" indicates that we are defining a phone
variable.