Knockout.js多个外部模板&多个VM切换失败

时间:2012-07-31 13:31:30

标签: javascript templates mvvm knockout.js

我正在使用以下内容:

我想要实现的目标如下:

  • 模板容器加载外部HTML并为该HTML加载特定的VM(正常工作)。
  • 模板容器加载/切换到另一个外部HTML,以及该HTML的其他特定VM(正常工作)。
  • 模板容器切换回第一个模板/ VM及其VM(不起作用!)。

我猜它不起作用的原因,是因为模板在VM之前加载(它确实给我绑定错误)。

我的网站结构如下(不包括上述图书馆):

  • index.html(持有模板容器)
  • js/script.js(持有主ViewModel)
  • js/firstvm.js(拥有第一个ViewModel)
  • js/secondvm.js(持有第二个ViewModel)
  • tmpl/firstvm.html(第一个虚拟机的模板)
  • tmpl/secondvm.html(第二个虚拟机的模板)

Or simply download the source and view the problem.

最重要的部分:

  • 的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");
    };
    

我希望有人可以帮我解决这个问题。提前谢谢!

聚苯乙烯。忘记提及:当按下“第一页”按钮两次时,它似乎确实有效(可能是因为加载了正确的VM)。

2 个答案:

答案 0 :(得分:7)

所以看起来问题是名称和数据需要同时更改,因此模板不会绑定到尚未存在的viewmodel。有几种方法可以解决这个问题。一个是加载模板并保留它们,但你可以继续重新加载它们:

模板绑​​定:

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

视图模型:

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());

我测试了这个,它有效。你可能想稍微调整一下,但是你明白了。

答案 1 :(得分:1)

这已经回答了,但是我想分享我使用标准淘汰模板的解决方案。我创建了自定义绑定,并将包装模板渲染调用到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);

    }
}

注意:自knockout 2.3 name模板绑定also accept observable

以来