Multiple Knockout.js External Templates and Multiple VM Switching

I am using the following:

What I'm trying to achieve is the following:

  • The template container loads external HTML and loads a specific virtual machine for that HTML (works).
  • The template container loads / switches to another external HTML along with another specific virtual machine for this HTML (works).
  • The template container switches back to the first template / VM along with their VM (doesn't work!).

I can guess why it does not work, because the template is loading in front of the virtual machine (this gives me binding errors).

The structure of my site is similar to this (with the exception of the libraries mentioned above):

  • index.html (holds the template container)
  • js/script.js (Holds the main ViewModel)
  • js/firstvm.js (Holds the first ViewModel)
  • js/secondvm.js ( ViewModel)
  • tmpl/firstvm.html ( )
  • tmpl/secondvm.html ( )

.

:

  • index.html

    <button data-bind="click: loadFirstPage">Load first page + ViewModel</button>
    <button data-bind="click: loadSecondPage">Load second page + ViewModel</button>
    < hr />
    <div data-bind="template: { name: function() { return currentTemplate(); }, data: currentData }"></div>
    
  • script.js

    function IndexViewModel() {
    
        var vm = this;
    
        this.currentTemplate = ko.observable();
        this.currentData = ko.observable();
    
        this.loadFirstPage = function() {
            vm.currentTemplate("firstvm");
            vm.currentData(new FirstViewModel());
        };
    
        this.loadSecondPage = function() {
            vm.currentTemplate("secondvm");
            vm.currentData(new SecondViewModel());
        };
    
        this.loadFirstPage();
    };
    ko.applyBindings(new IndexViewModel());
    
  • firstvm.html

    <p data-bind="text: displayValue"></p>
    
  • secondvm.html

    <p data-bind="text: displayValue2"></p>
    
  • firstvm.js

    function FirstViewModel() {
        this.displayValue = ko.observable("Text from firstvm.js");
    };
    
  • secondvm.js

    function SecondViewModel() {
        this.displayValue2 = ko.observable("Text from secondvm.js");
    };
    

, - . !

Ps. : " " , (, ).

+5
2

, , , , , . . , :

:

<div data-bind="template: {name: currentTemplate().name(), 
                data: currentTemplate().data() }"></div>

ViewModel:

function TemplateViewModel(name, data) {
        this.name = ko.observable(name);
        this.data = ko.observable(data);
    };

    function IndexViewModel() {

        var vm = this;

        this.currentTemplate = ko.observable();

        this.loadFirstPage = function() {           
            vm.currentTemplate(new TemplateViewModel("firstvm", new FirstViewModel()));
        };

        this.loadSecondPage = function() {
            vm.currentTemplate(new TemplateViewModel("secondvm", new SecondViewModel()));
        };

        this.loadFirstPage();
    };
    ko.applyBindings(new IndexViewModel());

, . , , .

+7

, , . setTimeout, , . :

ko.bindingHandlers.widget = {
    'init': function(element, valueAccessor) {
        return { 'controlsDescendantBindings': true };
    },
    'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var templateName = valueAccessor().widgetTemplate();
        var dataValue = valueAccessor()
        var innerBindingContext = bindingContext['createChildContext'](dataValue);

        // This puts rendering of template to end of queue. 
        // This is to avoid binding errors while new template is assigned to old widget data
        setTimeout(function(){
            ko.renderTemplate(templateName, innerBindingContext, {}, element);
        }, 0);

    }
}

: 2.3 name

+1

All Articles