雙向數(shù)據(jù)綁定指的是將對(duì)象屬性變化綁定到UI,或者反之。換句話說,如果我們有一個(gè)擁有name屬性的user對(duì)象,當(dāng)我們給user.name賦予一個(gè)新值是UI也會(huì)相應(yīng)的顯示新的名字。同樣的,如果UI包括了一個(gè)輸入字段用來輸入用戶名,輸入一個(gè)新的值會(huì)導(dǎo)致user對(duì)象中的那么屬性發(fā)生變化。
許多流行的客戶端JavaScript框架例如Ember.js,AngularJS以及KnockoutJS都將雙向數(shù)據(jù)綁定作為自己的頭號(hào)特性。但是這并不意味著從零開始實(shí)現(xiàn)雙向數(shù)據(jù)綁定就很困難,同樣的當(dāng)我們需要雙向數(shù)據(jù)綁定時(shí)并不是只能夠選擇這些框架其中的一個(gè)。雙向數(shù)據(jù)綁定底層的思想非常的基本,它可以被壓縮成為三個(gè)步驟:
1.我們需要一個(gè)方法來識(shí)別哪個(gè)UI元素被綁定了相應(yīng)的屬性
2.我們需要監(jiān)視屬性和UI元素的變化
3.我們需要將所有變化傳播到綁定的對(duì)象和元素
雖然實(shí)現(xiàn)的方法有很多,但是最簡(jiǎn)單也是最有效的途徑是使用發(fā)布者-訂閱者模式。思想很簡(jiǎn)單:我們可以使用自定義的data屬性在HTML代碼中指明綁定。所有綁定起來的JavaScript對(duì)象以及DOM元素都將“訂閱”一個(gè)發(fā)布者對(duì)象。任何時(shí)候如果JavaScript對(duì)象或者一個(gè)HTML輸入字段被偵測(cè)到發(fā)生了變化,我們將代理事件到發(fā)布者-訂閱者模式,這會(huì)反過來將變化廣播并傳播到所有綁定的對(duì)象和元素。
使用jQuery的簡(jiǎn)單實(shí)現(xiàn)
使用jQuery來實(shí)現(xiàn)雙向數(shù)據(jù)綁定非常的直接且簡(jiǎn)單,因?yàn)檫@個(gè)流行的庫能夠是我們輕松的訂閱和發(fā)布DOM事件,以及我們自定義的事件:
function DataBinder(object_id){ //使用一個(gè)jQuery對(duì)象作為簡(jiǎn)單的訂閱者發(fā)布者 var pubSub = jQuery({}); //我們希望一個(gè)data元素可以在表單中指明綁定:data-bind-<object_id>="<property_name>" var data_attr = "bind-" + object_id, message = object_id + ":change"; //使用data-binding屬性和代理來監(jiān)聽那個(gè)元素上的變化事件 // 以便變化能夠“廣播”到所有的關(guān)聯(lián)對(duì)象 jQuery(document).on("change","[data-" + data_attr + "]",function(evt){ var input = jQuery(this); pubSub.trigger(message, [ $input.data(data_attr),$input.val()]); }); //PubSub將變化傳播到所有的綁定元素,設(shè)置input標(biāo)簽的值或者其他標(biāo)簽的HTML內(nèi)容 pubSub.on(message,function(evt,prop_name,new_val){ jQuery("[data-" + data_attr + "=" + prop_name + "]").each(function(){ var $bound = jQuery(this); if($bound.is("input,text area,select")){ $bound.val(new_val); }else{ $bound.html(new_val); } }); }); return pubSub;}在這個(gè)實(shí)驗(yàn)中可以按照以下代碼簡(jiǎn)單的實(shí)現(xiàn)一個(gè)User模型:
function User(uid){ var binder = new DataBinder(uid), user = { atttibutes: {}, //屬性設(shè)置器使用數(shù)據(jù)綁定器PubSub來發(fā)布變化 set: function(attr_name,val){ this.attriures[attr_name] = val; binder.trigger(uid + ":change", [attr_name, val, this]); }, get: function(attr_name){ return this.attributes[attr_name]; }, _binder: binder }; binder.on(uid +":change",function(vet,attr_name,new_val,initiator){ if(initiator !== user){ user.set(attr_name,new_val); } })}
新聞熱點(diǎn)
疑難解答
圖片精選