depend

standalone depend handler

by Jaak Kütt

HTML

<script src="http://knockoutjs.com/downloads/knockout-2.3.0.debug.js"></script>
<div class="root" data-bind="template: { name: function(data){return displayMode(data);}, foreach: items }">
    
</div>

<script type="text/html" id="default">
    <div class="item" data-bind="'if':id">
        <div data-bind="html:content"></div>    
        <div data-bind="depend: ['pos1']">
            <div class="red" data-bind="template: { name: function(data){return displayMode(data);}, foreach: children().pos1 }"></div>
        </div>
        <div>
            <div data-bind="depend: ['pos2']">
                <div class="green" data-bind="template: { name: function(data){return displayMode(data);}, foreach: children().pos2 }"></div>
            </div>
            <div data-bind="depend: ['pos3']">
                <div class="blue" data-bind="template: { name: function(data){return displayMode(data);}, foreach: children().pos3 }"></div>
            </div>            
        </div>
    </div>
</script>    

<script type="text/html" id="first">
    <div class="item fat" data-bind="'if':id">
        <div data-bind="html:content"></div>    
        <div data-bind="depend: ['pos2']">
            <div class="red" data-bind="template: { name: function(data){return displayMode(data);}, foreach: children().pos2 }"></div>
        </div>
    </div>
</script>

CSS

.item{border:1px solid black;padding:10px;margin:10px;}
.red {color :red;}
.green {color: green;}
.blue {color: blue;}
.fat{border-width:2px;}

JavaScript

window.displayMode=function (module){
    return (module.template() && document.getElementById(module.template()))?module.template():'default';
}
function ChildArray(){
    this.addChild=function(pdata,cdata){
        if(typeof pdata==='string'){
            if(!this.hasOwnProperty(pdata)) this[pdata]=ko.observableArray([]);
            if(!cdata) return;
            if(Object.prototype.toString.call(cdata) === '[object Array]'){
                for(c in cdata) this[pdata].push(cdata[c]);
            }else{
                this[pdata].push(cdata);
            }
        }else if(typeof pdata==='object'){
            for(p in pdata) this.addChild(p,pdata[p]);
        }
    }
}

ko.bindingHandlers.depend = {     
    'update': function(e, vA, aBA, vM, bC) {
        var i = ko.utils.unwrapObservable(vA());
        
        //make the following function extend template AND add a dependency check for positions used in template AND pass on a porper binding context!
        
        if(!bC.$data.hasOwnProperty(i[0]) || typeof bC.$data[i[0]] !== 'function'){
            var existing = bC.$data.children();
            existing[i[0]]=ko.observableArray([]);
            bC.$data.children(existing);            
        }
    }    
};

function ViewChild(id){
    this.id=ko.observable(id);
    this.content=ko.observable('initial content for '+id);
    this.children=ko.observable(new ChildArray);
    this.template=ko.observable();
}

var child1=new ViewChild(1);
var child2=new ViewChild(2);
var child3=new ViewChild(3);
var child4=new ViewChild(4);
var child5=new ViewChild(5);

var MyViewModel={};
MyViewModel.items=ko.observableArray([]);

MyViewModel.items().push(child1);
ko.applyBindings(MyViewModel);

child1.children().addChild('pos3',[child3,child4]);
child3.children().addChild({'pos2':child5,'pos1':[new ViewChild(6),new ViewChild(7)]});

setTimeout(function(){
    //child3.template('first');
},1000);