Angularjs2 再谈数据绑定
在开始之前,我们先做一个简单的测试,假设有如下HTML片段:
1 | <input id="inDemo" value="Hello"> |
我们不难想象,运行后页面上会出现一个单行文本输入框,然后默认的值为 Hello。然后我们可以通过开发者工具的Console来执行一些临时的脚本,我们先获取这个元素:
1 | var inDemo = document.getElementById('inDemo'); |
然后接着操作,在界面上我们将输入框的Hello,修改为Angular,然后在Console中通过js获取input的value:
1 | console.log(inDemo.value); |
毋容置疑,会输出字符串Angular,那么,如果继续测试,执行下面的语句会输出什么呢?
1 | console.log(inDemo.getAttribute('value')); |
这里的结果会输出 Hello,那么这里的.value
和 .getAttribute('value')
区别在哪里呢? 这个就是我们这个测试的目的,也就是下面将要说明的 ** HTML Attribute ** 和 ** DOM Property **。
** HTML Attribute ** 和 ** DOM Property ** 的区别
Attribute 和 Property 的翻译都是属性的意思,加上完整的语义,那么可以表示为 HTML属性和 DOM属性,那么为什么会有这两个都是属性的东西存在呢?这个就要先简单了解下浏览器的工作方式。
浏览器会将获取到的HTML文本(网页),通过解析HTML文件生成DOM树,如果有接触过其他编程语言应该会了解到树型结构的数据,也就是一些语言中的Node
、TreeNode
等,没错,在Javascript中,DOM树中元素会被创建成一个HTMLElement
对象,而这个HTMLElement
对象也包含有一些属性,这些属性就是上面说的DOM Property
,而HTMLElement
对象一些属性的初始值则是通过HTML Attribute
来的。
那么整体的逻辑应该是这样的:
- 一段HTML片段
<input id="inDemo" value="Hello">
- 浏览器解析创建一个
HTMLElement
对象 - 根据
HTML Attribute
初始化对象属性,id=inDemo
,value=Hello
其实这个思维,我们在一些其他的开发中也有用到过,比如我们的程序在启动时需要读取配置文件,然后将配置文件中定义的属性值,转换为运行时的对象,从而在程序中来使用,如果在运行时修改配置对象的值,而不会影响配置文件的值,HTML Attribute
和 DOM Property
和这种方式可以看做是一致的。
我们可以看一下创建的对象都有哪些属性,接着测试,在Console窗口中执行:console.dir(inDemo)
可以看到,会拥有非常多的属性和事件,其中的id
也已经被赋值为inDemo
, 蓝色框的disabled:false
可以关注下,后面会介绍这个属性。
了解这些的意义是什么? 其实了解这些,只是需要明白一点,现阶段的前端框架,附带的数据双向绑定功能,Attribute
只是初始化了元素状态,打交道的都是DOM Property
,而我们在javascript
中使用的也是DOM Property
,只有明白了这点,才能更好的理解以及避免出现的一些问题,比如,常用的元素禁用,有时候我们为了禁用一个元素会这样写:
1 | <input id="inDemo" value="Hello" disabled="false"> |
这个渲染出来的文本框是禁用的还是可用的呢? 实际效果是禁用状态,可是明明给disabled
赋值是false
的呀。其实HTMLElement
对象的默认disabled
属性值是false
,然后 HTML Attribute
中只要出现了disabled
就会将HTMLElement
对象的disabled
设置为true
,和HTML Attribute
中disabled
取值无关。
总的来讲,attribute 是由 HTML 定义的,property 是由 DOM(Document Object Model) 定义的,且具有一些关系:
- 少量 HTML Attribute 和 Property 之间有着 1:1 的映射,比如:id、name、value。
- 有些 HTML Attribute 没有对应的 Property,比如:colspan。
- 有些 DOM Property 没有对应的 Attribute,比如:textContent。
- 大量 HTML Attribute 看起来像是映射到了 Property,但是并不是我们想的那样。
使用 NgModel 完成数据双向绑定
在上一篇我们简单提到,在input中的数据双向绑定使用[(ngModel)]=""
,在Angularjs2中,使用ngModel
进行双向绑定之前,需要导入FormsModule
模块。
在开始介绍[(ngModel)]
之前,我们先简单的了解下数据绑定。通俗的理解就是:将当前上下文中数据绑定给模板或者指令属性等。那么既然是绑定就需要了解绑定涉及到的两个概念:绑定目标 和 绑定数据源。
绑定的规则是:目标 = 源
,其实我们可以理解为赋值,变量的赋值。
绑定的目标是绑定符:[]
、()
、[()]
中的属性或者事件名,且只能绑定到显示标记为**输入[]和输出()**的属性,这点后面会做下说明。
接着回到[(ngModel)]
,我们可以看到包裹ngModel
既是输入又是输出,其实这是一个语法糖,ngModel
指令的完整写法为:
1 | <input [ngModel]="name" (ngModelChange)="name=$event"> |
一般建议使用封装好的[(ngModel)]
的方式,如果需要在用户输入的时候做些转换处理,则第二种是很方便的。当然,也可以直接利用 <input>
元素的value
属性和input
事件来实现绑定效果:
1 | <input [value]="name" (input)="name=$event.target.value"> |
具体在指令
的学习时会再次深入ngModel
指令进行分析,接下来简单聊下 输入(@Input) 和 **输出(@Output)**。
这里借用官方的一张图:
从图中可以看出,一般属性的赋值会被称作为输入,表示数据从当前的上下文中流入到指定的指定上。
而事件的赋值则被称为输出,表示事件调用会执行外部的处理器。
在指令
的学习中会详细的涉及到@Input()
和 @Output()
。