Angular Binding
previousThis post is the notes of Official Angular Document. This amazing document can be found here.
1. Binding Syntax: an Overview
Data-binding is a mechanism for corrdinating what users see, specifically with app data values. Angular provides many kinds of data-binding. Binding types can be grouped into three categories distinguished by the direction of data flow:
- From the
soure-to-view
- From the
view-to-source
- Two-way sequence:
view-to-source-to-view
Type | Syntax | Category |
---|---|---|
Interpolation Property Attribute Class Style |
\{\{expresion}} [target]="expression" bind-target="expression" |
One-way from data source to view target |
Event | (target)="statement" on-target="statement" |
One-way from view target to data source |
Two-way | [(target)]="expression" bindon-target="expression" |
Two-way |
- Binding types other than interpolation have a target name to the left of
=
, either surrounded by punctuation,[]
,()
, or preceded by a prefix:bind-
,on-
,bindon-
. - The target of a binding is the property or event inside the binding punctuation:
[]
,()
,[()]
2. Data-binding and HTML
With data-binding, you can control things like the state of a button:
<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Save</button>
Notice that the binding is to the disabled property of the button’s DOM element, not the attribute. This applies to data-binding in general. Data-binding works with properties of DOM elements, components, and directives, not HTML attributes.
3. HTML attribute vs. DOM property
The distinction between an HTML attribute and a DOM property is key to understanding hwo Angular binding works. Attributes are defined by HTML. Properties are accessed from DOM, or the Document Object Model, nodes. When the browser loads the page, it “reads” the HTML and generates DOM objects from it. For element nodes, most standard HTML attributes automatically become properties of DOM objects. But,
- A few HTML attributes have 1:1 mapping to properties; for example,
id
. - Some HTML attributes don’t have corresponding properties; for example,
aria-*
. - Some DOM properties don’t have corresponding attributes; for example,
textContent
.
This general rule can help you build a mental model of attributes and DOM properties: attributes initialize DOM properties and then they are done. Property values can change; attribute values can’t.
The HTML attribute and the DOM property are not the same thing, even when they have the same name.
DOM properties
DOM nodes are regular JS objects. Properties are what’s in DOM objects. We can alter them. They can have any value. They are case-sensitive(e.g. elem.nodeType
, not elem.NoDeType
). DOM property values are not always strings. They can be boolean.
HTML attributes
In HTML, tags may have attributes. When the browser parses the HTML to create DOM objects for tags, it recognizes standard attributes and creates DOM properties from them. So attributes are what’s written in HTML. And HTML attributes have the following features:
- Their name is case-insensitive
- Their values are always strings.
Example 1: <input>
When the browser renders <input>
, it creates a corresponding DOM node with a value
property initialized to “Sarah”.
<input type="text" value="Sarah">
When the user enters “Sally” into the input, the DOM element value
property becomes “Sally”. However, if you look at the HTML attributes value
using input .getAttribute('value')
, you can see that the attribute remains unchanged - it returns “Sarah”.
The HTML attribute value specifies the initial value; the DOM value property is the current value.
Example 2: <button>
The disable
property of a button is false by default so the button is enabled. When adding the disable
attribute, its presence alone initializes the button’s disable
property to true so teh button is disabled.
<button disable>Test Button</button>
Adding and removing the disabled attribute disables and enables the button. However, the value of the attribute is irrelevant, which is why you cannot enable a button by writing <button disabled="false">Still Disabled</button>
To control the state of the button, set the disabled property,
Note: Though you could technically set the [attr.disabled]
attribute binding, the values are different in that the property binding requires to a boolean value, while its corresponding attribute binding relies on whether the value is null or not. That is,
<input [disabled]="condition ? true : false">
<input [attr.disabled]="condition ? 'disabled' : null">
Generally, use property binding over attribute binding as it is more intuitive (being a boolean value), has a shorter syntax, and is more performant.
Template binding works with properties and events, not attributes.
In Angular, the only role of attributes is to initialize element and directive state. When you write a data-binding, you’re dealing exclusively with properties and events of the target object.
4. Binding targets
The target of a data-binding is something in the DOM. Depending on the binding type, the target can be a property (element, component, or directive), an event (element, component or directive), or sometimes an attribute name. They can be summarized as
Type | Target | Examples |
---|---|---|
Property | Element property Component property Directive property |
src, hero and ngClass in the following: <img [src]="heroImageUrl"> <app-hero-detail [hero]="currentHero"> </app-hero-detail> <div [ngClass]="{'special': isSpecial}"> |
Event | Element event Component event Directive event |
click, deleteRequest, and myClick in the following: <button (click)="onSave()">Save</button> <app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail> <div (myClick)="clicked=$event" clickable>click me</div> |
Two-way | Event and property | <input [(ngModel)]="name"> |
Attribute | Attribute (the exception) |
<button [attr.aria-label]="help">help</button> |
Class | class property | <div [class.special]="isSpecial">Special</div> |
Style | style property | <button [style.color]="isSpecial ? 'red' : 'green'"> |
5. Property Binding [property]
Use property binding to set properties of target elements or directive @Input decorators.
One-way in
- Property binding flows a value in one direction, from a component’s property into a target element property.
- Cannot use property binding to read or pull values out of target elements.
- Cannot use property binding to call a method on the target element. If the element raises events, you can listen to them with an event binding.
Examples:
<img [scr]="itemImageUrl">
: set an element property to a component property value.<td [colSpan]="2">Span 2 columns</tr>
: bind to thecolSpan
property. Notice that it’s notcolspan
, which is the attribute.<button [disable]="isUnchanged">Disable Button</button>
: Bind buttondisabled
state toisUnchanged
property.<p [ngClass]="classes">[ngClass] binding to the classes property making this blue</p>
: set a property of directive,[ngClass]
binding to theclasses
property making this….<app-item-detail [childItem]="parentItem"></app-item-detail>
: set the model property of a custom component - a great way for parent and child components to communicate.
Binding target
An element property between []
identifies the target property. There is also the bind-
prefix alternative. That is<img [src]="itemImageUrl">
is equivalent to <img bind-src="itemImageUrl">
.
Though the target name is usually the name of a property, there is an automatic attribute-to-property mapping in Angular for several common attributes. These include class/className, innerHtml/innerHTML, and tabindex/tabIndex.
Avoid side effects
Return the proper type
The template expression should evaluate to the type of value that the target property expects. For exampe, ListItemComponent
is nested within AppComponnet
and the item
property expects an object.
In src/app/app.component.html
<app-list-item [items]="currentItem"></app-list-item>
In src/app/list-item.component.ts
@Input() items: Item[];
In scr/app/item.ts
export class Item {
id: number;
name: string;
}
So in src/app/app.component.ts
, we give value to the current Item as follows
currentItem = [{
id: 49,
name: 'phone'
}];
Remember the brackets []
If you omit the brackets, Angular treats the string as a constant and initiazlize the target property with that string. E.g.
<app-item-detail childItem="parentItem"></app-item-detail>
Omitting the []
will render the string parentItem
not the value of parentItem
.
One-time string initialization
You should omit the []
when all of the follwoing are tre:
- The target property accepts a string value.
- The string is a fixed value that you can put directly into the template.
- This initial value never changes.
Property binding vs. interpolation
You often have a choice between interpolation and property binding. The following binding pairs do the same thing:
<p><img scr=""> is the <i>interpolated</i> image.</p>
<p><img [src]="itemImageUrl"> is the <i>property bound</i> image.</p>
<p><span>"" is the <i>interpolated</i> title.</span></p>
<p><span [innerHTML]="propertyTitle"></span> is the <i>property bound</i> title.</p>
Interpolation is a convenient alternative to property binding in many cases. When rendering data values as strings, there is no technical reason to prefer one form to the other. However, when setting an element property to a non-string data value, you must use property binding.
Content security
For example, in src/app/app.componnet.ts
evilTitle = 'Template <script>alert("evil never sleeps)</script> Syntax';
Both interpolation and property binding will render the content harmlessly.
6. Attribute, class, and style bindings
Attribute binding
Usually, setting an element property with a property binding is preferable to setting the attribute with a string. However, sometimes there is no element property to bind, so attribute binding is the solution. Attribute binding syntax: between the []
, start with the prefix attr
, followed by a dot (.
), and the name of the attribute. E.g.
<!-- create and set an aria attribute for assistive technology -->
<button [attr.aria-label]="actionName"> with Aria</button>
Class binding
Add and removes CSS class names from an element’s class
attritube with a class binding. Syntax: between the []
, start with the prefix class
, optionally followed by a dot (.
) and the name of a CSS class: [class.class-name]
. For example, we can bind to a specific class name. Angular adds the class when the template expression evaluates to truthy. It removes the class when the expressio is falsy.
<h3>toggle the "special" class on/off with a property:</h3>
<div [class.special]="isSpecial">This div is special</div>
<h3>binding to class.special overrides the class attribute:</h3>
<div class="special" [class.special]="!isSPecial">This one is not so special.</div>
<h3>Using the bind- syntax:</h3>
<div bind-class.special="special">This class binding is special too.</div>
Style binding
You can set inline styles with a style binding.
Syntax: [style.style-property]
. E.g.
<button [style.color]="isSpecial ? 'red' : 'green'">Red</button>
Some style binding styles have a unit extension. The following example conditionally sets the font size in em
and %
units. E.g.
<button [style.font-size.em]="isSpecial ? 3 : 1">Big</button>
<button [style.font-size.%]="isSpecial ? 150 : 50">Small</button>
This technique is suitable for setting a single style, but consider the NgStyle
directive when setting several inline styles at the same time.
7. Event binding (event)
Event binding allows you to listen for certain events such as keystrokes, mouse movements, clicks, and touches.
Syntax: (event)="action()"
, event
is the target event name and action()
is teh template statement; alternatively, we can use on-
prefix, i.e. on-click="action()"
$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, which performs an action in response to the event, such as storing a value from the HTML control into a model.
The binding conveys information about the event. This information can include data values such as an event object, string, or number named $event
. The target event determines the shape of the $event
object. If the target event is a native DOM element event, then $event
is a DOM event object, with properties such as target and target.value. For example,
<input [value]="currentItem.name"
(input)="current.name=$event.target.value">
This code binds property and event seperately.
[value]
: property binding(input)
: event binding. To update thename
property, the changed text is retrieved by$event.target.value
.
Custom events with EventEmitter
Directives typically raise custom events with an Angular EventEmitter
. The 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, which can be anything. Parent directives listen for the event by binding to this property and accessing the payload through the $event
object. For example,
In src/app/item-detail/item-detail.component.html
we bind an click event:
<img src="" [style.display]="displayNone">
<span [style.text-decoration]="lineThrough"></span>
<button (click)="delete()">Delete</button>
In src/app/item-detail/item-detail.component.ts
:
// This component makes a request but it can't actually delete a hero.
@Output() deleteRequest = new EventEmitter<Item>();
delete() {
this.deleteRequest.emit(this.item);
this.displayNone = this.displayNone ? '' : 'none';
this.lineThrough = this.lineThrough ? '' : 'line-through';
}
The component defines a deleteRequest property that returns an EventEmitter
. When the user clicks delete, the component invokes the delete()
method, telling the EventEmitter
to emit
an Item
object.
In src/app/app.component.html
<app-item-detail
(deleteRequest)="deleteItem($event)"
[item]="currentItem">
</app-item-detail>
In src/app/app.component.ts
, we can do the customized events by editting deleteItem(item: Item)
:
deleteItem(item: Item) {
// do something;
}
Template statements have side effects
Though template expressions shouldn’t have side effects, template statements usually do. The deleteItem()
method does have a side effect: it deletes an item.
8. Two-way binding [(…)]
Two-way binding gives your app a way to share data between a component class and its template.
Basics of two-way binding
Two-way binding does two things:
- Sets a specific element property.
- Listens for an element change event.
Angular offers a special two-way data binding syntax for this purpose,[()]
. The[()]
syntax combines the brackets of property binding,[]
, with the parentheses of event binding,()
. Visually,[()]
= BANANA IN A BOX.
Two-way binding in forms
The two-way binding syntax is a great convenience compared to separate property and event bindings. It would be convenient to use two-way binding with HTML form elements like <input>
and <select>
. However, no native HTML element follows the x value and xChange event pattern.