zoukankan      html  css  js  c++  java
  • js 实现angylar.js view层和model层双绑定(改变view刷新 model,改变model自动刷新view)

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

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

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

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

    (function(win){
        var doc = win.document,
            _bind_key = "data-bind-" ,
            _event_prefix = "message-",
            _data = {};//存放数据的空间
    
        /*生成唯一的gid*/
        var gid = (function(){
            var id =1;
            return function(){
                return "ng-"+(id++);
            }
        })();
    
        /*事件存放的容器*/
        var publisher = {
            callbacks : {},
            on : function( type , callback ){
                this.callbacks[type] = this.callbacks[type]||[];
                this.callbacks[type].push(callback);
            },
            fire : function( type ){
                var callback = this.callbacks[type]||[];
                for(var i=0,len= callback.length;i<len;i++){
                    callback[i].apply( this , arguments );
                }
            }
        };
    
    
        var user = {
            set : function( id, key , value){
                if(arguments<3){
                    return _data[object_id];
                }
                this.setData( id,key,value );
                publisher.fire(type,key, value);
            },
            setData : function( id, key , value ){
                _data[id][key] = value;
            }
        }
    
    
        /**
        * @param {Node} : elem  it can be a nodeList or one item
        * @param {Function} callback : //数据层,view层改变后会触发此函数
        */
        win.twoWayBind = function( elem ,callback){
            var id = gid();
            this.attr(elem ,_bind_key+id , true);
            this.propoties = {
                "id" : id,
                "data-attr" : _bind_key+id,
                "type" : "message-"+id
            }
    
            this.init( callback );
        }
    
        win.twoWayBind.prototype = {
            set : function(key , value){
                this.propoties[key] = value;
            },
            get : function(key){
                return this.propoties[key];
            },
            attr : function( elems , key, value ){
                //为elem元素设置相关属性
                if(!elems){return;}
                if(elems.nodeName || elems.nodeType===1){
                    elems = [elems];
                }
                for(var i =0 , len = elems.length;i<len;i++){
                    if( value ){
                        elems[i].setAttribute(key,value);
                    }else{
                        return elems[i].getAttribute(key);
                    }
                }
            },
            /**
            * @description 次函数用于外层调用,以实现改变数据的时候能监听器里面的函数
            */
            setData : function( key , value ){
                if(arguments.length<2){
                    return;
                }
                user.set( this.get('id'),key, value );
            },
            /*初始化*/
            init : function( callback ){
                var id = this.get('id');
                this.domBinder();
                this.dataBinder();
                if(callback){
                    publisher.on(this.get('type'), callback);
                }
            },
            domBinder : function(){
                var object_id = this.get('id'),
                    data_attr = this.get('data-attr');
                /*view监听的事件*/
                var handler = function( evt ){
                    var target = evt.target || evt.srcElement,
                        prop_name = target.getAttribute(data_attr);
    
                    if( prop_name ){
                        publisher.fire(type,prop_name,target.value);
                    }
                }
    
                if(doc.addEventListener){
                    doc.addEventListener('change' , handler , false );
                }else{
                    doc.attachEvent('change' , handler);
                }
    
                /*dom触发事件*/
                publisher.on(this.get('type') , function( evt , prop_name ,newValue , current ){
                    if(!evt){ return; }//如果触发此事件的目标不是一个元素//则不执行下面的代码
                    var elems = doc.querySelectorAll("["+data_attr+"]");
                    for(var i=0,len= elems.length;i<len;i++){
                        elem = elems[i];
                        if(elem.nodeName.toLowerCase()==="input" || elem.nodeName.toLowerCase()=="textarea"){
                            elem.value = newValue;
                        }else{
                            elem.innerHTML = newValue;
                        }
                    }
                });
            },
            dataBinder : function(){
                var object_id = this.get('id');
                _data[object_id] = _data[object_id] || {};
                var data_attr = _bind_key+object_id;
                type = "message-"+object_id;
    
                /*dom触发事件*/
                publisher.on(this.get('type') , function( evt , key ,value , isDataChange ){
                    user.setData(object_id,key,value);
                    console.log(evt);
                });
            }
        }
    
    })(window)

    完整代码如下:

    <!DOCTYPE HTML>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
    </head>
    <body>
    <input placeholder="按enter提交代码" type="text" id="text" />
    <div>
        <button value="1" onclick="setData(this)">设置字段value的值为1</button>
        <button value="2" onclick="setData(this)">设置字段value的值为2</button>
    </div>
    <ul id='list'></ul>
     <script type="text/javascript">
    (function(win){
        var doc = win.document,
            _bind_key = "data-bind-" ,
            _event_prefix = "message-",
            _data = {};//存放数据的空间
    
        /*生成唯一的gid*/
        var gid = (function(){
            var id =1;
            return function(){
                return "ng-"+(id++);
            }
        })();
    
        /*事件存放的容器*/
        var publisher = {
            callbacks : {},
            on : function( type , callback ){
                this.callbacks[type] = this.callbacks[type]||[];
                this.callbacks[type].push(callback);
            },
            fire : function( type ){
                var callback = this.callbacks[type]||[];
                for(var i=0,len= callback.length;i<len;i++){
                    callback[i].apply( this , arguments );
                }
            }
        };
    
    
        var user = {
            set : function( id, key , value){
                if(arguments<3){
                    return _data[object_id];
                }
                this.setData( id,key,value );
                publisher.fire(type,key, value);
            },
            setData : function( id, key , value ){
                _data[id][key] = value;
            }
        }
    
    
        /**
        * @param {Node} : elem  it can be a nodeList or one item
        * @param {Function} callback : //数据层,view层改变后会触发此函数
        */
        win.twoWayBind = function( elem ,callback){
            var id = gid();
            this.attr(elem ,_bind_key+id , true);
            this.propoties = {
                "id" : id,
                "data-attr" : _bind_key+id,
                "type" : "message-"+id
            }
    
            this.init( callback );
        }
    
        win.twoWayBind.prototype = {
            set : function(key , value){
                this.propoties[key] = value;
            },
            get : function(key){
                return this.propoties[key];
            },
            attr : function( elems , key, value ){
                //为elem元素设置相关属性
                if(!elems){return;}
                if(elems.nodeName || elems.nodeType===1){
                    elems = [elems];
                }
                for(var i =0 , len = elems.length;i<len;i++){
                    if( value ){
                        elems[i].setAttribute(key,value);
                    }else{
                        return elems[i].getAttribute(key);
                    }
                }
            },
            /**
            * @description 次函数用于外层调用,以实现改变数据的时候能监听器里面的函数
            */
            setData : function( key , value ){
                if(arguments.length<2){
                    return;
                }
                user.set( this.get('id'),key, value );
            },
            /*初始化*/
            init : function( callback ){
                var id = this.get('id');
                this.domBinder();
                this.dataBinder();
                if(callback){
                    publisher.on(this.get('type'), callback);
                }
            },
            domBinder : function(){
                var object_id = this.get('id'),
                    data_attr = this.get('data-attr');
                /*view监听的事件*/
                var handler = function( evt ){
                    var target = evt.target || evt.srcElement,
                        prop_name = target.getAttribute(data_attr);
    
                    if( prop_name ){
                        publisher.fire(type,prop_name,target.value);
                    }
                }
    
                if(doc.addEventListener){
                    doc.addEventListener('change' , handler , false );
                }else{
                    doc.attachEvent('change' , handler);
                }
    
                /*dom触发事件*/
                publisher.on(this.get('type') , function( evt , prop_name ,newValue , current ){
                    if(!evt){ return; }//如果触发此事件的目标不是一个元素//则不执行下面的代码
                    var elems = doc.querySelectorAll("["+data_attr+"]");
                    for(var i=0,len= elems.length;i<len;i++){
                        elem = elems[i];
                        if(elem.nodeName.toLowerCase()==="input" || elem.nodeName.toLowerCase()=="textarea"){
                            elem.value = newValue;
                        }else{
                            elem.innerHTML = newValue;
                        }
                    }
                });
            },
            dataBinder : function(){
                var object_id = this.get('id');
                _data[object_id] = _data[object_id] || {};
                var data_attr = _bind_key+object_id;
                type = "message-"+object_id;
    
                        /*dom触发事件*/
                publisher.on(this.get('type') , function( evt , key ,value , isDataChange ){
                    user.setData(object_id,key,value);
                    console.log(evt);
                });
            }
        }
    
    })(window)
     </script>
     <script type="text/javascript">
     var tipsContainer = document.getElementById('list');
     var binder = new twoWayBind( document.getElementById('text') ,function( e, key ,value ){
         var li = document.createElement('li');
         li.innerHTML = "当前input的值是:"+ value;
         tipsContainer.appendChild(li);
     });
     function setData(elem){
         var value = elem.value;
          binder.setData("key" , value);
     }
     </script>
    </body>
    </html>
    View Code

  • 相关阅读:
    array、vector、forward-list、list、deque的扩容,栈与队列,基于范围的for循环
    带默认参数值的函数
    :: 访问全局变量
    using来定义类的别名,typedef,#define
    const int* p
    内存
    变量的定义,强制类型转换
    HTTP请求流程(一)----流程简介
    DIY一个DNS查询器:程序实现
    详解C/C++函数指针声明
  • 原文地址:https://www.cnblogs.com/lztkiss/p/3380974.html
Copyright © 2011-2022 走看看