近段时间研究了下angular.js 觉得它内部实现的view和model层之间存在很微妙的关系,如下图

如上图说的,view的改变会update 数据层model, 数据层会update视图层view,这双方之所以能实现互相的监听,就是通过中间层(理解为监听层),代码初始化的时候就会将view和model的相关状态都保存在监听层里面(可以理解为保存一个handler的函数到监听层里面),

view和model的改变都触发监听器里面的绑定的handler,实现状态的共享;

个人简单实现了下,可能实现得不太好,望拍砖(没有实现啥兼容性的哈,只在chrome下运行),希望各位大神给点意见!!

  1. (function(win){
  2. var doc = win.document,
  3. _bind_key = "data-bind-" ,
  4. _event_prefix = "message-",
  5. _data = {};//存放数据的空间
  6.  
  7. /*生成唯一的gid*/
  8. var gid = (function(){
  9. var id =1;
  10. return function(){
  11. return "ng-"+(id++);
  12. }
  13. })();
  14.  
  15. /*事件存放的容器*/
  16. var publisher = {
  17. callbacks : {},
  18. on : function( type , callback ){
  19. this.callbacks[type] = this.callbacks[type]||[];
  20. this.callbacks[type].push(callback);
  21. },
  22. fire : function( type ){
  23. var callback = this.callbacks[type]||[];
  24. for(var i=0,len= callback.length;i<len;i++){
  25. callback[i].apply( this , arguments );
  26. }
  27. }
  28. };
  29.  
  30. var user = {
  31. set : function( id, key , value){
  32. if(arguments<3){
  33. return _data[object_id];
  34. }
  35. this.setData( id,key,value );
  36. publisher.fire(type,key, value);
  37. },
  38. setData : function( id, key , value ){
  39. _data[id][key] = value;
  40. }
  41. }
  42.  
  43. /**
  44. * @param {Node} : elem it can be a nodeList or one item
  45. * @param {Function} callback : //数据层,view层改变后会触发此函数
  46. */
  47. win.twoWayBind = function( elem ,callback){
  48. var id = gid();
  49. this.attr(elem ,_bind_key+id , true);
  50. this.propoties = {
  51. "id" : id,
  52. "data-attr" : _bind_key+id,
  53. "type" : "message-"+id
  54. }
  55.  
  56. this.init( callback );
  57. }
  58.  
  59. win.twoWayBind.prototype = {
  60. set : function(key , value){
  61. this.propoties[key] = value;
  62. },
  63. get : function(key){
  64. return this.propoties[key];
  65. },
  66. attr : function( elems , key, value ){
  67. //为elem元素设置相关属性
  68. if(!elems){return;}
  69. if(elems.nodeName || elems.nodeType===1){
  70. elems = [elems];
  71. }
  72. for(var i =0 , len = elems.length;i<len;i++){
  73. if( value ){
  74. elems[i].setAttribute(key,value);
  75. }else{
  76. return elems[i].getAttribute(key);
  77. }
  78. }
  79. },
  80. /**
  81. * @description 次函数用于外层调用,以实现改变数据的时候能监听器里面的函数
  82. */
  83. setData : function( key , value ){
  84. if(arguments.length<2){
  85. return;
  86. }
  87. user.set( this.get('id'),key, value );
  88. },
  89. /*初始化*/
  90. init : function( callback ){
  91. var id = this.get('id');
  92. this.domBinder();
  93. this.dataBinder();
  94. if(callback){
  95. publisher.on(this.get('type'), callback);
  96. }
  97. },
  98. domBinder : function(){
  99. var object_id = this.get('id'),
  100. data_attr = this.get('data-attr');
  101. /*view监听的事件*/
  102. var handler = function( evt ){
  103. var target = evt.target || evt.srcElement,
  104. prop_name = target.getAttribute(data_attr);
  105.  
  106. if( prop_name ){
  107. publisher.fire(type,prop_name,target.value);
  108. }
  109. }
  110.  
  111. if(doc.addEventListener){
  112. doc.addEventListener('change' , handler , false );
  113. }else{
  114. doc.attachEvent('change' , handler);
  115. }
  116.  
  117. /*dom触发事件*/
  118. publisher.on(this.get('type') , function( evt , prop_name ,newValue , current ){
  119. if(!evt){ return; }//如果触发此事件的目标不是一个元素//则不执行下面的代码
  120. var elems = doc.querySelectorAll("["+data_attr+"]");
  121. for(var i=0,len= elems.length;i<len;i++){
  122. elem = elems[i];
  123. if(elem.nodeName.toLowerCase()==="input" || elem.nodeName.toLowerCase()=="textarea"){
  124. elem.value = newValue;
  125. }else{
  126. elem.innerHTML = newValue;
  127. }
  128. }
  129. });
  130. },
  131. dataBinder : function(){
  132. var object_id = this.get('id');
  133. _data[object_id] = _data[object_id] || {};
  134. var data_attr = _bind_key+object_id;
  135. type = "message-"+object_id;
  136.  
  137. /*dom触发事件*/
  138. publisher.on(this.get('type') , function( evt , key ,value , isDataChange ){
  139. user.setData(object_id,key,value);
  140. console.log(evt);
  141. });
  142. }
  143. }
  144.  
  145. })(window)

