Angular 属性绑定 [] 和 {{}} 的差异

Angular 属性绑定 [] 和 {{}} 的差异

当我们在 Angular 中给属性绑定一个变量的时候,主要有下面三种绑定方式

  • [property] = "variable"
  • property = ""
  • [attr.property] = "xxx"

下面我们就看看它们之间的区别

[[]] 和 {{}}

这两种方式也是平常使用最多的,我们下面通过一个示例看看它们之间的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component({
selector: "my-test",
template: `<br>222222{{test}}`
})

export class TestComponent {
@Input() test: any
@Input() testTwo: string

ngOnChanges(change) {
console.log(this.test)
console.log(this.testTwo)
console.log(change)
}
}

然后我们来使用它

1
2
3
<my-test [test]="str" [testTwo]="'[]'"></my-test>
---------------------------
<my-test test="{{str}}" [testTwo]="'{}'"></my-test>

str 是一个字符串,并且我们通过 input 或者 button 来改变 str 的值的时候,两者在页面上的显示和控制台的打印都是正常的,看起来并没有什么差异,但是在 str 还没有赋值之前,前者打印的是 undefined,而后者打印的是空字符串,也许这样并不能看出什么,所以我们试着传递一个对象过去,这下便可以看出区别了,前者可以正常的显式我们传递的对象,比如 {a: 1, b: 2},但是后者显式的却是 '[Object Object]'

HTML attribute 和 DOM property

Angular 中还有一种 attribute 绑定,写法为 [attr.Attribute]="variable",那么它和 [property]="variable" 的区别又在哪里呢?

在看它们两者的区别之前我们先来了解一下 HTML attributeDOM property 两者的区别,这里虽然涉及到一些 HTML 当中的知识,但是仍然适用于 Angular 当中遇到的情况,在本章节当中我们约定,使用『特性』来代指 attribute ,使用『属性』来代指 property,当我们在书写 HTML 代码的时候,我们为 HTML 元素设置特性,例如

1
<input id="name" value="zhangsan" />

我们写了一个 input 标签,并给他定义了两个特性(idvalue),当浏览器解析这段代码的时候,会把 HTML 源码解析为 DOM 对象,确切的说是解析为 HTMLInputElement 对象,HTMLInputElement 的继承关系是

1
2
3
4
5
6
7
8
9
10
11
HTMLInputElement

HTMLElement

Element

Node

EventTarget

Object

通过查看文档会发现,HTMLInputElement 的原型上定义了很多属性和方法,例如 formnametypealtcheckedsrcvalue 等等,还有从 HTMLElement 继承来的 idtitleclientTop 等等,如果仔细寻找,就不难发现其中就有我们为 input 标签定义的特性 idvalue,这是因为当浏览器解析网页时,将 HTML 特性映射为了 DOM 的『属性』

Element 类还有一个 Element.attributes 属性,里面包含了所有的特性,但是 HTML attributeDOM property 并不总是一对一的关系,在下面我们会看到

DOM 属性

当浏览器解析完 HTML 后,生成的 DOM 是一个继承自 Object 的常规 JavaScript 对象,因此我们可以像操作任何 JavaScript 对象那样来操作 DOM 对象

1
2
Element.foo = 'bar'
Element.user = { name: 'zhangsan', age: '22'}

也可以为其添加方法,如果你想给每个 HTML 元素都添加属性或方法,甚至可以直接修改 Element.prototype,不过不建议这么操作

HTML 特性

DOM 属性类似,除了那些规范里定义的标准特性外,HTML 也可以添加非标准的属性,例如

1
<input id="name" value="zhangsan" foo="bar" />

HTML 特性映射为 DOM 属性时,『只映射标准属性』,访问非标准属性将得到 undefined

1
2
const el = document.getElementById('name')
el.foo === undefined

好在 DOM 对象也提供了操作特性的 API

1
2
3
4
Element.hasAttribute(name)        // 判断某个特性是否存在
Element.getAttribute(name) // 获取指定特性的值
Element.setAttribute(name, value) // 设置指定特性的值
Element.removeAttribute(name) // 移除指定特性

以上 API 定义在 Element 上,根据 HTML 规范,标签以及特性名是不区分大小写的,因此以下代码是一样的

