很easy的js雙向綁定框架(二):控制器繼承

初衷

上一篇已經實現了數據的雙向綁定,但model的控制範圍是整個文檔。在實際project中必須要有做用範圍,以便作ui模塊的拆分。
這一篇,咱們但願實現像angularjs同樣的控制器繼承:
1. 父controller的Model可以在子controller裏被訪問到
2. 子controller的model不影響父controller
3. controller繼承關係在html中指定,而不是js中指定javascript

目標

html裏,用isi-controller屬性去聲明控制器:html

<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屬性值同名的函數定義控制器:java

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

對用戶來講,僅僅要寫這些。就完事兒了。git

實現

版本號1

這個版本號採用最簡單直觀的思路:框架去找$(‘[isi-controller]’)的元素。而後給這些元素分別去綁定監聽器、運行控制器函數
代碼先列了:
index.html:angularjs

<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:github

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

SubController.js:markdown

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+'()');
    }
}

看看效果:
這裏寫圖片描寫敘述
悲劇了。沒有實現第二個初衷:子控制器不影響父控制器。
這個問題該怎樣解決呢?dom

版本號二,子不影響父

細緻看代碼。之因此會出現故障,是因爲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

相關文章
相關標籤/搜索