Angular 2 — A quick intro about template syntax
Angular 2 templating system gives us a syntax to express the dynamic part of our HTML.
In Angular 2, a component needs to have a view. To define a view, you can define a template inline (using template) or in a separate file (using templateUrl).
Template inline:
import {Component} from 'angular2/core';
@Component({
selector: 'cars-app',
template: '<h1>Welcome</h1>'
})
export class CarsApp {}
Separate file:
import {Component} from 'angular2/core';
@Component({
selector: 'cars-app',
templateUrl: 'components/cars/cars.component.html'
})
export class CarsApp {}
To simplify things, a template helps us to render HTML with some dynamic parts depending on our data. It allows us to express data and property binding, event binding and templating concerns. To be able to express those behaviours, Angular 2 comes with its own symbols:
- {{ }} for interpolation.
- [] for property binding.
- () for event binding.
- # for variable declaration.
- * for structural directives.
Let’s see how we use each of them in our template:
1. {{ }} for interpolation.
Pay attention to the component below.
import {Component} from 'angular2/core';
@Component({
selector: 'cars-app',
template: `
<h1>BMW</h1>
<h2>Total {{ totalCars }}</h2>
`
})
export class CarsApp {
totalCars: number = 985;
}
Did you notice the double curly braces in the example above? Good! 😉
The component cars-app will be activated every time it finds a <cars-app></cars-app>. The CarsApp class has a property, totalCars and the template has an <h2> tag, using the double curly braces to indicate that an expression has to be evaluated. This behaviour is called interpolation. Angular replaces that name with the string value of the corresponding component property, so it first evaluates and then converts to a string. If you already know Angular 1.x, you are probably familiar with this template syntax.
The browser should now display:
<cars-app>
<h1>BMW</h1>
<h2>Total 985</h2>
</cars-app>
Behind the scene as we said previously, Angular 2 detects a <cars-app> element in the page, then it creates an instance of the CarsApp class and this instance is the evaluation context of the template’s expressions. After that the CarsApp instance sets the totalCars property to ‘985’.
More examples using interpolation:
<!-- "The total of 7 + 9 is 16" -->
<p>The total of 7 + 9 is {{7 + 9}}</p>
The expression can also invoke methods of the host component:
<!-- "The sum of 1 + 1 is not 4" -->
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
2. [] for property binding.
Every DOM property can be written via special attributes on HTML elements using square brackets []. An HTML attribute can start with pretty much anything.
Angular 2 maintains the properties and attributes in sync when you use them, so if you are familiar with Angular 1.x directives for example ng-hide you can work directly with the hidden property and no more ng-hide needed.
Example:
Angular 1:
<div ng-hide="isHidden">Hidden element or not?</div>
Angular 2:
<div [hidden]="isHidden">Hidden element or not?</div>
This is just one of the dozens of directives that were used in Angular 1.
Properties can also have boolean values, so for example if we have the selected attribute on the <option> tag, it will look like this:
<option [selected]="isCarSelected" value="BMW">BMW</option>
The car will be selected if isCarSelected is true, and not selected if it is false. Whenever the value of isCarSelected changes, the selected property will be updated only because, as we said above, Angular 2 maintain the properties and attributes in sync!
We can get rid of ALL of the directives we used to work on Angular 1 and start manipulating the DOM using attributes on Angular 2.
We can also access nested properties like the color attribute of the style property.
For example:
<p [style.color]=”red”>Carlos Menezes</p>
If we do not want a dynamic property we can simply write property=”value”:
<some-component name=”Carlos Menezes”></some-component>
…and you can also still write attributes like this:
<some-component name="Brand {{car.name}}"></some-component>
<some-component [name]="'Brand '+ car.name"></some-component>
3. () for event binding.
On Angular 1.x we have one directive per event: ng-click, ng-keyup, ng-mousemove, etc… In Angular 2 there are no directives to keep in mind! By using event binding we can directly listen to DOM events in our Angular 2 application.
A complete overview of DOM events can be fount at: https://developer.mozilla.org/en-US/docs/Web/Events
To implement the event binding the syntax is really simple. The general syntax is:
([Name of DOM event]) =”[Template expression]”
The $event Object:
If we want to retrieve additional event information from the element we set the binding target, we can always pass the $event object to our method.
Example:
import {Component} from 'angular2/core';@Component({
selector: 'key-up',
template: `
<div>
<input (keyup)="onKey($event)" />
<div>{{values}}</div>
</div>
`
});
export class KeyUpComponent {
onKey(event){
this.values += event.target.value + ' | '
}
}
With the $event object we get access to event details every time the keyup event is triggered.
We can also use the event to prevent the default behaviour and/or cancel propagation if we want:
onKey(event){
event.preventDefault();
event.stopPropagation();
}
Assigning an event to a button (for example) can be done as follows:
<button (click)="onClick()">Click me!</button>
We can also do cool things like handling keyboard events as:
<textarea (keydown.space)="onSpacePress()">Press space!</textarea>
Every time we press the space key, the onSpacePress() method will be called. You can also do crazy combinations like (keydown.alt.space), etc…
4. # for variable declaration.
Local variables are variables that we can dynamically declare in our template only using the # syntax.
Let’s say we would like to display the value of an input:
<input type="text" #name>
{{ name.value }}
Using the # syntax, we are creating a local variable name referencing the DOM object HTMLInputElement. The local variable can be used anywhere in the template.
One of the useful uses of local variables is when we want to execute some kind of action on another element.
<google-youtube #player></google-youtube>
<button (click)="player.play()">Play!</button>
5. * for structural directives.
Structure directives exists if we like iterating over a collection and adding an element per item. A directive in Angular 2 is close to a component, but without a template, it is used to add behaviour to an element.
The structural directives provided by Angular 2 rely on using a template element, a standard tag from the HTML specification:
<template>
<div>Cars list</div>
</template>
The browser does not display the content above, (this example is very simple and does not have too much use, it´s simply to demonstrate my point) but if we add one ‘template’ element in a view, then Angular 2 can use its content. The structural directives have the ability to do simple actions with this content, like displaying it (or not), repeating it, etc…
NgIf
NgIf is used to hide or display an element based on the evaluation of an expression. If the result of the expression returns false, the element will be removed from the resulting DOM.
<template [ngIf]=”cars.length > 0">
<div>
<h2>Cars</h2>
</div>
</template>
The framework provides a few directives, like ngIf but we can define ours if we need to. In the example above, the template will be instantiated only if cars has at least one element, that is to say if there are cars.
The syntax in the above example is a bit long, there is a shorter version (which I prefer):
<div *ngIf="cars.length > 0"><h2>Races</h2></div>
The syntax uses * to show it is a structural directive.
It is also possible to combine multiple expressions:
<div *ngIf="color1 != 'green' && color1 != 'yellow'">color1 is not green and color1 is not yellow</div>
NgFor
NgFor is a repeater directive — a way to customize data display.
The purpose of NgFor is to repeat a DOM element several times by iterating over the elements of an array. For each element the value is passed on.
The syntax is:
*ngFor=”#currentElement of myArray”
Example:
<ul>
<li *ngFor="#car of cars">{{car.name}}</li>
</ul>
The equivalent but more wordy (using template):
<ul>
<template ngFor #car [ngForOf]="cars">
<li>{{car.name}}</li>
</template>
</ul>
It is possible to declare another local variable bound to the index of the current element:
<ul>
<li *ngFor="#car of cars; #i=index">{{i}} - {{car.name}}</li>
</ul>
The local variable i will receive the index of the current element, starting at zero.
The final result of the example above would be:
<ul>
<li>0 — BMW</li>
<li>1 — Audi</li>
</ul>
There are also other exported variable that can be useful, such as:
• even, a boolean that is true if the element has an even index
• odd, a boolean that is true if the element has an odd index
• last, a boolean that is true if the element is the last of the collection
Quick example:
<ul>
<li *ngFor="#car of cars; #isEven=even" [style.color]=”isEven ? 'green' : 'black'">
{{car.name}}
</li>
<ul>
NgSwitch
This directive allows us to switch between different templates based on a condition.
NgSwitch consists of three directives:
- NgSwitch
- NgSwitchWhen
- NgSwitchDefault
By using the NgSwitch we define which expression is used for evaluation. The evaluation takes place by applying NgSwitchWhen and assigning a value in child elements:
<div [ngSwitch]="messageCount">
<p *ngSwitchWhen="0">You have no message</p>
<p *ngSwitchWhen="1">You have a message</p>
<p *ngSwitchDefault>You have some messages</p>
</div>
ngSwitch takes a condition and then *ngSwitchWhen take the possible values. *ngSwitchDefault will display if none of the values matched.
The following directives are not structural directives like the ones we have just discussed. Instead, they are standard directives.
NgStyle
We use this directive if we want to apply dynamic CSS styling based on Angular expression to HTML elements.
To apply NgStyle to a specific HTML element the directive ngStyle is used as follows:
<div [ngStyle]="{color: 'red', background-color: 'black', fontWeight: '200'}">
Text in red color on black background and I have got style!
</div>
The directive expects an object whose keys are the styles to set. The keys can either be in camelCase (fontWeight) or in dash-case (‘font-weight’)
Another way of applying dynamic styling is to use the following syntax:
<div [style.background-color]="red">
Red blackground
</div>
NgClass
The ngClass directive allows us to add or remove class dynamically on an element.
We can either set one class using property binding:
<div [class.lovely-div]="isAlovelyDiv()">I am lovely div!</div>
Or, if you want to set several at the same time, use ngClass:
<div [ngClass]="{'lovely-div': isAlovelyDiv(), 'background-div': myBackgroundDiv()}">I am lovely div!</div>
Canonical syntax:
Angular provides canonical syntax sugar for property binding, event binding and for local variables; this is mainly useful if our server side templating system is having problems with the [] or () syntax mentioned above, or if you don’t want to use [], (), *….
So….
If you want to declare a property binding, you can do:
<cars-cmp [name]="car.name"></cars-cmp>
or, using the canonical syntax:
<cars-cmp bind-name="car.name"></cars-cmp>
For event binding, you can do:
<button (click)="onClick()">Click here!</button>
or, using the canonical syntax:
<button on-click="onClick()">Click here!</button>
For two way binding, you can do:
<some-component [(twoWayProp)]="someExp"></<some-component>
or, using the canonical syntax:
<some-component bindon-twoWayProp="someExp"></<some-component>
And for local variables, you can use var-:
<video-player var-player></video-player>
<button (click)="player.pause()">Pause</button>
instead of the shorter form:
<video-player #player></video-player>
<button (click)="player.pause()">Pause</button>
Happy coding! 😃
References:
https://angular.io/docs/ts/latest/guide/template-syntax.html
http://victorsavkin.com/post/119943127151/angular-2-template-syntax