docs: re-write interpolation section and add example (#25170)
PR Close #25170
This commit is contained in:
parent
7bafe180fb
commit
d2eea769f6
47
aio/content/examples/interpolation/e2e/src/app.e2e-spec.ts
Normal file
47
aio/content/examples/interpolation/e2e/src/app.e2e-spec.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
|
describe('Interpolation e2e tests', () => {
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
browser.get('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display Interpolation and Template Expressions', function () {
|
||||||
|
expect(element(by.css('h1')).getText()).toEqual('Interpolation and Template Expressions');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display Current customer: Maria', function () {
|
||||||
|
expect(element.all(by.css('h3')).get(0).getText()).toBe(`Current customer: Maria`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display The sum of 1 + 1 is not 4.', function () {
|
||||||
|
expect(element.all(by.css('p:last-child')).get(0).getText()).toBe(`The sum of 1 + 1 is not 4.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display Expression Context', function () {
|
||||||
|
expect(element.all(by.css('h2')).get(1).getText()).toBe(`Expression Context`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display a list of customers', function () {
|
||||||
|
expect(element.all(by.css('li')).get(0).getText()).toBe(`Maria`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display two pictures', function() {
|
||||||
|
let pottedPlant = element.all(by.css('img')).get(0);
|
||||||
|
let lamp = element.all(by.css('img')).get(1);
|
||||||
|
|
||||||
|
expect(pottedPlant.getAttribute('src')).toContain('pottedPlant');
|
||||||
|
expect(pottedPlant.isDisplayed()).toBe(true);
|
||||||
|
|
||||||
|
expect(lamp.getAttribute('src')).toContain('lamp');
|
||||||
|
expect(lamp.isDisplayed()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support user input', function () {
|
||||||
|
let input = element(by.css('input'));
|
||||||
|
let label = element(by.css('label'));
|
||||||
|
expect(label.getText()).toEqual('Type something:');
|
||||||
|
input.sendKeys('abc');
|
||||||
|
expect(label.getText()).toEqual('Type something: abc');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
<div>
|
||||||
|
<h1>Interpolation and Template Expressions</h1>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Interpolation</h2>
|
||||||
|
<!-- #docregion interpolation-example1 -->
|
||||||
|
<h3>Current customer: {{ currentCustomer }}</h3>
|
||||||
|
<!-- #enddocregion interpolation-example1 -->
|
||||||
|
|
||||||
|
<!-- #docregion component-property -->
|
||||||
|
<p>{{title}}</p>
|
||||||
|
<div><img src="{{itemImageUrl}}"></div>
|
||||||
|
<!-- #enddocregion component-property -->
|
||||||
|
|
||||||
|
<h3>Evaluating template expressions </h3>
|
||||||
|
<h4>Simple evaluation (to a string):</h4>
|
||||||
|
<!-- #docregion convert-string -->
|
||||||
|
<!-- "The sum of 1 + 1 is 2" -->
|
||||||
|
<p>The sum of 1 + 1 is {{1 + 1}}.</p>
|
||||||
|
<!-- #enddocregion convert-string -->
|
||||||
|
|
||||||
|
<h4>Evaluates using a method (also evaluates to a string):</h4>
|
||||||
|
<!-- #docregion invoke-method -->
|
||||||
|
<!-- "The sum of 1 + 1 is not 4" -->
|
||||||
|
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}.</p>
|
||||||
|
<!-- #enddocregion invoke-method -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<h2>Expression Context</h2>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Component context, properties of app.component.ts:</h3>
|
||||||
|
<!-- #docregion component-context -->
|
||||||
|
<h4>{{recommended}}</h4>
|
||||||
|
<img [src]="itemImageUrl2">
|
||||||
|
<!-- #enddocregion component-context -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4>Template context, template input variables (let customer):</h4>
|
||||||
|
<!-- #docregion template-input-variable -->
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let customer of customers">{{customer.name}}</li>
|
||||||
|
</ul>
|
||||||
|
<!-- #enddocregion template-input-variable -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div (keyup)="0">
|
||||||
|
<h4>Template context: template reference variables (#customerInput):</h4>
|
||||||
|
<label>Type something:
|
||||||
|
<!-- #docregion template-reference-variable -->
|
||||||
|
<input #customerInput>{{customerInput.value}}</label>
|
||||||
|
<!-- #enddocregion template-reference-variable -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,27 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
it(`should have as title 'Featured product:'`, async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('Featured product:');
|
||||||
|
}));
|
||||||
|
it('should render title in a p tag', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('p').textContent).toContain('Featured product:');
|
||||||
|
}));
|
||||||
|
});
|
25
aio/content/examples/interpolation/src/app/app.component.ts
Normal file
25
aio/content/examples/interpolation/src/app/app.component.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import { CUSTOMERS } from './customers';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
customers = CUSTOMERS;
|
||||||
|
|
||||||
|
currentCustomer = 'Maria';
|
||||||
|
title = 'Featured product:';
|
||||||
|
itemImageUrl = '../assets/pottedPlant.png';
|
||||||
|
|
||||||
|
recommended = 'You might also like:';
|
||||||
|
itemImageUrl2 = '../assets/lamp.png';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getVal(): number { return 2; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
18
aio/content/examples/interpolation/src/app/app.module.ts
Normal file
18
aio/content/examples/interpolation/src/app/app.module.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
BrowserModule
|
||||||
|
],
|
||||||
|
providers: [],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
3
aio/content/examples/interpolation/src/app/customer.ts
Normal file
3
aio/content/examples/interpolation/src/app/customer.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export class Customer {
|
||||||
|
name: string;
|
||||||
|
}
|
9
aio/content/examples/interpolation/src/app/customers.ts
Normal file
9
aio/content/examples/interpolation/src/app/customers.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Customer } from './customer';
|
||||||
|
|
||||||
|
export const CUSTOMERS: Customer[] = [
|
||||||
|
{ name: 'Maria' },
|
||||||
|
{ name: 'Oliver' },
|
||||||
|
{ name: 'Walter' },
|
||||||
|
{ name: 'Lakshmi' },
|
||||||
|
{ name: 'Yasha' }
|
||||||
|
];
|
BIN
aio/content/examples/interpolation/src/assets/lamp.png
Normal file
BIN
aio/content/examples/interpolation/src/assets/lamp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
BIN
aio/content/examples/interpolation/src/assets/potted-plant.png
Normal file
BIN
aio/content/examples/interpolation/src/assets/potted-plant.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
14
aio/content/examples/interpolation/src/index.html
Normal file
14
aio/content/examples/interpolation/src/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Interpolation</title>
|
||||||
|
<base href="/">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
aio/content/examples/interpolation/src/main.ts
Normal file
12
aio/content/examples/interpolation/src/main.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { enableProdMode } from '@angular/core';
|
||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
import { AppModule } from './app/app.module';
|
||||||
|
import { environment } from './environments/environment';
|
||||||
|
|
||||||
|
if (environment.production) {
|
||||||
|
enableProdMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
|
.catch(err => console.log(err));
|
10
aio/content/examples/interpolation/stackblitz.json
Normal file
10
aio/content/examples/interpolation/stackblitz.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"description": "Interpolation",
|
||||||
|
"files": [
|
||||||
|
"!**/*.d.ts",
|
||||||
|
"!**/*.js",
|
||||||
|
"!**/*.[1,2].*"
|
||||||
|
],
|
||||||
|
"file": "src/app/app.component.ts",
|
||||||
|
"tags": ["interpolation"]
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
itemImageUrl = '../assets/lamp.png';
|
||||||
|
isUnchanged = true;
|
||||||
|
classes = 'special';
|
||||||
|
// #docregion parent-data-type
|
||||||
|
parentItem = 'bananas';
|
||||||
|
// #enddocregion parent-data-type
|
||||||
|
|
||||||
|
// #docregion pass-object
|
||||||
|
currentItem = [{
|
||||||
|
id: 21,
|
||||||
|
name: 'peaches'
|
||||||
|
}];
|
||||||
|
// #enddocregion pass-object
|
||||||
|
|
||||||
|
interpolationTitle = 'Interpolation';
|
||||||
|
propertyTitle = 'Property binding';
|
||||||
|
|
||||||
|
// #docregion malicious-content
|
||||||
|
evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
|
||||||
|
// #enddocregion malicious-content
|
||||||
|
}
|
@ -42,31 +42,46 @@ Begin with the first form of data binding—interpolation—to see how m
|
|||||||
|
|
||||||
{@a interpolation}
|
{@a interpolation}
|
||||||
|
|
||||||
## Interpolation ( <span class="syntax">{{...}}</span> )
|
## Interpolation and Template Expressions
|
||||||
|
|
||||||
You met the double-curly braces of interpolation, `{{` and `}}`, early in your Angular education.
|
Interpolation allows you to incorporate calculated strings into the text
|
||||||
|
between HTML element tags and within attribute assignments. Template
|
||||||
|
expressions are what you use to calculate those strings.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="first-interpolation" header="src/app/app.component.html" linenums="false">
|
The interpolation <live-example></live-example> demonstrates all of
|
||||||
|
the syntax and code snippets described in this section.
|
||||||
|
|
||||||
|
### Interpolation `{{...}}`
|
||||||
|
|
||||||
|
Interpolation refers to embedding expressions into marked up text.
|
||||||
|
By default, interpolation uses as its delimiter the double curly braces, `{{` and `}}`.
|
||||||
|
|
||||||
|
In the following snippet, `{{ currentCustomer }}` is an example of interpolation.
|
||||||
|
|
||||||
|
<code-example path="interpolation/src/app/app.component.html" region="interpolation-example1" header="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
You use interpolation to weave calculated strings into the text between HTML element tags and within attribute assignments.
|
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.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="title+image" header="src/app/app.component.html" linenums="false">
|
<code-example path="interpolation/src/app/app.component.html" region="component-property" header="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
The text between the braces is often the name of a component property. Angular replaces that name with the
|
In the example above, Angular evaluates the `title` and `itemImageUrl` properties
|
||||||
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 some title text and then an image.
|
||||||
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**
|
More generally, the text between the braces is a **template expression**
|
||||||
and then **converts to a string**. The following interpolation illustrates the point by adding the two numbers:
|
that Angular first **evaluates** and then **converts to a string**.
|
||||||
|
The following interpolation illustrates the point by adding two numbers:
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="sum-1" header="src/app/app.component.html" linenums="false">
|
<code-example path="interpolation/src/app/app.component.html" region="convert-string" header="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
The expression can invoke methods of the host component such as `getVal()`, seen here:
|
The expression can invoke methods of the host component such as `getVal()` in
|
||||||
|
the following example:
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="sum-2" header="src/app/app.component.html" linenums="false">
|
<code-example path="interpolation/src/app/app.component.html" region="invoke-method" header="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
Angular evaluates all expressions in double curly braces,
|
Angular evaluates all expressions in double curly braces,
|
||||||
@ -74,60 +89,67 @@ converts the expression results to strings, and links them with neighboring lite
|
|||||||
it assigns this composite interpolated result to an **element or directive property**.
|
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.
|
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](guide/template-syntax#property-binding), as is explained [below](guide/template-syntax#property-binding-or-interpolation).
|
|
||||||
|
|
||||||
But first, let's take a closer look at template expressions and statements.
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
However, interpolation is a special syntax that Angular converts into a
|
||||||
|
property binding.
|
||||||
|
|
||||||
|
If you'd like to use something other than `{{` and `}}`, you can
|
||||||
|
configure the interpolation delimiter via the
|
||||||
|
[interpolation](api/core/Component#interpolation)
|
||||||
|
option in the `Component` metadata.
|
||||||
|
|
||||||
<hr/>
|
</div>
|
||||||
|
|
||||||
{@a template-expressions}
|
### Template expressions
|
||||||
|
|
||||||
## Template expressions
|
A template **expression** produces a value and appears within the double
|
||||||
|
curly braces, `{{ }}`.
|
||||||
A template **expression** produces a value.
|
|
||||||
Angular executes the expression and assigns it to a property of a binding target;
|
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 target could be an HTML element, a component, or a directive.
|
||||||
|
|
||||||
The interpolation braces in `{{1 + 1}}` surround the template expression `1 + 1`.
|
The interpolation braces in `{{1 + 1}}` surround the template expression `1 + 1`.
|
||||||
In the [property binding](guide/template-syntax#property-binding) section below,
|
In the property binding,
|
||||||
a template expression appears in quotes to the right of the `=` symbol as in `[property]="expression"`.
|
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.
|
In terms of syntax, template expressions are similar to JavaScript.
|
||||||
Many JavaScript expressions are legal template expressions, but not all.
|
Many JavaScript expressions are legal template expressions, with a few exceptions.
|
||||||
|
|
||||||
JavaScript expressions that have or promote side effects are prohibited,
|
You can't use JavaScript expressions that have or promote side effects,
|
||||||
including:
|
including:
|
||||||
|
|
||||||
* assignments (`=`, `+=`, `-=`, ...)
|
* Assignments (`=`, `+=`, `-=`, `...`)
|
||||||
* <code>new</code>
|
* Operators such as `new`, `typeof`, `instanceof`, etc.
|
||||||
* chaining expressions with <code>;</code> or <code>,</code>
|
* Chaining expressions with <code>;</code> or <code>,</code>
|
||||||
* increment and decrement operators (`++` and `--`)
|
* The increment and decrement operators `++` and `--`
|
||||||
|
* Some of the ES2015+ operators
|
||||||
|
|
||||||
Other notable differences from JavaScript syntax include:
|
Other notable differences from JavaScript syntax include:
|
||||||
|
|
||||||
* no support for the bitwise operators `|` and `&`
|
* No support for the bitwise operators such as `|` and `&`
|
||||||
* new [template expression operators](guide/template-syntax#expression-operators), such as `|`, `?.` and `!`.
|
* New template expression operators, such as `|`, `?.` and `!`
|
||||||
|
<!-- link to: guide/template-syntax#expression-operators -->
|
||||||
{@a expression-context}
|
|
||||||
|
|
||||||
### Expression context
|
### Expression context
|
||||||
|
|
||||||
The *expression context* is typically the _component_ instance.
|
The *expression context* is typically the _component_ instance.
|
||||||
In the following snippets, the `title` within double-curly braces and the
|
In the following snippets, the `recommended` within double curly braces and the
|
||||||
`isUnchanged` in quotes refer to properties of the `AppComponent`.
|
`itemImageUrl2` in quotes refer to properties of the `AppComponent`.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="context-component-expression" header="src/app/app.component.html" linenums="false">
|
<code-example path="interpolation/src/app/app.component.html" region="component-context" header="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
An expression may also refer to properties of the _template's_ context
|
An expression may also refer to properties of the _template's_ context
|
||||||
such as a [template input variable](guide/template-syntax#template-input-variable) (`let hero`)
|
such as a template input variable,
|
||||||
or a [template reference variable](guide/template-syntax#ref-vars) (`#heroInput`).
|
<!-- link to built-in-directives#template-input-variables -->
|
||||||
|
`let customer`, or a template reference variable, `#customerInput`.
|
||||||
|
<!-- link to guide/template-ref-variables -->
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="context-var" header="src/app/app.component.html" linenums="false">
|
<code-example path="interpolation/src/app/app.component.html" region="template-input-variable" header="src/app/app.component.html (template input variable)" linenums="false">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
<code-example path="interpolation/src/app/app.component.html" region="template-reference-variable" header="src/app/app.component.html (template reference variable)" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
The context for terms in an expression is a blend of the _template variables_,
|
The context for terms in an expression is a blend of the _template variables_,
|
||||||
@ -136,34 +158,32 @@ 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_,
|
the template variable name takes precedence, followed by a name in the directive's _context_,
|
||||||
and, lastly, the component's member names.
|
and, lastly, the component's member names.
|
||||||
|
|
||||||
The previous example presents such a name collision. The component has a `hero`
|
The previous example presents such a name collision. The component has a `customer`
|
||||||
property and the `*ngFor` defines a `hero` template variable.
|
property and the `*ngFor` defines a `customer` template variable.
|
||||||
The `hero` in `{{hero.name}}`
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
The `customer` in `{{customer.name}}`
|
||||||
refers to the template input variable, not the component's property.
|
refers to the template input variable, not the component's property.
|
||||||
|
|
||||||
Template expressions cannot refer to anything in
|
Template expressions cannot refer to anything in
|
||||||
the global namespace (except `undefined`). They can't refer to `window` or `document`. They
|
the global namespace, except `undefined`. They can't refer to
|
||||||
can't call `console.log` or `Math.max`. They are restricted to referencing
|
`window` or `document`. Additionally, they
|
||||||
|
can't call `console.log()` or `Math.max()` and they are restricted to referencing
|
||||||
members of the expression context.
|
members of the expression context.
|
||||||
|
|
||||||
|
</div>
|
||||||
{@a no-side-effects}
|
|
||||||
|
|
||||||
{@a expression-guidelines}
|
|
||||||
|
|
||||||
### Expression guidelines
|
### Expression guidelines
|
||||||
|
|
||||||
Template expressions can make or break an application.
|
When using template expressions follow these guidelines:
|
||||||
Please follow these guidelines:
|
|
||||||
|
|
||||||
* [No visible side effects](guide/template-syntax#no-visible-side-effects)
|
* [No visible side effects](guide/template-syntax#no-visible-side-effects)
|
||||||
* [Quick execution](guide/template-syntax#quick-execution)
|
* [Quick execution](guide/template-syntax#quick-execution)
|
||||||
* [Simplicity](guide/template-syntax#simplicity)
|
* [Simplicity](guide/template-syntax#simplicity)
|
||||||
* [Idempotence](guide/template-syntax#idempotence)
|
|
||||||
|
|
||||||
The only exceptions to these guidelines should be in specific circumstances that you thoroughly understand.
|
|
||||||
|
|
||||||
#### No visible side effects
|
### No visible side effects
|
||||||
|
|
||||||
A template expression should not change any application state other than the value of the
|
A template expression should not change any application state other than the value of the
|
||||||
target property.
|
target property.
|
||||||
@ -172,37 +192,43 @@ 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.
|
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.
|
The view should be stable throughout a single rendering pass.
|
||||||
|
|
||||||
#### Quick execution
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
There is one exception to this behavior that applies to `*ngFor`. `*ngFor` has `trackBy` functionality that can deal with referential inequality of objects that when iterating over them.
|
||||||
|
|
||||||
|
For more information, see the [*ngFor with `trackBy`](guide/template-syntax#ngfor-with-trackby) section of this guide.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
### Quick execution
|
||||||
|
|
||||||
Angular executes template expressions after every change detection cycle.
|
Angular executes template expressions after every change detection cycle.
|
||||||
Change detection cycles are triggered by many asynchronous activities such as
|
Change detection cycles are triggered by many asynchronous activities such as
|
||||||
promise resolutions, http results, timer events, keypresses and mouse moves.
|
promise resolutions, HTTP results, timer events, key presses and mouse moves.
|
||||||
|
|
||||||
Expressions should finish quickly or the user experience may drag, especially on slower devices.
|
Expressions should finish quickly or the user experience may drag, especially on slower devices.
|
||||||
Consider caching values when their computation is expensive.
|
Consider caching values when their computation is expensive.
|
||||||
|
|
||||||
#### Simplicity
|
### Simplicity
|
||||||
|
|
||||||
Although it's possible to write quite complex template expressions, you should avoid them.
|
Although it's possible to write complex template expressions, it's a better
|
||||||
|
practice to avoid them.
|
||||||
|
|
||||||
A property name or method call should be the norm.
|
A property name or method call should be the norm, but an occasional Boolean negation, `!`, is OK.
|
||||||
An occasional Boolean negation (`!`) is OK.
|
Otherwise, confine application and business logic to the component,
|
||||||
Otherwise, confine application and business logic to the component itself,
|
where it is easier to develop and test.
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
<!-- end of Interpolation doc -->
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
@ -1673,8 +1699,8 @@ You can only bind to _another_ component or directive through its _Input_ and _O
|
|||||||
|
|
||||||
Remember that all **components** are **directives**.
|
Remember that all **components** are **directives**.
|
||||||
|
|
||||||
The following discussion refers to _components_ for brevity and
|
The following discussion refers to _components_ for brevity and
|
||||||
because this topic is mostly a concern for component authors.
|
because this topic is mostly a concern for component authors.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="no-toc">Discussion</h3>
|
<h3 class="no-toc">Discussion</h3>
|
||||||
@ -1985,7 +2011,7 @@ You'll need this template operator when you turn on strict null checks. It's opt
|
|||||||
|
|
||||||
{@a any-type-cast-function}
|
{@a any-type-cast-function}
|
||||||
|
|
||||||
## The `$any` type cast function (`$any( <expression> )`)
|
## The `$any` type cast function (`$any( <expression> )`)
|
||||||
|
|
||||||
Sometimes a binding expression will be reported as a type error and it is not possible or difficult
|
Sometimes a binding expression will be reported as a type error and it is not possible or difficult
|
||||||
to fully specify the type. To silence the error, you can use the `$any` cast function to cast
|
to fully specify the type. To silence the error, you can use the `$any` cast function to cast
|
||||||
@ -1994,7 +2020,7 @@ the expression to [the `any` type](http://www.typescriptlang.org/docs/handbook/b
|
|||||||
<code-example path="template-syntax/src/app/app.component.html" region="any-type-cast-function-1" header="src/app/app.component.html" linenums="false">
|
<code-example path="template-syntax/src/app/app.component.html" region="any-type-cast-function-1" header="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
In this example, when the Angular compiler turns your template into TypeScript code,
|
In this example, when the Angular compiler turns your template into TypeScript code,
|
||||||
it prevents TypeScript from reporting that `marker` is not a member of the `Hero`
|
it prevents TypeScript from reporting that `marker` is not a member of the `Hero`
|
||||||
interface.
|
interface.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user