什么是数据绑定

数据绑定是将一个用户界面元素的属性绑定到一个类型/对象实例上的某个属性的方法,实现该元素属性和该对象实例属性在一方数值修改时另一方的数值也随之修改。

百度百科中的数据绑定的举例:如果一个开发者有一个Customer类型的实例,那么他就可以把Customer的“Name”属性绑定到一个TextBox的“Text”属性上。“绑定”了这2个属性之后,对TextBox的Text属性的更改将“传播”到Customer的Name属性,而对Customer的Name属性的更改同样会“传播”到TextBox的Text属性。Windows窗体的简单数据绑定支持绑定到任何public或者internal级别的·NET Framework属性,同样可以利用数据库来简单地绑定页面控件的单个属性。

在此之前做项目的时候做过C#编写的WPF应用程序,对其中的数据绑定有一些应用,感觉非常方便,开发效率很高。

C#WPF数据绑定举例:

 

 先建模型类,告警类:

1 public class CAlarm                                        //告警类
2     {
3         public int iID { get; set; }                        //告警对象编号
4         public String sTsName { get; set; }           //告警对象名称
5         public String sAlarmLevel { get; set; }       //告警级别
6         public String sDesc { get; set; }                //告警描述
7         public DateTime dAlarmTime { get; set; }         //告警时间
8     }

页面布局代码:

 1 <Window x:Class="WPFBinding.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="告警列表" Height="350" Width="487">
 5     <Grid Height="311" Width="464">
 6         <ListView Name="listView1" Height="311" Width="464">
 7             <ListView.View>
 8                 <GridView>
 9                     <GridViewColumn Header="编号" DisplayMemberBinding="{Binding iID}" Width="50"></GridViewColumn>
10                     <GridViewColumn Header="对象名称" DisplayMemberBinding="{Binding sTsName}" Width="100"></GridViewColumn>
11                     <GridViewColumn Header="告警级别" DisplayMemberBinding="{Binding sAlarmLevel}" Width="60"></GridViewColumn>
12                     <GridViewColumn Header="告警描述" DisplayMemberBinding="{Binding sDesc}" Width="100"></GridViewColumn>
13                     <GridViewColumn Header="告警时间" DisplayMemberBinding="{Binding dAlarmTime}" Width="100"></GridViewColumn>
14                 </GridView>
15             </ListView.View>
16         </ListView>
17     </Grid>
18 </Window>

然后是MainWindow的代码:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Windows;
 6 using System.Windows.Controls;
 7 using System.Windows.Data;
 8 using System.Windows.Documents;
 9 using System.Windows.Input;
10 using System.Windows.Media;
11 using System.Windows.Media.Imaging;
12 using System.Windows.Navigation;
13 using System.Windows.Shapes;
14 using System.Collections.ObjectModel;
15 
16 namespace WPFBinding
17 {
18     /// <summary>
19     /// MainWindow.xaml 的交互逻辑
20     /// </summary>
21     public partial class MainWindow : Window
22     {
23         private IList<CAlarm> m_ilAlarms = new ObservableCollection<CAlarm>();
24         public MainWindow()
25         {
26             DateTime dt = new DateTime();
27             TimeSpan ts = new TimeSpan(0, 0, 2, 0, 0);
28 
29             m_ilAlarms.Add(new CAlarm() { iID = 1, sTsName = "对象1", sAlarmLevel = "一级告警", sDesc = "水浸检测到漏水", dAlarmTime = dt });
30             m_ilAlarms.Add(new CAlarm() { iID = 2, sTsName = "对象1", sAlarmLevel = "二级告警", sDesc = "水浸检测到漏水", dAlarmTime = dt = dt.Add(ts) });
31             m_ilAlarms.Add(new CAlarm() { iID = 3, sTsName = "对象2", sAlarmLevel = "一级告警", sDesc = "水浸检测到漏水", dAlarmTime = dt = dt.Add(ts) });
32             m_ilAlarms.Add(new CAlarm() { iID = 4, sTsName = "对象3", sAlarmLevel = "三级告警", sDesc = "水浸检测到漏水", dAlarmTime = dt = dt.Add(ts) });
33             m_ilAlarms.Add(new CAlarm() { iID = 5, sTsName = "对象2", sAlarmLevel = "一级告警", sDesc = "水浸检测到漏水", dAlarmTime = dt = dt.Add(ts) });
34             InitializeComponent();
35 
36 
37             this.listView1.ItemsSource = m_ilAlarms;
38         }
39     }
40 
41     public class CAlarm                                //告警类
42     {
43         public int iID { get; set; }                //告警对象编号
44         public String sTsName { get; set; }            //告警对象名称
45         public String sAlarmLevel { get; set; }        //告警级别
46         public String sDesc { get; set; }            //告警描述
47         public DateTime dAlarmTime { get; set; }        //告警时间
48     }
49 }

