js 实现angylar.js view层和model层双绑定(改变view刷新 model,改变model自动刷新view)

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

js 实现angylar.js view层和model层双绑定(改变view刷新 model,改变model自动刷新view)

如上图说的,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)

完整代码如下:

js 实现angylar.js view层和model层双绑定(改变view刷新 model,改变model自动刷新view)
<!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

你可能感兴趣的:(Model)