Template Syntax
HTML in templates
element is a notable exception; it is forbidden, eliminating the risk of script injection attacks. In practice,
is ignored and a warning appears in the browser console. See the Security page for details.
Some legal HTML doesn't make much sense in a template. The ,
, and
elements have no useful role. Pretty much everything else is fair game.
You can extend the HTML vocabulary of your templates with components and directives that appear as new elements and attributes. In the following sections, you'll learn how to get and set DOM (Document Object Model) values dynamically through data binding.
Begin with the first form of data binding—interpolation—to see how much richer template HTML can be.
Interpolation ( {{...}} )
You met the double-curly braces of interpolation, {{
and }}
, early in your Angular education.
My current hero is {{currentHero.name}}
You use interpolation to weave calculated strings into the text between HTML element tags and within attribute assignments.
{{title}}
src="{{heroImageUrl}}" style="height:30px">
The text 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 the example above, Angular evaluates the title
and heroImageUrl
properties and "fills in the blanks", first displaying a bold application title and then a heroic image.
More generally, the text 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:
The sum of 1 + 1 is {{1 + 1}}
The expression can invoke methods of the host component such as getVal()
, seen here:
The sum of 1 + 1 is not {{1 + 1 + getVal()}}
Angular evaluates all expressions in double curly braces, converts the expression results to strings, and links them with neighboring literal strings. Finally, it assigns this composite interpolated result to an element or directive property.
You appear to be inserting the result between element tags and assigning it to attributes. It's convenient to think so, and you rarely suffer for this mistake. Though this is not exactly true. Interpolation is a special syntax that Angular converts into a property binding, as is explained below.
But first, let's take a closer look at template expressions and statements.
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.
The interpolation braces in {{1 + 1}}
surround the template expression 1 + 1
. In the property bindingsection below, a template expression appears in quotes to the right of the =
symbol as in [property]="expression"
.
You write these 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 (
=
,+=
,-=
, ...) new
- 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, such as
|
,?.
and!
.
Expression context
The expression context is typically the component instance. In the following snippets, the title
within double-curly braces and the isUnchanged
in quotes refer to properties of the AppComponent
.
{{title}}
<span [hidden]="isUnchanged">changedspan>
An expression may also refer to properties of the template's context such as a template input variable (let hero
) or a template reference variable (#heroInput
).
#heroInput> {{heroInput.value}}
The context for terms in an expression is a blend of the template variables, the directive's context object (if it has one), and the component's members. If you reference a name that belongs to more than one of these namespaces, the template variable name takes precedence, followed by a name in the directive's context, and, lastly, the component's member names.
The previous example presents such a name collision. The component has a hero
property and the *ngFor
defines a hero
template variable. The hero
in {{hero.name}}
refers to the template input variable, not the component's property.
Template expressions cannot refer to anything in the global namespace (except undefined
). 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.
Expression guidelines
Template expressions can make or break an application. Please follow these guidelines:
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. You 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 after every change detection cycle. Change detection cycles are triggered by many asynchronous activities such as promise resolutions, http results, timer events, keypresses and mouse moves.
Expressions should finish quickly or the user experience may drag, especially on slower devices. Consider caching values when their computation is expensive.
Simplicity
Although it's possible to write quite complex template expressions, you should avoid them.
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 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.
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 an array
), it returns the same object reference when called twice in a row.
Template statements
A template statement responds to an event raised by a binding target such as an element, component, or directive. You'll see template statements in the event binding section, appearing in quotes to the right of the =
symbol as in (event)="statement"
.
A template statement has a side effect. That's the whole point of an event. It's how you update application state from user action.
Responding to events is the other side of Angular's "unidirectional data flow". You're free to change anything, anywhere, during this turn of the event loop.
Like template expressions, template statements use a language that looks like JavaScript. The template statement parser differs from the template expression parser and specifically supports both basic assignment (=
) and chaining expressions (with ;
or ,
).
However, certain JavaScript syntax is not allowed:
new
- increment and decrement operators,
++
and--
- operator assignment, such as
+=
and-=
- the bitwise operators
|
and&
- the template expression operators
Statement context
As with expressions, statements can refer only to what's in the statement context such as an event handling method of the component instance.
The statement context is typically the component instance. The deleteHero in (click)="deleteHero()"
is a method of the data-bound component.
The statement context may also refer to properties of the template's own context. In the following examples, the template $event
object, a template input variable (let hero
), and a template reference variable (#heroForm
) are passed to an event handling method of the component.
Template context names take precedence over component context names. In deleteHero(hero)
above, the hero
is the template input variable, not the component's hero
property.
Template 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
.
Statement guidelines
As with expressions, avoid writing complex template statements. A method call or simple property assignment should be the norm.
Now that you have a feel for template expressions and statements, you're ready to learn about the varieties of data binding syntax beyond interpolation.
Binding syntax: An overview
Data binding is a mechanism for coordinating what users see, with application data values. While you could push values to and pull values from HTML, the application is easier to write, read, and maintain if you turn these chores over to a binding framework. You simply declare bindings between binding sources and target HTML elements and let the framework do the work.
Angular provides many kinds of data binding. This guide covers most of them, after a high-level view of Angular data binding and its syntax.
Binding types can be grouped into three categories distinguished by the direction of data flow: from the source-to-view, from view-to-source, and in the two-way sequence: view-to-source-to-view:
Data direction | Syntax | Type |
---|---|---|
One-way from data source to view target |
| Interpolation Property Attribute Class Style |
One-way from view target to data source |
| Event |
Two-way |
| Two-way |
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-
).
The target name is the name of a property. It may look like the name of an attribute but it never is. To appreciate the difference, you must develop a new way to think about template HTML.
A new mental model
With all the power of data binding and the ability to extend the HTML vocabulary with custom markup, it is tempting to think of template HTML as HTML Plus.
It really is HTML Plus. But it's also significantly different than the HTML you're used to. It requires a new mental model.
In the normal course of HTML development, you create a visual structure with HTML elements, and you modify those elements by setting element attributes with string constants.
class="special">Mental Model
src="assets/images/hero.png">
You still create a structure and initialize attribute values this way in Angular templates.
Then you learn to create new elements with components that encapsulate HTML and drop them into templates as if they were native HTML elements.
class="special">Mental Model
That's HTML Plus.
Then you learn about data binding. The first binding you meet might look like this:
You'll get to that peculiar bracket notation in a moment. Looking beyond it, your intuition suggests that you're binding to the button's disabled
attribute and setting it to the current value of the component's isUnchanged
property.
Your intuition is incorrect! Your everyday HTML mental model is misleading. In fact, once you start data binding, you are no longer working with HTML attributes. You aren't setting attributes. You are setting the properties of DOM elements, components, and directives.
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 you might think!
That last category is confusing until you grasp 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 , 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 you discover if you 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 you 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 you cannot enable a button by writing .
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.
This fact bears repeating: Template binding works with properties and events, not attributes.
In the world of 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. HTML attributes effectively disappear.
With this model firmly in mind, read on to 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:
Type | Target | Examples |
---|---|---|
Property | Element property Component property Directive property |
|
Event | Element event Component event Directive event |
|
Two-way | Event and property |
|
Attribute | Attribute (the exception) |
|
Class | class property |
|
Style | style property |
|
With this broad view in mind, you're ready to look at binding types in detail.
Property binding ( [property] )
Write a template property binding to set a property of a view element. The binding sets the property to the value of a template expression.
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:
[src]="heroImageUrl">
Another example is disabling a button when the component says that it isUnchanged
:
Another is setting a property of a directive:
Yet another is setting the model property of a custom component (a great way for parent and child components to communicate):
[hero]="currentHero">
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.
You cannot use property binding to pull values out of the target element. You can't bind to a property of the target element to read it. You can only set it.
Similarly, you 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.
If you must read a target element property or call one of its methods, you'll need a different technique. See the API reference for ViewChild and ContentChild.
Binding target
An element property between enclosing square brackets identifies the target property. The target property in the following code is the image element's src
property.
[src]="heroImageUrl">
Some people prefer the bind-
prefix alternative, known as the canonical form:
bind-src="heroImageUrl">
The target name is always the name of a property, even when it appears to be the name of something else. You 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:
Technically, Angular is matching the name to a directive input, 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.
If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error.
Avoid side effects
As mentioned previously, evaluation of a template expression should have no visible side effects. The expression language itself does its part to keep you safe. You can't assign a value to anything in a property binding expression nor use the increment and decrement operators.
Of course, the expression might invoke a property or method that has side effects. Angular has no way of knowing that or stopping you.
The expression could call something like getFoo()
. Only you know what getFoo()
does. If getFoo()
changes something and you happen to be binding to that something, you risk an unpleasant experience. Angular may or may not display the changed value. Angular may detect the change and throw a warning error. In general, 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 you're sending in the property binding:
[hero]="currentHero">
Remember the brackets
The brackets tell Angular to evaluate the template expression. If you omit 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:
hero="currentHero">
One-time string initialization
You 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 you can bake into the template.
- This initial value never changes.
You 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 about it.
prefix="You are my" [hero]="currentHero">
The [hero]
binding, on the other hand, remains a live binding to the component's currentHero
property.
Property binding or interpolation?
You often have a choice between interpolation and property binding. The following binding pairs do the same thing:
src="{{heroImageUrl}}"> is the interpolated image.
[src]="heroImageUrl"> is the property bound image.
"{{title}}" is the interpolated title.
" [innerHTML]="title">" is the property bound title.
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. You lean toward readability, which tends to favor interpolation. You suggest establishing coding style rules and choosing the form that both conforms to the rules and feels most natural for the task at hand.
When setting an element property to a non-string data value, you must use property binding.
Content security
Imagine the following malicious content.
evilTitle = 'Template Syntax'
"{{evilTitle}}" is the interpolated
evil title.
" [innerHTML]="evilTitle">" is the property bound evil title.Attribute, class, and style bindings
Attribute binding
colspan="{{1 + 1}}">Three-Four
Template parse errors:
Can't bind to 'colspan' since it isn't a known native property
element does not have a colspan
property. It has the "colspan" attribute, but interpolation and property binding can set only properties, not attributes.attr
, followed by a dot (.
) and the name of the attribute. You then set the attribute value, using an expression that resolves to a string.[attr.colspan]
to a calculated value: border=1>
[attr.colspan]="1 + 1">One-Two
Five
One-Two | |
Five | Six |
Class binding
class
attribute with a class binding.class
, optionally followed by a dot (.
) and the name of a CSS class: [class.class-name]
.
class="bad curly special">Bad curly special
class="bad curly special"
[class]="badCurly">Bad curly
[class.special]="isSpecial">The class binding is special
class="special"
[class.special]="!isSpecial">This one is not so special
Style binding
style
, followed by a dot (.
) and the name of a CSS style property: [style.style-property]
.
Event binding ( (event) )
onSave()
method whenever a click occurs:
Target event
(click)
— identifies the target event. In the following example, the target is the button's click event.
on-
prefix alternative, known as the canonical form:
(myClick)="clickMessage=$event" clickable>click with myClick
myClick
directive is further described in the section on aliasing input/output properties.$event and event handling statements
$event
.$event
is a DOM event object, with properties such as target
and target.value
. [value]="currentHero.name"
(input)="currentHero.name=$event.target.value" >
value
property by binding to the name
property. To listen for changes to the value, the code binds 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
.name
property, the changed text is retrieved by following the path $event.target.value
.$event
has whatever shape the directive decides to produce.Custom events with EventEmitter
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.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.HeroDetailComponent
:template: `
{{prefix}} {{hero?.name}}
`
// This component makes a request but it can't actually delete a hero.
deleteRequest = new EventEmitter<Hero>();
delete() {
this.deleteRequest.emit(this.hero);
}
deleteRequest
property that returns an EventEmitter
. When the user clicks delete, the component invokes the delete()
method, telling the EventEmitter
to emit a Hero
object.HeroDetailComponent
's deleteRequest
event. (deleteRequest)="deleteHero($event)" [hero]="currentHero">
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
deleteHero
method has a side effect: it deletes a hero. Template statement side effects are not just OK, but expected.Two-way binding ( [(...)] )
[(x)]
. The [(x)]
syntax combines the brackets of property binding, [x]
, with the parentheses of event binding, (x)
.[(x)]
syntax is easy to demonstrate when the element has a settable property called x
and a corresponding event named xChange
. Here's a SizerComponent
that fits the pattern. It has a size
value property and a companion sizeChange
event:
- import { Component, EventEmitter, Input, Output } from '@angular/core';
-
- @Component({
- selector: 'app-sizer',
- template: `
-
-
-
-
-
`
size
is an input value from a property binding. Clicking the buttons increases or decreases the size
, within min/max values constraints, and then raises (emits) the sizeChange
event with the adjusted size.AppComponent.fontSizePx
is two-way bound to the SizerComponent
: [(size)]="fontSizePx">
[style.font-size.px]="fontSizePx">Resizable Text
AppComponent.fontSizePx
establishes the initial SizerComponent.size
value. Clicking the buttons updates the AppComponent.fontSizePx
via the two-way binding. The revised AppComponent.fontSizePx
value flows through to the style binding, making the displayed text bigger or smaller.SizerComponent
binding into this: [size]="fontSizePx" (sizeChange)="fontSizePx=$event">
$event
variable contains the payload of the SizerComponent.sizeChange
event. Angular assigns the $event
value to the AppComponent.fontSizePx
when the user clicks the buttons.
and
. However, no native HTML element follows the x
value and xChange
event pattern.Built-in directives
Built-in attribute directives
RouterModule
and the FormsModule
define their own attribute directives. This section is an introduction to the most commonly used attribute directives:NgClass
- add and remove a set of CSS classesNgStyle
- add and remove a set of HTML stylesNgModel
- two-way data binding to an HTML form element
NgClass
ngClass
to add or remove several classes simultaneously.
[class.special]="isSpecial">The class binding is special
NgClass
directive may be the better choice.ngClass
to a key:value control object. Each key of the object is a CSS class name; its value is true
if the class should be added, false
if it should be removed.setCurrentClasses
component method that sets a component property, currentClasses
with an object that adds or removes three classes based on the true
/false
state of three other component properties:currentClasses: {};
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
'saveable': this.canSave,
'modified': !this.isUnchanged,
'special': this.isSpecial
};
}
ngClass
property binding to currentClasses
sets the element's classes accordingly:
setCurrentClasses()
, both initially and when the dependent properties change.NgStyle
NgStyle
you can set many inline styles simultaneously.
[style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
This div is x-large or smaller.
NgStyle
directive may be the better choice.ngStyle
to a key:value control object. Each key of the object is a style name; its value is whatever is appropriate for that style.setCurrentStyles
component method that sets a component property, currentStyles
with an object that defines three styles, based on the state of three other component properties:currentStyles: {};
setCurrentStyles() {
// CSS styles: set per current state of component properties
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px'
};
}
ngStyle
property binding to currentStyles
sets the element's styles accordingly:
setCurrentStyles()
, both initially and when the dependent properties change.NgModel - Two-way binding to form elements with [(ngModel)]
NgModel
directive makes that easy. Here's an example: [(ngModel)]="currentHero.name">
FormsModule is required to use ngModel
ngModel
directive in a two-way data binding, you must import the FormsModule
and add it to the NgModule's imports
list. Learn more about the FormsModule
and ngModel
in the Forms guide.FormsModule
to make [(ngModel)]
available.import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // <--- angular="" from="" import="" javascript="" span="">
/* Other imports */
@NgModule({
imports: [
BrowserModule,
FormsModule // <--- import="" into="" span="" the="">NgModule
],
/* Other module metadata */
})
export class AppModule { }--->--->
Inside [(ngModel)]
name
binding, note that you could have achieved the same result with separate bindings to the
element's value
property and input
event. [value]="currentHero.name"
(input)="currentHero.name=$event.target.value" >
ngModel
directive hides these onerous details behind its own ngModel
input and ngModelChange
output properties.
[ngModel]="currentHero.name"
(ngModelChange)="currentHero.name=$event">
ngModel
data property sets the element's value property and the ngModelChange
event property listens for changes to the element's value.NgModel
directive only works for an element supported by a ControlValueAccessor that adapts an element to this protocol. The
box is one of those elements. Angular provides value accessors for all of the basic HTML form elements and the Forms guide shows how to bind to them.[(ngModel)]
to a non-form native element or a third-party custom component until you write a suitable value accessor, a technique that is beyond the scope of this guide.NgModel
altogether. The sizer
shown above is an example of this technique.ngModel
bindings is an improvement over binding to the element's native properties. You can do better.[(ngModel)]
syntax: [(ngModel)]="currentHero.name">
[(ngModel)]
all you need? Is there ever a reason to fall back to its expanded form?[(ngModel)]
syntax can only set a data-bound property. If you need to do something more or something different, you can write the expanded form.
[ngModel]="currentHero.name"
(ngModelChange)="setUppercaseName($event)">
Built-in structural directives
- why you prefix the directive name with an asterisk (*).
- to use
to group elements when there is no suitable host element for the directive. - how to write your own structural directive.
- that you can only apply one structural directive to an element.
NgIf
- conditionally add or remove an element from the DOMNgSwitch
- a set of directives that switch among alternative views- NgForOf - repeat a template for each item in a list
NgIf
NgIf
directive to that element (called the host element). Bind the directive to a condition expression like isActive
in this example. *ngIf="isActive">
*
) in front of ngIf
.isActive
expression returns a truthy value, NgIf
adds the HeroDetailComponent
to the DOM. When the expression is falsy, NgIf
removes the HeroDetailComponent
from the DOM, destroying that component and all of its sub-components.Show/hide is not the same thing
[class.hidden]="!isSpecial">Show with class
[class.hidden]="isSpecial">Hide with class
[class.hidden]="isSpecial">
NgIf
.NgIf
is false
, Angular removes the element and its descendents from the DOM. It destroys their components, potentially freeing up substantial resources, resulting in a more responsive user experience.NgIf
may be the safer choice.Guard against null
ngIf
directive is often used to guard against null. Show/hide is useless as a guard. Angular will throw an error if a nested expression tries to access a property of null
.NgIf
guarding two
s. The currentHero
name will appear only when there is a currentHero
. The nullHero
will never be displayed.
NgForOf
NgForOf
is a repeater directive — a way to present a list of items. You define a block of HTML that defines how a single item should be displayed. You tell Angular to use that block as a template for rendering each item in the list.NgForOf
applied to a simple
:
NgForOf
to a component element, as in this example: *ngFor="let hero of heroes" [hero]="hero">
*
) in front of ngFor
.*ngFor
is the instruction that guides the repeater process.*ngFor microsyntax
*ngFor
is not a template expression. It's a microsyntax — a little language of its own that Angular interprets. The string "let hero of heroes"
means:Take each hero in theheroes
array, store it in the localhero
looping variable, and make it available to the templated HTML for each iteration.
around the host element, then uses this template repeatedly to create a new set of elements and bindings for each hero
in the list.Template input variables
let
keyword before hero
creates a template input variable called hero
. The NgForOf
directive iterates over the heroes
array returned by the parent component's heroes
property and sets hero
to the current item from the array during each iteration.hero
input variable within the NgForOf
host element (and within its descendants) to access the hero's properties. Here it is referenced first in an interpolation and then passed in a binding to the hero
property of the
component.
*ngFor="let hero of heroes" [hero]="hero">
*ngFor with index
index
property of the NgForOf
directive context returns the zero-based index of the item in each iteration. You can capture the index
in a template input variable and use it in the template.index
in a variable named i
and displays it with the hero name like this.
NgFor
is implemented by the NgForOf
directive. Read more about the other NgForOf
context values such as last
, even
, and odd
in the NgForOf API reference.*ngFor with trackBy
NgForOf
directive may perform poorly, especially with large lists. A small change to one item, an item removed, or an item added can trigger a cascade of DOM manipulations.id
of each hero hasn't changed. But Angular sees only a fresh list of new object references. It has no choice but to tear down the old DOM elements and insert all new DOM elements.trackBy
. Add a method to the component that returns the value NgForOf
should track. In this case, that value is the hero's id
.trackByHeroes(index: number, hero: Hero): number { return hero.id; }
trackBy
to this method.
hero.id
s. "Change ids" creates new heroes with new hero.id
s.- With no
trackBy
, both buttons trigger complete DOM element replacement. - With
trackBy
, only changing theid
triggers element replacement.
The NgSwitch directives
switch
statement. It can display one element from among several possible elements, based on a switch condition. Angular puts only the selected element into the DOM.NgSwitch
, NgSwitchCase
, and NgSwitchDefault
as seen in this example.
[ngSwitch]="currentHero.emotion">
*ngSwitchCase="'happy'" [hero]="currentHero">
*ngSwitchCase="'sad'" [hero]="currentHero">
*ngSwitchCase="'confused'" [hero]="currentHero">
*ngSwitchDefault [hero]="currentHero">
NgSwitch
is the controller directive. Bind it to an expression that returns the switch value. The emotion
value in this example is a string, but the switch value can be of any type.[ngSwitch]
. You'll get an error if you try to set *ngSwitch
because NgSwitch
is an attributedirective, not a structural directive. It changes the behavior of its companion directives. It doesn't touch the DOM directly.*ngSwitchCase
and *ngSwitchDefault
. The NgSwitchCase
and NgSwitchDefault
directives are structural directives because they add or remove elements from the DOM.NgSwitchCase
adds its element to the DOM when its bound value equals the switch value.NgSwitchDefault
adds its element to the DOM when there is no selectedNgSwitchCase
.
hero-switch.components.ts
file. Each component has a hero
input property which is bound to the currentHero
of the parent component.
switch case with the following.
Template reference variables ( #var )
#phone
declares a phone
variable on an
element. #phone placeholder="phone number">
phone
variable declared on this
is consumed in a
on the other side of the template #phone placeholder="phone number">
How a reference variable gets its value
phone
refers to the phone number
box. The phone button click handler passes the input value to the component's callPhone
method. But a directive can change that behavior and set the value to something else, such as itself. The NgForm
directive does that.
[hidden]="!heroForm.form.valid">
{{submitMessage}}
heroForm
, appears three times in this example, separated by a large amount of HTML. What is the value of heroForm
?FormsModule
, it would be the HTMLFormElement. The heroForm
is actually a reference to an Angular NgForm directive with the ability to track the value and validity of every control in the form.
element doesn't have a form
property. But the NgForm
directive does, which explains how you can disable the submit button if the heroForm.form.valid
is invalid and pass the entire form control tree to the parent component's onSubmit
method.Template reference variable warning notes
#phone
) is not the same as a template input variable (let phone
) such as you might see in an *ngFor
. Learn the difference in the Structural Directives guide.ref-
prefix alternative to #
. This example declares the fax
variable as ref-fax
instead of #fax
. ref-fax placeholder="fax number">
Input and Output properties
@Input
decorator. Values flow into the property when it is data bound with a property binding@Output
decorator. The property almost always returns an Angular EventEmitter
. Values flow out of the component as events bound with an event binding.Discussion
=
). [src]="iconUrl"/>
iconUrl
and onSave
are members of the AppComponent
class. They are not decorated with @Input()
or @Output
. Angular does not object.Binding to a different component
=
).AppComponent
template binds AppComponent
class members to properties of the HeroDetailComponent
whose selector is 'app-hero-detail'
. [hero]="currentHero" (deleteRequest)="deleteHero($event)">
Uncaught Error: Template parse errors:
Can't bind to 'hero' since it isn't a known property of 'app-hero-detail'
HeroDetailComponent
has hero
and deleteRequest
properties. But the Angular compiler refuses to recognize them.TypeScript public doesn't matter
@Input()
and @Output()
decorators.Declaring Input and Output properties
HeroDetailComponent
do not fail because the data bound properties are annotated with @Input()
and @Output()
decorators.@Input() hero: Hero;
@Output() deleteRequest = new EventEmitter<Hero>();
inputs
and outputs
arrays of the directive metadata, as in this example:@Component({
inputs: ['hero'],
outputs: ['deleteRequest'],
})
Input or output?
EventEmitter
objects.HeroDetailComponent.hero
is an input property from the perspective of HeroDetailComponent
because data flows into that property from a template binding expression.HeroDetailComponent.deleteRequest
is an output property from the perspective of HeroDetailComponent
because events stream out of that property and toward the handler in a template binding statement.Aliasing input/output properties
myClick
selector to a
tag, you expect to bind to an event property that is also called myClick
.
(myClick)="clickMessage=$event" clickable>click with myClick
myClick
directive name is not a good name for a property that emits click messages.myClick
alias to the directive's own clicks
property.@Output('myClick') clicks = new EventEmitter (); // @Output(alias) propertyName = ...
inputs
and outputs
arrays. You write a colon-delimited (:
) string with the directive property name on the left and the public alias on the right:@Directive({
outputs: ['clicks:myClick'] // propertyName:alias
})
Template expression operators
The pipe operator ( | )
|
):
Title through uppercase pipe: {{title | uppercase}}
Title through a pipe chain:
{{title | uppercase | lowercase}}
Birthdate: {{currentHero?.birthdate | date:'longDate'}}
json
pipe is particularly helpful for debugging bindings:
{{currentHero | json}}
{ "id": 0, "name": "Hercules", "emotion": "happy",
"birthdate": "1970-02-25T08:00:00.000Z",
"url": "http://www.imdb.com/title/tt0065832/",
"rate": 325 }
The safe navigation operator ( ?. ) and null property paths
?.
) is a fluent and convenient way to guard against null and undefined values in property paths. Here it is, protecting against a view render failure if the currentHero
is null.The current hero's name is {{currentHero?.name}}
title
property is null?The title is {{title}}
name
of a null hero.The null hero's name is {{nullHero.name}}
TypeError: Cannot read property 'name' of null in [null].
hero
property could never be null. If it must never be null and yet it is null, that's a programming error that should be caught and fixed. Throwing an exception is the right thing to do.title
property does.currentHero
is null.
&&
, knowing that the expression bails out when it encounters the first null.The null hero's name is {{nullHero && nullHero.name}}
a.b.c.d
.?.
) is a more fluent and convenient way to guard against nulls in property paths. The expression bails out when it hits the first null value. The display is blank, but the app keeps rolling without errors.
The null hero's name is {{nullHero?.name}}
a?.b?.c?.d
.The non-null assertion operator ( ! )
--strictNullChecks
flag. TypeScript then ensures that no variable is unintentionally null or undefined.!
) serves the same purpose in an Angular template.hero
is defined, you can assert that hero
properties are also defined.
hero.name
might be null or undefined.
The $any
type cast function ($any( )
)
$any
cast function to cast the expression to the any
type.
The hero's marker is {{$any(hero).marker}}
marker
is not a member of the Hero
interface.$any
cast function can be used in conjunction with this
to allow access to undeclared members of the component.
Undeclared members is {{$any(this).member}}
$any
cast function can be used anywhere in a binding expression where a method call is valid.
Comments
Post a Comment