运行结果查看绑定:

MainWindow后端代码使用ObservableCollection这个集合,可以双向绑定。

private IList<CAlarm> m_ilAlarms = new ObservableCollection<CAlarm>();是定义一个双向绑定的集合

以下是界面绑定内容

<GridViewColumn Header="编号" DisplayMemberBinding="{Binding iID}" Width="50"></GridViewColumn>

<GridViewColumn Header="对象名称" DisplayMemberBinding="{Binding sTsName}" Width="100"></GridViewColumn>

<GridViewColumn Header="告警级别" DisplayMemberBinding="{Binding sAlarmLevel}" Width="60"></GridViewColumn>

<GridViewColumn Header="告警描述" DisplayMemberBinding="{Binding sDesc}" Width="100"></GridViewColumn>

<GridViewColumn Header="告警时间" DisplayMemberBinding="{Binding dAlarmTime}" Width="100"></GridViewColumn>

使用DisplayMemberBinding="{Binding iID}"的方式绑定

当然WPF中还有其他绑定数据到界面元素的方法和绑定类型,在此不再一一赘述。我想说的是WPF的这种绑定的方式大大提高了开发的效率。


然后在最近的一个web应用的项目中前端和后端分离,使用ajax获取后端json数据,然后拿到数据后再通过js刷新到web页面的dom对象相应的属性中显示在页面上。

js代码写了一大片一大片的。好累人啊。

联想到WPF的开发高效,现在真的好痛苦啊。

我是一个很懒偷奸耍滑的人。怎么办?度娘。果然有现成的:

以下是度娘百科中的内容:

AngularJS

编辑
AngularJS[1]  诞生于2009年,由Misko Hevery 等人创建,后为Google所收购。是一款优秀的前端JS框架,已经被用于Google的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVW(Model-View-Whatever)、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。
AngularJS 是一个 JavaScript框架。它是一个以 JavaScript 编写的库。它可通过 <script> 标签添加到HTML 页面。
AngularJS 通过 指令 扩展了 HTML,且通过 表达式 绑定数据到 HTML。
AngularJS 是以一个 JavaScript 文件形式发布的,可通过 script 标签添加到网页中。
 
看来我太low了。老土了!跟不上了!
 
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
<
p>双向数据绑定:</p> <input type="text" [(ngModel)]="user.name"/> <div [ngStyle]="style1">{{user.name}}</div>

js代码:

//ts代码
user:any = {
    name:"12345"
  }

直接搞定。

好高效啊!


 

我能不能自己写个绑定代码?试试:

先做一个文本框和json对象绑定的试试。

查资料发现es5的特性get/set访问器有希望实现这个双向绑定的功能

