Angular 学习之路 07 – 数据绑定(1)

本章我们将阐述关于数据绑定的内容。如果没有数据绑定,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。并且,只要firstNamelastName的值发生变化,{{firstName}}{{lastName}}的显示就会发生变化。这一过程反过来并不会实现:通过某种方式修改{{firstName}}{{lastName}}的值,并不会同步到firstNamelastName。这就是“单向数据绑定”的含义。

注意,模板表达式不能改变应用的状态。为了计算表达式的值,Angular 需要从组建读取数据,然后渲染到页面。如果表达式改变了应用的状态,可能会导致计算结果与组件类保存的数据不一致。这意味着模板表达式中不允许出现以下操作符:

  • 赋值运算符:=+=-=,...
  • 某些关键字,例如newtypeof或者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>
安全导航符

模板表达式可以使用安全导航符,以避免nullundefiend异常:

<!-- 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,就可以在编译期对变量做出检查:如果变量是nullundefiend,或者无法判断变量是不是nullundefiend,都会抛出一个编译错误。如果你能保证变量一定不是nullundefiend,就可以使用非空断言运算符,来告诉 TypeScript 不要对该表达式进行检查。注意,这个检查是编译期的,不是运行时的:

<p>The item's name is: {{item!.name}}</p>

本章我们详细阐述了 Angular 数据绑定的插值绑定。插值绑定是一种字符串绑定,所绑定的值只能是字符串。在后半部分,我们展示了很多种插值绑定的应用场景。不要在意这些场景中的“指令”、“模板引用变量”这些名词,我们会在以后的章节中详细介绍。现在只要了解插值的应用即可。

Leave a Reply