Implement simple js two-way data binding and js binding

Source: Internet
Author: User

Implement simple js two-way data binding and js binding

Bidirectional data binding refers to the ability to change the attributes of the bound object to the user interface, and vice versa. In other words, if we have a user object and a name attribute, once we assign a new value to user. name, a new name will be displayed on the UI. Similarly, if the UI contains an input box for entering the user name, entering a new value will change the name attribute of the user object accordingly.

Many popular JS framework clients such as Ember. js, Angular. js, or KnockoutJS have published two-way data binding on the latest features. This does not mean that it is very difficult to implement it from scratch, nor does it mean that when these functions are needed, using these frameworks is the only choice. The following idea is actually very basic and can be considered as a three-step plan:

We need a method to bind the UI elements and attributes to each other.
We need to monitor the changes in attributes and UI elements.
We need to make all bound objects and elements aware of changes.

There are still many ways to implement the above idea. A simple and effective method is to use the PubSub mode. This idea is simple: we use the data feature to bind HTML code. All JavaScript objects and DOM elements bound together will subscribe to a PubSub object. When a JavaScript object or an HTML input element monitors data changes, events bound to the PubSub object are triggered, and other bound objects and elements are changed accordingly.

Simple implementation with jQuery

For DOM Event subscription and publishing, jQuery is very simple to implement. Next we will use Jquery as follows:

function DataBinder( object_id ) { // Use a jQuery object as simple PubSub var pubSub = jQuery({}); // We expect a `data` element specifying the binding // in the form: data-bind-<object_id>="<property_name>" var data_attr = "bind-" + object_id,  message = object_id + ":change"; // Listen to change events on elements with the data-binding attribute and proxy // them to the PubSub, so that the change is "broadcasted" to all connected objects jQuery( document ).on( "change", "[data-" + data_attr + "]", function( evt ) { var $input = jQuery( this ); pubSub.trigger( message, [ $input.data( data_attr ), $input.val() ] ); }); // PubSub propagates changes to all bound elements, setting value of // input tags or HTML content of other tags 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, textarea, select") ) {  $bound.val( new_val );  } else {  $bound.html( new_val );  } }); }); return pubSub;}

For the above implementation, the following is the simplest implementation method of a User model:

function User( uid ) { var binder = new DataBinder( uid ),  user = {  attributes: {},  // The attribute setter publish changes using the DataBinder PubSub  set: function( attr_name, val ) {   this.attributes[ attr_name ] = val;   binder.trigger( uid + ":change", [ attr_name, val, this ] );  },  get: function( attr_name ) {   return this.attributes[ attr_name ];  },  _binder: binder  }; // Subscribe to the PubSub binder.on( uid + ":change", function( evt, attr_name, new_val, initiator ) { if ( initiator !== user ) {  user.set( attr_name, new_val ); } }); return user;}

Now, if we want to bind the User model attributes to the UI, we only need to bind the appropriate data features to the corresponding HTML elements.

// javascriptvar user = new User( 123 );user.set( "name", "Wolfgang" );// html<input type="number" data-bind-123="name" />

In this way, the input value is automatically mapped to the name attribute of the user object, and vice versa.

. This is a simple implementation!

JQuery implementation is not required

In most projects today, jQuery may have been used, so the above example is completely acceptable. However, if we need to try to do something to the other extreme and delete the dependencies on jQuery, what should we do? Well, it is not difficult to verify this (especially when we only support Internet Explorer 8 or a later version ). In the end, we must use general javascript to implement a customized PubSub and retain DOM events:

function DataBinder( object_id ) { // Create a simple PubSub object var pubSub = {  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 ( var i = , 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( evt ) {  var target = evt.target || evt.srcElement, // IE compatibility   prop_name = target.getAttribute( data_attr );  if ( prop_name && prop_name !== "" ) {   pubSub.publish( message, prop_name, target.value );  }  }; // Listen to change events and proxy to PubSub if ( document.addEventListener ) { document.addEventListener( "change", changeHandler, false ); } else { // IE uses attachEvent instead of addEventListener document.attachEvent( "onchange", changeHandler ); } // PubSub propagates changes to all bound elements pubSub.on( message, function( evt, prop_name, new_val ) { var elements = document.querySelectorAll("[" + data_attr + "=" + prop_name + "]"),  tag_name; for ( var i = , 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;  } } }); return pubSub;}

In addition to the jQuery trigger method called in the setter, the model can be the same. Calling the trigger method replaces the publish method that calls the PubSub with different features:

// In the model's setter:function User( uid ) { // ... user = { // ... set: function( attr_name, val ) {  this.attributes[ attr_name ] = val;  // Use the `publish` method  binder.publish( uid + ":change", attr_name, val, this ); } } // ...}

Once again, we achieved the desired results through a pure javascript code with less than one hundred lines, which can be maintained.

The above is a tutorial on js two-way data binding. I hope it will help you learn more.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.