Bidirectional data binding refers to the binding of object property changes to the UI, or vice versa. In other words, if we have a user object with the name attribute, when we assign a new value to User.Name, the UI will display the new name accordingly. Similarly, if the UI includes an input field to enter a user name, entering a new value causes the property to change in the user object.
The idea of two-way data binding is very basic, and it can be compressed into three steps:
- We need a way to identify which UI element is bound to the corresponding property
- We need to monitor changes in properties and UI elements
- We need to propagate all the changes to the bound objects and elements
Although there are many ways to implement it, the simplest and most effective way is to use the Publisher-Subscriber pattern. The idea is simple: we can use the custom data property to indicate the binding in the HTML code. All bound JavaScript objects and DOM elements are "subscribed" to a Publisher object. At any time, if a JavaScript object or an HTML input field is detected to have changed, we will delegate the event to the Publisher-Subscriber pattern, which in turn broadcasts the change and propagates it to all bound objects and elements. Many people here naturally think of using jquery, using Dom's event actions to listen for UI changes, then modify the corresponding data fields, and then use the on
change to listen for data fields from the definition event to broadcast the changes to all bound objects and elements. This article mainly discusses the use of JavaScript to achieve two-way data binding, if you are interested in jquery implementation can refer to-javascript for simple two-way data binding.
Here we just make a slight modification to the previous observer pattern, plus the event listener for DOM elements:
functionDataBinder (object_id) {//Create a simple PubSub object varPubSub ={callbacks: {}, on:function(msg,callback) { This. callbacks[msg] = This. callbacks[msg] | | []; This. Callbacks[msg].push (callback); }, Publish:function(msg) { This. callbacks[msg] = This. callbacks[msg] | | []; for(vari = 0,len = This. callbacks[msg].length; i < Len; i++) { This. callbacks[msg][i].apply ( This, arguments); }; }}, Data_attr= "data-bind-" +object_id, Message= object_id + ": Change", Changehandler=function(event) {vartarget = Event.target | | Event.srcelement,//IE8 compatibleProp_name =Target.getattribute (data_attr); if(Prop_name && prop_name!== "") {pubsub.publish (message,prop_name,target.value); } }; //Monitor event changes, and proxy to PubSub if(Document.addeventlistener) {Document.addeventlistener ("KeyUp", Changehandler,false); } Else{ //IE8 use attachevent instead of AddeventlistenterDocument.attachevent ("onkeyup", Changehandler); }; //PubSub propagate changes to all binding elementsPubsub.on (Message,function(event,prop_name,new_val) {varelements = Document.queryselectorall ("[" + data_attr + "=" +prop_name + "]"), tag_name; for(vari = 0,len = Elements.length; i < Len; i++) {tag_name=elements[i].tagname.tolowercase (); if(tag_name = = = "Input" | | tag_name = = = "TextArea" | | tag_name = = = "Select") {Elements[i].value=New_val; } Else{elements[i].innerhtml=New_val; }; }; }) returnPubSub;}
Then define the model on the line:
functionUser (UID) {varBinder =NewDataBinder (UID), user={attribute: {},//The property set uses the data binder pubsub to publishSet:function(attr_name,val) { This. attribute[attr_name] =Val; Binder.publish (UID+ ": Change", Attr_name,val, This); }, get:function(attr_name) {return This. Attribute[attr_name]; }, _binder:binder}; Binder.on (UID+ ": Change",function(event,attr_name,new_val,initiator) {if(Initiator!==user) {User.set (attr_name,new_val); } }); returnuser; }
It's very simple to use, just create a new model and set the field through the model.
var New User (123 ); " Name "," Tsy ");
JavaScript for simple two-way binding