完整代码如下:

  1. <!DOCTYPE HTML>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <input placeholder="按enter提交代码" type="text" id="text" />
  9. <div>
  10. <button value="1" onclick="setData(this)">设置字段value的值为1</button>
  11. <button value="2" onclick="setData(this)">设置字段value的值为2</button>
  12. </div>
  13. <ul id='list'></ul>
  14. <script type="text/javascript">
  15. (function(win){
  16. var doc = win.document,
  17. _bind_key = "data-bind-" ,
  18. _event_prefix = "message-",
  19. _data = {};//存放数据的空间
  20.  
  21. /*生成唯一的gid*/
  22. var gid = (function(){
  23. var id =1;
  24. return function(){
  25. return "ng-"+(id++);
  26. }
  27. })();
  28.  
  29. /*事件存放的容器*/
  30. var publisher = {
  31. callbacks : {},
  32. on : function( type , callback ){
  33. this.callbacks[type] = this.callbacks[type]||[];
  34. this.callbacks[type].push(callback);
  35. },
  36. fire : function( type ){
  37. var callback = this.callbacks[type]||[];
  38. for(var i=0,len= callback.length;i<len;i++){
  39. callback[i].apply( this , arguments );
  40. }
  41. }
  42. };
  43.  
  44. var user = {
  45. set : function( id, key , value){
  46. if(arguments<3){
  47. return _data[object_id];
  48. }
  49. this.setData( id,key,value );
  50. publisher.fire(type,key, value);
  51. },
  52. setData : function( id, key , value ){
  53. _data[id][key] = value;
  54. }
  55. }
  56.  
  57. /**
  58. * @param {Node} : elem it can be a nodeList or one item
  59. * @param {Function} callback : //数据层,view层改变后会触发此函数
  60. */
  61. win.twoWayBind = function( elem ,callback){
  62. var id = gid();
  63. this.attr(elem ,_bind_key+id , true);
  64. this.propoties = {
  65. "id" : id,
  66. "data-attr" : _bind_key+id,
  67. "type" : "message-"+id
  68. }
  69.  
  70. this.init( callback );
  71. }
  72.  
  73. win.twoWayBind.prototype = {
  74. set : function(key , value){
  75. this.propoties[key] = value;
  76. },
  77. get : function(key){
  78. return this.propoties[key];
  79. },
  80. attr : function( elems , key, value ){
  81. //为elem元素设置相关属性
  82. if(!elems){return;}
  83. if(elems.nodeName || elems.nodeType===1){
  84. elems = [elems];
  85. }
  86. for(var i =0 , len = elems.length;i<len;i++){
  87. if( value ){
  88. elems[i].setAttribute(key,value);
  89. }else{
  90. return elems[i].getAttribute(key);
  91. }
  92. }
  93. },
  94. /**
  95. * @description 次函数用于外层调用,以实现改变数据的时候能监听器里面的函数
  96. */
  97. setData : function( key , value ){
  98. if(arguments.length<2){
  99. return;
  100. }
  101. user.set( this.get('id'),key, value );
  102. },
  103. /*初始化*/
  104. init : function( callback ){
  105. var id = this.get('id');
  106. this.domBinder();
  107. this.dataBinder();
  108. if(callback){
  109. publisher.on(this.get('type'), callback);
  110. }
  111. },
  112. domBinder : function(){
  113. var object_id = this.get('id'),
  114. data_attr = this.get('data-attr');
  115. /*view监听的事件*/
  116. var handler = function( evt ){
  117. var target = evt.target || evt.srcElement,
  118. prop_name = target.getAttribute(data_attr);
  119.  
  120. if( prop_name ){
  121. publisher.fire(type,prop_name,target.value);
  122. }
  123. }
  124.  
  125. if(doc.addEventListener){
  126. doc.addEventListener('change' , handler , false );
  127. }else{
  128. doc.attachEvent('change' , handler);
  129. }
  130.  
  131. /*dom触发事件*/
  132. publisher.on(this.get('type') , function( evt , prop_name ,newValue , current ){
  133. if(!evt){ return; }//如果触发此事件的目标不是一个元素//则不执行下面的代码
  134. var elems = doc.querySelectorAll("["+data_attr+"]");
  135. for(var i=0,len= elems.length;i<len;i++){
  136. elem = elems[i];
  137. if(elem.nodeName.toLowerCase()==="input" || elem.nodeName.toLowerCase()=="textarea"){
  138. elem.value = newValue;
  139. }else{
  140. elem.innerHTML = newValue;
  141. }
  142. }
  143. });
  144. },
  145. dataBinder : function(){
  146. var object_id = this.get('id');
  147. _data[object_id] = _data[object_id] || {};
  148. var data_attr = _bind_key+object_id;
  149. type = "message-"+object_id;
  150.  
  151. /*dom触发事件*/
  152. publisher.on(this.get('type') , function( evt , key ,value , isDataChange ){
  153. user.setData(object_id,key,value);
  154. console.log(evt);
  155. });
  156. }
  157. }
  158.  
  159. })(window)
  160. </script>
  161. <script type="text/javascript">
  162. var tipsContainer = document.getElementById('list');
  163. var binder = new twoWayBind( document.getElementById('text') ,function( e, key ,value ){
  164. var li = document.createElement('li');
  165. li.innerHTML = "当前input的值是:"+ value;
  166. tipsContainer.appendChild(li);
  167. });
  168. function setData(elem){
  169. var value = elem.value;
  170. binder.setData("key" , value);
  171. }
  172. </script>
  173. </body>
  174. </html>

