当我们在 Angular
中给属性绑定一个变量的时候,主要有下面三种绑定方式
[property] = "variable"
property = ""
[attr.property] = "xxx"
下面我们就看看它们之间的区别
[[]] 和 {{}}
这两种方式也是平常使用最多的,我们下面通过一个示例看看它们之间的区别
1 | @Component({ |
然后我们来使用它
1 | <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 | HTMLInputElement |
通过查看文档会发现,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 | Element.foo = 'bar' |
也可以为其添加方法,如果你想给每个 HTML
元素都添加属性或方法,甚至可以直接修改 Element.prototype
,不过不建议这么操作
HTML 特性
和 DOM
属性类似,除了那些规范里定义的标准特性外,HTML
也可以添加非标准的属性,例如
1 | <input id="name" value="zhangsan" foo="bar" /> |
当 HTML
特性映射为 DOM
属性时,『只映射标准属性』,访问非标准属性将得到 undefined
1 | const el = document.getElementById('name') |
好在 DOM
对象也提供了操作特性的 API
1 | Element.hasAttribute(name) // 判断某个特性是否存在 |
以上 API
定义在 Element
上,根据 HTML
规范,标签以及特性名是不区分大小写的,因此以下代码是一样的
1 | Element.getAttribute('id') |
并且特性永远都是字符串或 null
,如果我们为特性设置非字符串的值,则引擎会将此值转换为字符串,属性是具有类型的
1 | Element.getAttribute('checked') === '' // 特性是字符串 |
即使都是字符串,属性和特性也可能不同,有一个例外就是 href
HTML attribute
,对于href
返回HTML
设置的值DOM property
,对于href
返回解析后的完整url
特性和属性的同步
当标准的特性更新时,对应的属性也会更新,反之亦然,但是 input.value
的同步是单向的,只是 attribute ==> property
,当修改特性时,属性也会更新,但是修改属性后,特性却还是原值
1 | Element.setAttribute('value', 'zhangsan') // 修改特性 |
非标准特性
非标准 HTML
特性并不会自动映射为 DOM
属性,当我们使用 data-
开头的特性时,会映射到 DOM
的 dataset
属性,中划线格式会变成驼峰格式
1 | Element.setAttribute('data-name', 'zhangsan') |
自定义特性 VS 非规范特性
HTML
允许我们自定义标签,也可以扩展标签的特性,但是我们推荐使用已经进入 HTML5
规范的自定义特性 data-*
,比如我们想为div
标签增加一个 age
特性,我们可以有两种选择
1 | <div 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 | [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 | property="{{fun()}}" |
只是它会做多一步,把 return
的值转换成 string