上代码:

  1 <!DOCTYPE html>
  2 <html>
  3 <head>
  4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5     <title>setAndgetTest</title>
  6     <style>
  7         html,
  8         body {
  9             width: 100%;
 10             height: 100%;
 11             overflow: hidden;
 12         }
 13     </style>
 14     <script type="text/javascript">
 15         
 16         //声明一个json对象用来和dom对象绑定
 17         var theObj = {
 18             student_id: 100001,
 19             name: "Leao",
 20             age: 31,
 21             phoneNmb: "18618328433"
 22         };
 23 
 24         var CBinding = function(obj) {
 25             var vals = {};
 26             /*
 27             private变量,用来保存对象属性值的集合,只保存基本类型,以json形式保存,比如vals最终可能是这样:
 28             {
 29                 name:"Leao",
 30                 student_id: 100001,
 31                 age: 31,
 32                 phoneNmb: "18618328433"
 33             }
 34             */
 35 
 36             var doms = {};
 37             /*
 38             private变量,用来保存dom对象的集合,也就是vals的内容跟dom对象的绑定,doms最终可能是这样
 39             {
 40                 Obj:domObj,        //domObj是name绑定的dom对象
 41                 protertyName:"value"    //protertyName是name在dom对象domObj上绑定的属性名称
 42             }
 43 
 44             其实可以把上面的vals修改一下:
 45                     name: "Leao",
 46                     age: 31,
 47                     phoneNmb: "18618328433"
 48             {
 49                 name:{
 50                     name: "name1",
 51                     bindingObj:{
 52                         Obj:domObj,
 53                         protertyName:"value"
 54                     }
 55                 },
 56                 student_id: 100001,//没有绑定
 57                 age: 31,//没有绑定
 58                 phoneNmb: "18618328433"//没有绑定
 59             }
 60             */
 61 
 62             this.bInit = false;//是否被初始化
 63 
 64             if (null != obj) {
 65                 var names = Object.getOwnPropertyNames(obj);//获取obj的所有属性名
 66                 var sFieldName = "";
 67 
 68                 if (null != names && 0 < names.length) {
 69                                 var iIndex = 0, iCount = names.length, val = null;
 70 
 71                     for (iIndex = 0; iIndex < iCount; iIndex++) {
 72                         sFieldName = names[iIndex];
 73                         val = obj[sFieldName];
 74                         vals[sFieldName] = val;
 75                         doms[sFieldName] = {
 76                             Obj: {},        //保存dom对象
 77                             bSet: false,        //是否被绑定
 78                             protertyName: ""    //dom对象的属性名称
 79                         };
 80 
 81                         Object.defineProperty(this, sFieldName, {
 82                             value: val,
 83                             configurable: true,    //能否使用delete、能否需改属性特性、或能否修改访问器属性、,false为不可重新定义,默认值为true
 84                             enumerable: true,    //对象属性是否可通过for-in循环,flase为不可循环,默认值为true
 85                             writable: true,        //对象属性是否可修改,flase为不可修改,默认值为true
 86                         });
 87                     }
 88                 }
 89             }
 90         
 91             this.bInit = true;//被初始化
 92 
 93             /*
 94                 绑定方法,
 95                 @domObj,绑定的dom对象
 96             */
 97             this._binding = function(domObj) {//
 98                 if (null != domObj) {
 99                     var sName = domObj.name || domObj.id;//绑定按照name或id进行匹配
100 
101                     if (null != sName && 0 < sName.length) {
102                         var theVal = vals[sName];
103                         var theDom = doms[sName];
104 
105                         if (null != theVal && null != theDom) {    //查看是存在sName的的值和绑定对象
106                             var fieldName = "value";    //绑定的dom对象属性名称,此处只实现了value以后可以在本方法中添加一个参数用来表示属性名称
107 
108                             theDom.Obj = domObj;        //保存绑定的dom对象引用/指针
109                             theDom.bSet = true;        //设置绑定标志
110                             theDom.protertyName = fieldName;
111                             theDom.Obj[fieldName] = theVal;    //直接赋值
112 
113                             Object.defineProperty(this, sName, {
114                                 get: function() {    //重写读取访问器
115 
116                                     var theDom = doms[sName];
117                                     var reValue = null;
118 
119                                     console.log("get:");
120                                     console.log(this);
121                                     if(theDom.bSet){
122                                         reValue = theDom.Obj[fieldName];//直接读取dom对象的fieldName属性值
123                                     }
124                                     else{
125                                         reValue = vals[sName];        //未绑定使用保存值
126                                         console.log(sName + "属性未绑定,使用保存值");
127                                     }
128                                     return reValue;
129                                 },
130                                 set: function(val) {//重写设置访问器
131                                     var theDom = doms[sName];
132 
133                                     vals[sName] = val;//保存值
134                                     console.log("set:");
135                                     console.log(this);
136                                     if(theDom.bSet){
137                                         theDom.Obj[fieldName] = val;//直接设置dom对象的fieldName属性值
138                                     }
139                                     else{
140                                         console.log(sName + "属性未绑定,使用保存值");
141                                     }
142                                 }
143                             });
144                         }
145                     }
146                 }
147             }
148         }
149 
150         CBinding.prototype.binding = function(domObj) {    //定义绑定原型方法
151             this._binding(domObj);//调用定义的方法
152         };
153 
154         function bindingObj(domObj, dataObj) {    //dom对象和json对象绑定,返回新的绑定对象
155             var rObj = null;
156             if (null != domObj && null != dataObj) {
157                 if (null == dataObj.bInit) {
158                     rObj = new CBinding(dataObj);
159                 } else {
160                     rObj = dataObj;
161                 }
162                 rObj.binding(domObj);
163             }
164 
165             return rObj;
166         }
167 
168         function bd() {
169             var domName = document.getElementById("name");
170             var domAge = document.getElementById("age");
171 
172             console.log("原始json对象:");
173             console.log(theObj);
174 
175             theObj = bindingObj(domName, theObj);    //绑定name,把原来的json替换为CBinding对象
176             bindingObj(domAge, theObj);        //绑定age
177 
178             console.log("绑定后json对象:");
179             console.log(theObj);
180 
181             console.log("获取name和age:");
182             console.log(theObj.name);
183             console.log(theObj.age);
184 
185             //开始更改name和age
186             console.log("开始更改name和age:");
187             theObj.name = "testName";
188             theObj.age = 35;
189 
190             console.log("获取更改后的name和age:");
191             console.log(theObj.name);
192             console.log(theObj.age);
193 
194             console.log("测试是否能访问到CBinding的private变量vals:");
195             console.log(theObj.vals);//测试是否能访问到CBinding的private变量vals
196         }
197 
198         function setvalue() {
199 
200             var domName = document.getElementById("name1");
201             var domAge = document.getElementById("age1");
202 
203             theObj.name = domName.value;    //测试setter
204             theObj.age = domAge.value;    //测试setter
205         }
206 
207         function getvalue(){
208         
209             var domName = document.getElementById("name2");
210             var domAge = document.getElementById("age2");
211     
212             domName.value = theObj.name;    //测试getter
213             domAge.value = theObj.age;    //测试getter
214         }
215     </script>
216 </head>
217 
218 <body onload="bd()">
219     <table>
220         <tr>
221             <td>姓名</td>
222             <td><input id="name" /></td>
223         </tr>
224         <tr>
225             <td>年龄</td>
226             <td><input id="age" /></td>
227         </tr>
228         <tr>
229             <td>学号</td>
230             <td><input id="student_id" /></td>
231         </tr>
232         <tr>
233             <td>电话号码</td>
234             <td><input id="phoneNmb" /></td>
235         </tr>
236         <tr>
237             <td>设置姓名</td>
238             <td><input id="name1" /></td>
239         </tr>
240         <tr>
241             <td>设置年龄</td>
242             <td><input id="age1" /></td>
243         </tr>
244         <tr>
245             <td colspan="2"><button onclick="setvalue()">设置</button></td>
246         </tr>
247         <tr>
248             <td>读取姓名</td>
249             <td><input id="name2" readonly /></td>
250         </tr>
251         <tr>
252             <td>读取年龄</td>
253             <td><input id="age2" /></td>
254         </tr>
255         <tr>
256             <td colspan="2"><button onclick="getvalue()">读取</button></td>
257         </tr>
258     </table>
259 </body>
260 
261 </html>

把上面的代码保存为htm文件在浏览器中打开画面:

浏览器控制台打印画面:

下面是json对象没有绑定时在控制台打印出来的内容:

能看到其每个成员的值。

绑定后发现绑定的值(只绑定了name和age)看不见了。

获取name和age控制台打印:

 

前端设置name和age被绑定的两个文本框内容也跟着修改:

 

点击读取按钮,能获取到name和age


 

以上是我使用ECAMScript5 特性get/set访问器实现的json数据绑定到dom对象一个实现。还有待于改进。

当然了还是使用AngularJS来的高效,毕竟那么多年了,AngularJS已经很成熟了,大家都在用。我在这里只是从另一个角度来思考一下能否实现json和dom绑定的解决思路。

还希望技术大牛看到后多提宝贵意见和建议。