很easy的js双向绑定框架(二):控制器继承

初衷

上一篇已经实现了数据的双向绑定,但model的控制范围是整个文档。在实际project中必需要有作用范围,以便做ui模块的拆分。
这一篇,我们希望实现像angularjs一样的控制器继承:
1. 父controller的Model能够在子controller里被訪问到
2. 子controller的model不影响父controller
3. controller继承关系在html中指定。而不是js中指定

目标

html里,用isi-controller属性去声明控制器:

<body>
    <div isi-controller="ParentController">
        <input data-bind="name">
        <div isi-controller="SubController">
            <input data-bind="name">
        </div>
    </div>
</body

希望上面的input name 改了。以下的会跟着变,而以下的变了,上面的不变。


js里,用和上面isi-controller属性值同名的函数定义控制器:

function ParentController() {
    var model = new Model();
    model.set('name', 'parent');
}
function ParentController() {
    var model = new Model();
    model.set('name', 'sub');
}

对用户来说,仅仅要写这些。就完事儿了。

实现

版本号1

这个版本号採用最简单直观的思路:框架去找$(‘[isi-controller]’)的元素。然后给这些元素分别去绑定监听器、运行控制器函数
代码先列了:
index.html:

<html>
  <head>
    <title>simple MVVM</title>
    <script src="js/ParentController.js"></script>
    <script src="js/SubController.js"></script>
    <script src="js/frame_v2.js"></script>
  </head>
  <body isi-controller="ParentController">
    <input type="text" data-bind="name">
    <div isi-controller="SubController">
      <input type="text" data-bind="name">
    </div>
  </body>
</html>

ParentController.js:

function ParentController() {
    var model = new Model();
    model.set('name', 'parent');
}

SubController.js:

function SubController() {
    var model = new Model();
    model.set('name','sub');
}

frame_v2.js: (对比上一篇,主要修改在绑监听器和new Model的自己主动化)

var pubsub = ... //见上一篇
var Model = ...  //见上一篇
// listener capture view changes --> publish model.change event
var changeHandler = function(event) {
    var target = event.target,
        propName = target.getAttribute('data-bind');
    if( propName && propName !== '' ) {
        pubsub.pub('model.change', propName, target.value);
    }
    event.stopPropagation();
}

/*----------- Init --------------*/
window.onload = function() {
    /* first step:
     * find controllers' dom
     */
    var controllerRanges = document.querySelectorAll('[isi-controller]');
    /* second step:
     * bind listeners for each controllers' range,
     * view.change event --> change each controllers' range
     */
    for(var i=0, len=controllerRanges.length; i<len; i++) {
        controllerRanges[i].addEventListener('change', changeHandler, false);
        // view.change event --> change view
        (function(index){
            pubsub.sub('view.change', function(propName, newVal) {
                var elements = controllerRanges[index].querySelectorAll('[data-bind=' + propName +']'),
                    tagName;
                for(var i=0,l=elements.length; i<l; i++) {
                    tagName = elements[i].tagName.toLowerCase();
                    if(tagName==='input' || tagName==='textarea' || tagName==='select') {
                        elements[i].value = newVal;
                    } else {
                        elements[i].innerHTML = newVal;
                    }
                }
            });
        })(i);
    }
     /* third step:
     * execute each controller function
     */
    for(var i=0, len=controllerRanges.length; i<len; i++) {
        var controllerName = controllerRanges[i].getAttribute('isi-controller');
        eval(controllerName+'()');
    }
}

看看效果:
这里写图片描写叙述
悲剧了。

没有实现第二个初衷:子控制器不影响父控制器。
这个问题该怎样解决呢?

版本号二,子不影响父

细致看代码,之所以会出现故障,是由于view.change信道的作用范围是有问题的。无论哪个model发出的view.change事件,两个控制器的view都会改变。
所以,我们给公布view.change事件的时候,多公布一个控制器名。好让接收view.change的时候知道应不应该修改html:

var Model = function(controllerName) {
    var model = {
        controllerName:controllerName,
        props: {},
        set: function(propName, value) {
            this.props[propName] = value;
            pubsub.pub('view.change', propName, value, this.controllerName); //就是这里!

} }

控制器里new Model的时候注意把controller的名字初始化进去:
ParentController.js:

function ParentController() {
    var model = new Model('ParentController');
    model.set('name', 'parent');
}

最后接收view.change信道消息的时候,推断下controllerName:

pubsub.sub('view.change', function(propName, newVal, controllerName) {
                ....
                thisControllerName = controllerRanges[index].getAttribute('isi-controller'),
                if(thisControllerName !== controllerName)
                    return;
                ....
}

当然。监听器公布model.change的时候也是一样。要把控制器名称公布出去。代码就不贴了。


再看看效果:
这里写图片描写叙述
妥了

全部代码:
github/victorisildur

原文地址:https://www.cnblogs.com/tlnshuju/p/7239139.html