[转]js实现简单的双向数据绑定

转自:http://www.see-source.com/blog/300000038/444

双向数据绑定指的就是,绑定对象属性的改变到用户界面的变化的能力,反之亦然。换种说法,如果我们有一个user对象和一个name属性,一旦我们赋了一个新值给user.name,在UI上就会显示新的姓名了。同样地,如果UI包含了一个输入用户姓名的输入框,输入一个新值就应该会使user对象的name属性做出相应的改变。

很多热门的JS框架客户端如Ember.jsAngular.js 或者KnockoutJS ,都在最新特性上刊登了双向数据绑定。这并不意味着从零实现它很难,也不是说需要这些功能的时候,采用这些框架是唯一的选择。下面的想法实际上很基础,可以被认为是3步走计划:

  1. 我们需要一个UI元素和属性相互绑定的方法
  2. 我们需要监视属性和UI元素的变化
  3. 我们需要让所有绑定的对象和元素都能感知到变化

还是有很多方法能够实现上面的想法,有一个简单有效的方法就是使用PubSub模式。 这个思路很简单:我们使用数据特性来为HTML代码进行绑定,所有被绑定在一起的JavaScript对象和DOM元素都会订阅一个PubSub对象。只要JavaScript对象或者一个HTML输入元素监听到数据的变化时,就会触发绑定到PubSub对象上的事件,从而其他绑定的对象和元素都会做出相应的变化。

用jQuery做一个简单的实现

对于DOM事件的订阅和发布,用jQuery实现起来是非常简单的,接下来我们就是用Jquery比如下面:

 1 function DataBinder( object_id ) {
 2   // Use a jQuery object as simple PubSub
 3   var pubSub = jQuery({});
 4  
 5   // We expect a `data` element specifying the binding
 6   // in the form: data-bind-<object_id>="<property_name>"
 7   var data_attr = "bind-" + object_id,
 8       message = object_id + ":change";
 9  
10   // Listen to change events on elements with the data-binding attribute and proxy
11   // them to the PubSub, so that the change is "broadcasted" to all connected objects
12   jQuery( document ).on( "change", "[data-" + data_attr + "]", function( evt ) {
13     var $input = jQuery( this );
14  
15     pubSub.trigger( message, [ $input.data( data_attr ), $input.val() ] );
16   });
17  
18   // PubSub propagates changes to all bound elements, setting value of
19   // input tags or HTML content of other tags
20   pubSub.on( message, function( evt, prop_name, new_val ) {
21     jQuery( "[data-" + data_attr + "=" + prop_name + "]" ).each( function() {
22       var $bound = jQuery( this );
23  
24       if ( $bound.is("input, textarea, select") ) {
25         $bound.val( new_val );
26       } else {
27         $bound.html( new_val );
28       }
29     });
30   });
31  
32   return pubSub;
33 }

对于上面这个实现来说,下面是一个User模型的最简单的实现方法:

 1 function User( uid ) {
 2   var binder = new DataBinder( uid ),
 3  
 4       user = {
 5         attributes: {},
 6  
 7         // The attribute setter publish changes using the DataBinder PubSub
 8         set: function( attr_name, val ) {
 9           this.attributes[ attr_name ] = val;
10           binder.trigger( uid + ":change", [ attr_name, val, this ] );
11         },
12  
13         get: function( attr_name ) {
14           return this.attributes[ attr_name ];
15         },
16  
17         _binder: binder
18       };
19  
20   // Subscribe to the PubSub
21   binder.on( uid + ":change", function( evt, attr_name, new_val, initiator ) {
22     if ( initiator !== user ) {
23       user.set( attr_name, new_val );
24     }
25   });
26  
27   return user;
28 }

现在我们如果想要将User模型属性绑定到UI上,我们只需要将适合的数据特性绑定到对应的HTML元素上。

JS代码:

1 var user = new User( 123 );
2 user.set( "name", "Wolfgang" );

HTML代码:

1 <input type="number" data-bind-123="name" />

不需要jQuery的实现

