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 attribute 和 DOM property 两者的区别,这里虽然涉及到一些 HTML 当中的知识,但是仍然适用于 Angular 当中遇到的情况,在本章节当中我们约定,使用『特性』来代指 attribute ,使用『属性』来代指 property,当我们在书写 HTML 代码的时候,我们为 HTML 元素设置特性,例如

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

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

1
2
3
4
5
6
7
8
9
10
11
HTMLInputElement
↓
HTMLElement
↓
Element
↓
Node
↓
EventTarget
↓
Object

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

而 Element 类还有一个 Element.attributes 属性,里面包含了所有的特性,但是 HTML attribute 和 DOM 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- 开头的特性时,会映射到 DOM 的 dataset 属性,中划线格式会变成驼峰格式

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,但反之则不然
  • 更改 property 和 attribute 的值,都会将更新反映到 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

×