【问题标题】:Knockout.js multiple external templates & multiple VM switching failsKnockout.js 多个外部模板和多个 VM 切换失败
【发布时间】:2012-07-29 06:56:12
【问题描述】:

我正在使用以下内容:

我想要达到的目标如下:

  • 模板容器加载外部 HTML 并为该 HTML 加载特定 VM(有效)。
  • 模板容器加载/切换到另一个外部 HTML,以及该 HTML 的另一个特定 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)。

【问题讨论】:

  • 您是否尝试过更改模板:{name: } 绑定以直接绑定到计算的或可观察的而不是函数? ko 应该自动评估这种情况下的变化。
  • @Nick 是的。可悲的是,这不起作用..
  • data-bind="template: { name: currentTemplate, data: currentData }" 为我工作。

标签: javascript templates mvvm knockout.js


【解决方案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.3namealso accept observable

【讨论】:

    【解决方案2】:

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

    模板绑​​定:

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

    我对此进行了测试,它有效。您可能想对其进行更多调整,但您明白了。

    【讨论】:

    • 是的 - 这就是诀窍!问题确实是模板和数据需要同时改变,但是我没有想到这个解决方案。如此简单但有效 - 谢谢!
    • 绝妙的解决方案!请注意,您不需要额外的 TemplateViewModel() 函数。您可以通过使用:vm.currentTemplate({ name: 'firstvm', data: new FirstViewModel() });. 使其更简单一些
    猜你喜欢
    • 2012-08-22
    • 1970-01-01
    • 2013-06-19
    • 1970-01-01
    • 2017-06-18
    • 1970-01-01
    • 2021-10-19
    • 2020-04-10
    • 1970-01-01
    相关资源
    最近更新 更多