1
2
3
Element.getAttribute('id')
Element.getAttribute('ID')
Element.getAttribute('iD')

并且特性永远都是字符串或 null,如果我们为特性设置非字符串的值,则引擎会将此值转换为字符串,属性是具有类型的

1
2
3
4
5
Element.getAttribute('checked') === ''          // 特性是字符串
Element.checked === false // 属性是 boolean 类型的值

Element.getAttribute('style') === 'color:blue' // 特性是字符串
typeof Element.style === 'object' // 属性是 CSSStyleDeclaration 对象

即使都是字符串,属性和特性也可能不同,有一个例外就是 href

  • HTML attribute,对于 href 返回 HTML 设置的值
  • DOM property,对于 href 返回解析后的完整 url

特性和属性的同步

当标准的特性更新时,对应的属性也会更新,反之亦然,但是 input.value 的同步是单向的,只是 attribute ==> property,当修改特性时,属性也会更新,但是修改属性后,特性却还是原值

1
2
3
4
5
Element.setAttribute('value', 'zhangsan')       // 修改特性
Element.value === 'zhangsan' // 属性也更新了

Element.value = 'newValue' // 修改属性
Element.getAttribute('value')) === 'zhangsan' // 特性没有更新

非标准特性

非标准 HTML 特性并不会自动映射为 DOM 属性,当我们使用 data- 开头的特性时,会映射到 DOMdataset 属性,中划线格式会变成驼峰格式

1
2
3
4
5
Element.setAttribute('data-name', 'zhangsan')
Element.dataset.name === 'zhangsan'

Element.setAttribute('data-age', 18)
Element.dataset.age === '18'

自定义特性 VS 非规范特性

HTML 允许我们自定义标签,也可以扩展标签的特性,但是我们推荐使用已经进入 HTML5 规范的自定义特性 data-*,比如我们想为div 标签增加一个 age 特性,我们可以有两种选择

1
2
<div age="18">zhangsan</div>
<div data-age="18">zhangsan</div>

虽然第一种代码更短,但是却有一个潜在的风险,因为 HTML 规范是一直发展变化的,也许在未来的某个版本中,age 被添加进了标准特性里面,这将会引起潜在的 bug

总结

  • property 指的是 DOM 中的属性,是 JavaScript 里的对象
  • attribute 指的是 HTML 标签上的特性,它的值只能够是字符串
  • DOM 中的有一个 attribute 的属性,其中就是 HTML 标签上的特性列表
  • 两者之间的数据绑定是单向的,更改 attribute 会同步到 property,但反之则不然
  • 更改 propertyattribute 的值,都会将更新反映到 HTML 页面中

Angular 当中,由于 element 没有这些属性,中括号 [] 的属性绑定语法自然不行,而且模版编译的时候会报错,所以对于非基本属性,Angular 提供了这种 [attr.Attribute]="variable" 的特性绑定语法

当然,在 property 是基本属性时,使用 [property]="variable" 绑定属性,然后改变 variable 的值,对应的 attribute 也会跟着属性同步,Angular 应该是做了属性和特性的数据双向绑定的工作,所以只有 property 是基本属性,两者一样,例如 [id][attr.id] 的作用并无区别

两个需要注意的地方

一个就是常见的传值的差异

1
2
3
[property]="false"

[property]="{{false}}"

特别需要注意,因为后者其实绑定的是 'false'(字符串),所以一旦用在了 if 语句当中就可能出现问题

另外一个就是 Input 标签的 maxlength 特性对应的是 maxLength 属性(注意 L 是大写),虽然是大写,但是在浏览器当中解析出来的依然是 <input maxlength="10" />(小写),所以写成 [attr.maxlength][attr.maxLength][maxLength] 都是可以,但是 [maxlength] 就不行,同理 minlength/minLength 是一样的

小结

综上所诉,属性绑定最好还是用中括号,双大括号用于展示,如

1
<div>{{obj | json}}</div>

实际上在渲染视图之前,Angular 把这些插值表达式翻译成相应的属性绑定,还有就是,它依然可以用于执行 Angular 的模板语法,例如

1
2
3
property="{{fun()}}"

property="{{a ? b : c}}"

只是它会做多一步,把 return 的值转换成 string

参考

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×