在如今的大多数项目里,可能已经使用了jQuery,因此上面的例子完全可以接受。不过,如果我们需要试着向另一个极端做,并且还删除对jQuery的依赖,那么怎么做呢?好,证实一下这么做并不难(尤其是在我们限制只支持IE 8及以上版本的情况下)。最终,我们必须使用一般的javascript实现一个定制的PubSub并且保留了DOM事件:

 1 function DataBinder( object_id ) {
 2   // Create a simple PubSub object
 3   var pubSub = {
 4         callbacks: {},
 5  
 6         on: function( msg, callback ) {
 7           this.callbacks[ msg ] = this.callbacks[ msg ] || [];
 8           this.callbacks[ msg ].push( callback );
 9         },
10  
11         publish: function( msg ) {
12           this.callbacks[ msg ] = this.callbacks[ msg ] || []
13           for ( var i = 0, len = this.callbacks[ msg ].length; i < len; i++ ) {
14             this.callbacks[ msg ][ i ].apply( this, arguments );
15           }
16         }
17       },
18  
19       data_attr = "data-bind-" + object_id,
20       message = object_id + ":change",
21  
22       changeHandler = function( evt ) {
23         var target = evt.target || evt.srcElement, // IE8 compatibility
24             prop_name = target.getAttribute( data_attr );
25  
26         if ( prop_name && prop_name !== "" ) {
27           pubSub.publish( message, prop_name, target.value );
28         }
29       };
30  
31   // Listen to change events and proxy to PubSub
32   if ( document.addEventListener ) {
33     document.addEventListener( "change", changeHandler, false );
34   } else {
35     // IE8 uses attachEvent instead of addEventListener
36     document.attachEvent( "onchange", changeHandler );
37   }
38  
39   // PubSub propagates changes to all bound elements
40   pubSub.on( message, function( evt, prop_name, new_val ) {
41     var elements = document.querySelectorAll("[" + data_attr + "=" + prop_name + "]"),
42         tag_name;
43  
44     for ( var i = 0, len = elements.length; i < len; i++ ) {
45       tag_name = elements[ i ].tagName.toLowerCase();
46  
47       if ( tag_name === "input" || tag_name === "textarea" || tag_name === "select" ) {
48         elements[ i ].value = new_val;
49       } else {
50         elements[ i ].innerHTML = new_val;
51       }
52     }
53   });
54  
55   return pubSub;
56 }

除了设置器里调用 jQuery的trigger方法外,模型可以保持一样。调用trigger方法将替代为调用我们定制的具有不同特征的PubSub的publish方法:

 1 // In the model's setter:
 2 function User( uid ) {
 3   // ...
 4  
 5   user = {
 6     // ...
 7     set: function( attr_name, val ) {
 8       this.attributes[ attr_name ] = val;
 9       // Use the `publish` method
10       binder.publish( uid + ":change", attr_name, val, this );
11     }
12   }
13  
14   // ...
15 }

再次说明一下,我们用一般的纯javascript的少于100行的维护代码获得了同样的结果。

转载于:https://www.cnblogs.com/zhixianyuanyangbuxianxian/p/4128018.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/420457.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

zookeeper作为服务注册中心

zk客户端消费者pom, 排除依赖yaml 入驻zk主启动配置restTemplate ribbon负载均衡调用服务提供者调用实现服务提供者pomyamlmainboot提供被调用的服务linux命令查看zk的服务&#xff0c;结点信息客户端消费者 pom, 排除依赖 <?xml version"1.0" encoding"…

写一个简单控制台启动的mcv服务

1.在项目工程里添加wcf服务&#xff0c;工程中会出现两个文件IService.cs和Service.cs. IService.cs中定义服务中的接口&#xff0c;接口必须加特性[OperationContract]才能在客户端添加服务后&#xff0c;实例化对象中发现该函数。 namespace ConsoleApplication1 { [ServiceC…

consul作为服务注册中心

consulconsul下载地址providerorderconsul 由go语言编写的一款优秀的服务注册中心应用。 https://www.consul.io/intro/index.html 中文文档 功能&#xff1a; 1. 服务发现 提供HTTP和DNS两种发现方式 2. 健康监测 支持多种协议&#xff0c;HTTP、TCP、Docker、Shell脚本定制…

ribbon, restTemplate 负载均衡服务调用