js 实现angylar.js view层和model层双绑定(改变view刷新 model,改变model自动刷新view)的更多相关文章

  1. MVC、MVP、MVVM、Angular.js、Knockout.js、Backbone.js、React.js、Ember.js、Avalon.js、Vue.js 概念摘录

    注:文章内容都是摘录性文字,自己阅读的一些笔记,方便日后查看. MVC MVC(Model-View-Controller),M 是指业务模型,V 是指用户界面,C 则是控制器,使用 MVC 的目的是 ...

  2. js设置自动刷新

    如何实现刷新当前页面呢?借助js你将无所不能. 1,reload 方法,该方法强迫浏览器刷新当前页面.语法:location.reload([bForceGet])   参数: bForceGet, ...

  3. js自动刷新页面代码

    <script language="JavaScript">function myrefresh(){window.location.reload();}setTime ...

  4. 读书笔记_MVC__关于通过js构建ORM,实现Model层

    最近一直在学习MVC构建富应用的WEB程序,自己一直对MVC的设计模式理解的不是十分透彻,终于在研读了github上Spine的源码之后,对构建Model层有了一点自己的理解. 本文仅为个人理解,如有 ...

  5. 再探 Ext JS 6 (sencha touch/ext升级版) 变化篇 (编译命令、滚动条、控制层、模型层、路由)

    从sencha touch 2.4.2升级到ext js 6,cmd版本升级到6.0之后发生了很多变化 首先从cmd说起,cmd 6 中sencha app build package不能使用了,se ...

  6. JS 点击按钮后弹出遮罩层,有关闭按钮

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <t ...

  7. 【Knockout.js 学习体验之旅】(3)模板绑定

    本文是[Knockout.js 学习体验之旅]系列文章的第3篇,所有demo均基于目前knockout.js的最新版本(3.4.0).小茄才识有限,文中若有不当之处,还望大家指出. 目录: [Knoc ...

  8. Angular.js vs Ember.js

    Angular.js 拥抱 HTML/CSS Misko Hevery(Angular.js的开发者之一)回答了这一问题,他的主要观点如下: 在HTML中加入太多逻辑不是好做法.Angular.js只 ...

  9. HTML5学习+javascript学习:打飞机游戏简介以及Model层

    本着好记性不如烂博客以及分享成功的喜悦和分享失败的苦楚,今天我来分享下一个练手项目:打飞机游戏~从小就自己想做游戏,可是一直没有机会.HTML5给了我们这个平台,这个平台可以有很多以前想都不敢想的东西 ...

随机推荐

  1. 分享一个导出Excel时页面不跳转的小技巧

    今天在点击客户档案导出的时候,发现先是打开了一个新标签,然后新标签自动关掉,弹出一个文件下载确认的窗口,点击确认后开始下载导出的Excel文件.这样的过程感觉窗口闪来闪去,而且可能会给用户带来困惑,是 ...

  2. DropdownList绑定的两种方法

    动态绑定方法一:动态绑定数据库中的字段. SqlConnection conn = UtilitySqlClass.OperateDataBase.ReturnConn();string strSQL ...

  3. ecmall二次开发 直接实例化mysql对象

    $db = &db(); // 第一步赋值数据库类库, $db->query(sql); // 第二步执行mysql 语句; 常用的数据库函数: 得到一行数据 $user=$db-> ...

  4. C++进制转换(十进制转二进制、八进制、随意进制)

    十进制转二进制: //十进制转二进制 #include<iostream> using namespace std; void printbinary(const unsigned int ...

  5. RotateAnimation详解

    其他构造器的旋转也可参考这副图. RotateAnimation旋转坐标系为以旋转点为坐标系(0,0)点.x轴为0度,顺时针方向旋转一定的角度.        1.RotateAnimation(fr ...

  6. wp上一款应用的出生与死亡

    起因 因为自己买了个wp手机,所以对于微软的这个wp系统还是非常喜欢,无奈软件质量不高,过年前便买了个wp的开发者帐号,不是很贵,还想着为wp的生态系统做点贡献.无奈工作繁忙,一直没有机会去做.但是自 ...

  7. Android短信监听软件

    本案例是在android手机中运行,是一个没有界面的短信监听软件.主要是用BroadcastReceiver来接受短信广播,当接收到短信后就跳转到service中来转发短信.哈哈,不是用来干坏事的.这 ...

  8. Device Pixel Ratio & Media Queries

    一些重要的名词解释: CSS pixels(CSS 像素):详见http://www.w3.org/TR/css3-values/#reference-pixe CSS声明的像素值,可随着放大缩小而放 ...

  9. 如何恢复 Linux 上删除的文件,第 1 部分

    来源:http://www.ibm.com/developerworks/cn/linux/l-cn-filesrc/ 原理及普通文件的恢复 要想恢复误删除的文件,必须清楚数据在磁盘上究竟是如何存储的 ...

  10. Linux 下五个顶级的开源命令行 Shell

    这个世界上有两种 Linux 用户:敢于冒险的和态度谨慎的. 其中一类用户总是本能的去尝试任何能够戳中其痛点的新选择.他们尝试过不计其数的窗口管理器.系统发行版和几乎所有能找到的桌面插件. 另一类用户 ...