上一章我们简单介绍了 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 个评论
写得非常好,清晰,重点突出,要点齐全。感谢~