本章我们将阐述关于数据绑定的内容。如果没有数据绑定,Angular 组件就没什么用处,因为它不能显示动态数据。除了显示数据,组件还应该能够对用户操作做出响应。数据绑定能够使组件的显示与组件类的数据保持一致:也就是你在界面上看到的数据就是组件保存的数据。
什么是数据绑定?
数据绑定是一种视图与组件之间的数据能够保持一致的技术。用户在界面更新数据,Angular 将变更同步到视图绑定的组件类;组件类的数据被修改,界面也会同步更新显示。
数据绑定在 Angular 中有很多种表现形式,但最通用的方式是将数据绑定分为两类:单向数据绑定和双向数据绑定。
单向数据绑定
单向数据绑定意味着数据是单向流动的:从组件流向视图或者从视图流向组件。
从组件到视图
将组件的数据绑定到视图,可以使用插值和属性绑定。
插值
插值允许我们在 HTML 中使用特殊的表达式。Angular 会计算这个表达式,然后将表达式的结果转换为字符串,替换 HTML 中的表达式。因此,Angular 的插值也被称为字符串插值。这意味着,表达式的结果必须能够转换成一个字符串。如果结果类型是boolean
或者object
,渲染结果可能不会与预期一致。如果需要绑定boolean
或者object
,则需要使用属性绑定。
插值只能用于 property,不能用于 attribute。
Angular 使用{{}}
标记插值,其具体语法为:
{{ templateExpression }}
{{}}
中的表达式被称为模板表达式。
只要模板表达式有变化,Angular 会立即重新计算并更新视图。
看下面的一个例子:
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `Welcome, {{firstName}} {{lastName}}`, styleUrls: ['./app.component.css'] }) export class AppComponent { firstName = 'Tom'; lastName = 'Scott'; }
当我们运行应用时,{{firstName}}
会被替换为firstName
的计算结果,也就是 Tom,{{lastName}}
会被替换为lastName
的计算结果,也就是 Scott。并且,只要firstName
或lastName
的值发生变化,{{firstName}}
或{{lastName}}
的显示就会发生变化。这一过程反过来并不会实现:通过某种方式修改{{firstName}}
或{{lastName}}
的值,并不会同步到firstName
或lastName
。这就是“单向数据绑定”的含义。
注意,模板表达式不能改变应用的状态。为了计算表达式的值,Angular 需要从组建读取数据,然后渲染到页面。如果表达式改变了应用的状态,可能会导致计算结果与组件类保存的数据不一致。这意味着模板表达式中不允许出现以下操作符:
- 赋值运算符:
=
,+=
,-=
,... - 某些关键字,例如
new
,typeof
或者instanceof
- 链式表达式符号,
;
或,
- 递增、递减运算符
++
或--
- 位运算符,如
|
或&
插值的使用有很多种,下面举一些例子。
调用函数
使用插值可以调用组件的函数:
// 模板 {{ getTitle() }} // 组件 title = 'Angular Interpolation'; getTitle(): string { return this.title; }
拼接字符串
在插值中拼接字符串:
<p>Welcome to {{title}}</p> <p>{{ 'Hello & Welcome to' + ' Angular Interpolation '}}</p> <p>Welcome {{ firstName }}, {{ lastName }}</p> <p>Welcome {{ getFirstName() }}, {{ getLastName() }}</p>
数学计算
在插值中进行数学运算:
// 模板 <p>100x80 = {{100*80}}</p> <p>Largest: {{max(100, 200)}}</p> // 组件 max(first: number, second: number): number { return Math.max(first, second); }
绑定到元素 property
插件可以绑定到 HTML 元素、组件或者指令的任意能够接受字符串的属性:
<p>Show me <span class="{{ redColor }}">red</span></p> <p style.color="{{ redColor }}">This is red</p> <img src="{{itemImageUrl}}" /> <a href="/product/{{productID}}">{{productName}}</a>
使用模板引用变量
页面元素可以使用模板引用变量引用;模板表达式中也可以使用模板引用变量。例如:
<input (keyup)="0" #name> <p>Welcome {{name.value}} </p>
模板引用变量以 # 开头。上面代码中,#name
就是一个模板引用变量。我们可以通过模板引用变量获取这个元素,例如使用#name.value
就可以获取#name
指向的那个<input>
的元素的value
的值。
XSS
在替换视图表达式之前,Angular 会把表达式结果进行转义,以避免 XSS 攻击。也就是说,下面的插值实际上是不能实现的:
// 模板 <p>{{script}}</p> <p>{{div}}</p> // 组件 script ='<script>alert("You are hacked")</script>' div='<div>this is a div</div>';
ngNonBindable
如果你就是想在页面显示{{}}
,而不是直接显示插值的结果,那么就可以使用ngNonBindable
指令:
<p>Evaluate: {{variable}}</p> <p ngNonBindable>Do not evaluate: {{variable}}</p>
使用管道
插件中可以使用管道:
<p>pipe chain: {{title | json | lowercase}}</p>
安全导航符
模板表达式可以使用安全导航符,以避免null
或undefiend
异常:
<!-- nullItem 如果为 null 的话就会抛出异常: --> <!-- TypeError: Cannot read property 'name' of null --> <p>The item name is: {{nullItem.name}}</p> <!-- nullItem 即便为 null 也可能安全执行 --> <p>The item name is: {{nullItem?.name}}</p>
非空断言运算符
如果我们在 tsconfig.json 中开启strictNullChecks
,就可以在编译期对变量做出检查:如果变量是null
或undefiend
,或者无法判断变量是不是null
或undefiend
,都会抛出一个编译错误。如果你能保证变量一定不是null
或undefiend
,就可以使用非空断言运算符,来告诉 TypeScript 不要对该表达式进行检查。注意,这个检查是编译期的,不是运行时的:
<p>The item's name is: {{item!.name}}</p>
本章我们详细阐述了 Angular 数据绑定的插值绑定。插值绑定是一种字符串绑定,所绑定的值只能是字符串。在后半部分,我们展示了很多种插值绑定的应用场景。不要在意这些场景中的“指令”、“模板引用变量”这些名词,我们会在以后的章节中详细介绍。现在只要了解插值的应用即可。