Angular implements a two-way binding mechanism. The so-called two-way binding is nothing more than reflecting data in real time from interface operations, and real-time display of data changes to the interface. This article describes js two-way Data Binding in detail. If you are interested, mutual 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-
="
" 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
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.