上一章我们简单介绍了 Angular 数据绑定。Angular 数据绑定分为单向数据绑定和双向数据绑定。单向数据绑定又包括从组建到视图和从视图到组件;前者又分为插值和属性绑定。
前面我们已经介绍过插值。插值仅接受字符串类型,如果我们需要传递boolean、Array或object就无能为力了。为解决这一问题,Angular 又引入了属性绑定。
属性绑定
属性绑定允许我们将组件的字段绑定到 HTML 元素属性。当组件字段值发生改变,Angular 会自动更新 HTML 中绑定的属性。我们可以使用属性绑定设置诸如class、href、src等,也可以将绑定到自定义组件或者指令使用@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 对象HTMLInputElement的disabled属性是一个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 并不同名。例如,HTMLTableCellElement有colSpan和rowSpan property,HTML 的<td>标签则是colspan和rowspan attribute。在属性绑定时,需要使用[colSpan]和[rowSpan]。
属性绑定与插值的语法区别就是[],所以不要忘记[]。如果忘记使用[],Angular 就会当作普通的字符串传入,从而发生不可预知的错误。
与字符串插值类似,属性绑定也会自动进行 HTML 转移,以避免 XSS 攻击。
属性绑定和插值
所有插值能实现的功能,都可以使用属性绑定完成。插值可以看作是一种特殊的属性绑定。例如,
<h1> {{ title }} </h1> 等价于
<h1 [innerText]="title"></h1>
事实上,在渲染页面之前,Angular 会自动将插值转换为属性绑定。
插值更简单,并且可读性更高。例如上面的代码示例,使用插值明显比属性绑定易读。但插值的限制在于仅能使用字符串。如果要绑定的数据不是字符串,就只能使用属性绑定。
1 个评论
写得非常好,清晰,重点突出,要点齐全。感谢~