Angular 学习之路 08 – 数据绑定(2)

上一章我们简单介绍了 Angular 数据绑定。Angular 数据绑定分为单向数据绑定和双向数据绑定。单向数据绑定又包括从组建到视图和从视图到组件;前者又分为插值和属性绑定。

前面我们已经介绍过插值。插值仅接受字符串类型,如果我们需要传递booleanArrayobject就无能为力了。为解决这一问题,Angular 又引入了属性绑定

属性绑定

属性绑定允许我们将组件的字段绑定到 HTML 元素属性。当组件字段值发生改变,Angular 会自动更新 HTML 中绑定的属性。我们可以使用属性绑定设置诸如classhrefsrc等,也可以将绑定到自定义组件或者指令使用@Input()修饰的属性。

属性绑定使用如下语法:

[binding-target] = "binding-source"

其中,binding-target 是元素属性名,binding-source 是要绑定的数据源,是一个模板表达式,可以是组件属性,可以是组件函数,可以是模板引用变量,或者其它合法的类型。

下面来看属性绑定的一个例子:

import { Component } from '@angular/core';
 
@Component({
  selector: 'app-root',
  template: `
<h1 [innerText]="title"></h1>
<h2>Example</h2>
<button [disabled]="isDisabled">I am disabled</button>`,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = "Angular Property Binding";
  isDisabled = true;
}

上面的代码有两个属性绑定。title类型是字符串,绑定到<h1>标签的innerText属性;isDisabled类型是boolean,绑定到<button>标签的disabled属性。当我们在运行时修改title或者isDisabled的值的时候,Angular 都会自动更新 HTML 视图。

注意,属性绑定同样也是单向数据绑定。这意味着,如果我们把isDisabled绑定到<input type="checkbox">标签,用户点击 checkbox 时,尽管页面的显示会有变化,但isDisabled的值是不会发生改变的。

property 和 attribute 的区别

前面的章节我们一直在强调 Angular 绑定的是 property,而不是 attribute。那么,property 和 attribute 的区别是什么呢?

简单来说,property 是 DOM 定义的,attribute 是 HTML 定义的。

从历史上来说,property 和 attribute 都可以被翻译成“属性”。但这两者完全不同。

property 是对象的属性,属于面向对象的范畴,因此由 DOM 定义(因为 DOM,即 Document Object Model,文档对象模型)。它代表一个对象的属性,例如 input 的宽度、样式等。attribute 是 HTML 编程语言的一部分。注意,语言提供的是对象的一种表达载体。我们可以用 HTML 定义 DOM,也可以发明一种别的什么语言定义。少量 HTML attribute 与 DOM property 是一一对应的,例如id;有些 HTML attribute 不对应任何 DOM property,例如 colspan(我们可以在 HTML 中写colspan,但在 DOM 的HTMLTableDataCellElement对象中是没有同名 property);有些 DOM property 不对应任何 HTML attribute,例如innerText(DOM 的HTMLElement对象有一个innerText属性,但 HTML 标签没有);大量的 HTML attribute 与 DOM property 同名,但却不是对应的,这部分尤其令人迷惑(尽管二者名字相同,以至于我们在 HTML 用属性绑定的时候可能会以为用到的是 HTML attribute,但其实是绑定到了 DOM property)。

HTML attribute 与 DOM property 同名,并不意味着一一对应。这一点我们可以理解成,HTML attribute 初始化 DOM property,之后,attribute 的任务就完成了。例如我们的 HTML 代码为<input value="TOM" />,在浏览器渲染时,将创建 DOM 对象HTMLInputElement,并将其value属性初始化为 TOM。当用户在输入框输入了 Jerry 时,是 DOM 的value值变为 Jerry,而不是 attribute 变化了。我们可以利用浏览器开发者工具查看,<input>标签依旧是<input value="TOM" />,使用input.getAttribute('value')返回值依旧是 TOM。此时,attribute value 与 property value 的值已经不一致了。也就是说,HTML attribute 指定了初始值,DOM property 才是运行时的当前值。

理解了这一点,我们来看一个奇怪的例子,即disabled。HTML attribute 和 DOM property 都有disabled。DOM 对象HTMLInputElementdisabled属性是一个boolean值,当值为false是,input 可用,当值为true时,input 不可用。然而,对于 HTML attribute 的disabled而言,只要出现了disabled,不论其值是什么,只要有这个 attribute,对于的 DOM property 都被初始化为true。这也就是为什么代码<input disabled="false" />依旧会得到一个不可用的 input。回忆一下我们前面写的 Angular 属性绑定:<button [disabled]="isDisabled">I am disabled</button>,为什么这里的disabled可以使用boolean类型进行赋值,也就可以理解了,因为我们绑定到的是 DOM property 而不是 HTML attribute。

不同于插值的字符串类型,属性绑定需要注意类型匹配:绑定的值与属性的类型必须一致,否则会出现错误。另外需要注意的是,Angular 属性绑定的是 property,有些 property 与 attribute 并不同名。例如,HTMLTableCellElementcolSpanrowSpan property,HTML 的<td>标签则是colspanrowspan attribute。在属性绑定时,需要使用[colSpan][rowSpan]

属性绑定与插值的语法区别就是[],所以不要忘记[]。如果忘记使用[],Angular 就会当作普通的字符串传入,从而发生不可预知的错误。

与字符串插值类似,属性绑定也会自动进行 HTML 转移,以避免 XSS 攻击。

属性绑定和插值

所有插值能实现的功能,都可以使用属性绑定完成。插值可以看作是一种特殊的属性绑定。例如,

<h1> {{ title }} </h1>

等价于

<h1 [innerText]="title"></h1>

事实上,在渲染页面之前,Angular 会自动将插值转换为属性绑定。

插值更简单,并且可读性更高。例如上面的代码示例,使用插值明显比属性绑定易读。但插值的限制在于仅能使用字符串。如果要绑定的数据不是字符串,就只能使用属性绑定。

One Response

  1. 穗穗 2020年11月24日

Leave a Reply