ribbonribbon conceptribbon核心组件IRule模仿源码重写轮询ribbon concept spring cloud ribbon 是基于Netflix ribbon实现的一套客户端负载均衡的工具。 简单的说&#xff0c; Ribbon是Netflix发布的开源项目&#xff0c; 主要功能是提供客户端的软件负载均衡算法和服务 完善的…

网页排版出现空白

网页顶部多了一个空白行&#xff0c;甚至引起了式样的错乱&#xff0c;样式都设置好了&#xff0c;可还是有空白行。很是奇怪&#xff0c;后来搜索了一下&#xff0c;找到原因了。就是因为页面编码时加了BOM&#xff0c;才导致的空白行&#xff0c;怎么也去不掉。 分析原因&…

openfeign 负载均衡调用服务

https://github.com/spring-cloud/spring-cloud-openfeign openFeign, fegin &#x1f446;open feginopenFegin使用日志打印配置类open fegin Feign是一个声明式的web服务客户端&#xff0c;让编写web服务客户端变得非常容易&#xff0c;只需创建一个接口并在接口上添加注解即…

MVC中使用ajax传递json数组

解决方法 去www.json.org下载JSON2.js再调用JSON.stringify(JSONData)将JSON对象转化为JSON串。var people [{ "UserName": "t1", "PassWord": "111111", "Sex": "男" }, { "UserName": "t2"…

hystrix断路器引发的相关概念

1 分布式系统面临的问题 服务雪崩 多个微服务之间调用的时候&#xff0c;假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的"扇出”&#xff0c;如果扇出的链路上某个微服务的调用响应时间过长或者不可用&#xff0c;对微服务A的调用就会…

ABAP 弹出框自建内容POPUP

可以自己设计内容&#xff0c;仅供参考。转载于:https://www.cnblogs.com/matinal/p/4135269.html

前端学习(1287):node.js的组成

var first hello nodejs; console.log(first);function fn() {console.log("fn函数被调用了"); }fn();for (var i 0; i < 5; i) {console.log(i); }if (true) {console.log(123); } 运行结果

gateway路由网关,zuul的替代品

zuul1 low, zuul2 还没长大呢&#xff0c;不敢用。 gateway 基于netty, spring flux, reactor 异步非阻塞, 快呀。 与spring良好整合, spring社区的呢。官方推荐。https://spring.io/projects/spring-cloud-gateway https://cloud.spring.io/spring-cloud-static/spring-cloud-…

ASP.NET MVC 5 入门教程 (3) 路由route

文章来源&#xff1a; Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc-5-get-started-route.html 上一节&#xff1a;ASP.NET MVC 5 入门教程 (2) 控制器Controller 下一节&#xff1a;ASP.NET MVC 5 入门教程 (4) View和ViewBag 源码下载&#xff1a;点我下载 上一节我…

spring cloud config 配置中心

/(ㄒoㄒ)/~~ 还有好多要学鴨 cloud config分布式面临的系统配置问题&#xff0c;如何解决分为服务端配置与客户端配置客户端不能自动更新git上的配置分布式面临的系统配置问题&#xff0c;如何解决 1. 分布式系统面临的配置问题 微服务意味着要将单体应用中的业务拆分成一个个…

前端学习(1289):nodejs模块化的开发规范

demo02.js const add (n1, n2) > n1 n2exports.add add; demo03.js const a require(./demo02.js); console.log(a); 运行结果

spring cloud bus消息总线

解决的痛点: 当git仓库的配置更新后&#xff0c; cloud config 客户端不能获取到配置信息的问题, 需要手动发送请求&#xff0c;刷新配置。 可以参照 spring cloud config cloud busbus消息总线使用rabbitMQ推送消息原理架构实现使用curl命令刷新客户端的配置bus bus配合conf…

前端学习(1290):nodejs模块化的开发导出另一种方式

demo04.js const greeting _name > hello ${_name};module.exports.greeting greeting; demo05.js const a require(./demo04.js); console.log(a); console.log(a.greeting(geyao)); 运行结果 demo04.js const greeting _name > hello ${_name}; const x 100;…

c++ 匿名函数

#include<iostream> #include <cstdlib> using namespace std;int main() {//为了在以后便于区分&#xff0c;我这段main()代码叫做main1autofunc [] { printf("%d\n",1989); };func();system("pause");return 0; } 转载于:https://www.cnblo…