本章我们将阐述关于数据绑定的内容。如果没有数据绑定,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 数据绑定的插值绑定。插值绑定是一种字符串绑定,所绑定的值只能是字符串。在后半部分,我们展示了很多种插值绑定的应用场景。不要在意这些场景中的“指令”、“模板引用变量”这些名词,我们会在以后的章节中详细介绍。现在只要了解插值的应用即可。