1

我正在使用 KO 外部模板引擎将我的单页 webapp 分解为多个文件,但我正在加载的模板仅包含 KO 标记。

尽管对该主题进行了广泛研究,但我无法弄清楚如何将 KO 绑定可靠地应用于正在加载的模板。

关键点是:

  • 模板定义的 DOM 元素最初不存在
  • 我的模板绑定是动态的,所以我不认为 KO 在将绑定应用于整个文档时会请求模板
  • 我不想让模板加载同步
  • pb 变得更加棘手,因为注入器可能正在发生一些模板缓存,这导致 KO 抱怨我应用了两次绑定。

这是我的代码,它至少有两个问题:

  • 无法保证模板已完成加载,因为我正在调用 applyBindings
  • KO 抱怨我在来回导航时尝试重新应用绑定

在将外部模板节点添加到 DOM 时,有什么建议可以使用一种干净、可靠的方法将绑定一次且仅一次应用于外部模板节点?

  • index.html中:

    <div id="templateDiv" data-bind="template: { name: currentView() }"></div>
    
  • main.js 中

    function AdminViewModel() {
         var self = this;
         self.currentView = ko.observable('adminHome');
     }
    
    var viewModel = new AdminViewModel();
    
    var SammyApp = $.sammy('#admin_content', function() {
      //...
      this.get('#/editMembers', function(context) {
          viewModel.currentView('editMembers');
          ko.applyBindings(viewModel, $('.ko-template').get(0));
      });
    };
    
    ko.applyBindings(viewModel);
    
4

4 回答 4

1

如果我明白你的意思,那么你可以这样做

使用 get 将模板加载到 js 中的一个基本文件中并分配给脚本标记,如下所示

var script   = document.createElement("script");
               script.id  = "YourTemplateName";
               script.type  = "text/html";
               script.text  = result.Value; //template data
               document.body.appendChild(script);

并在您的 index/base html 文件中分配模板

<div id="OtherTemplateDiv" data-bind="template: { name: 'YourTemplateName' }">
            </div>
于 2014-06-19T05:43:23.517 回答
0

我为 KO 制作了一个模板引擎

https://github.com/AndersMalmgren/Knockout.Bootstrap.TemplateStore/wiki

它需要一个启用 Owin 的 Web 服务器,一旦配置它就知道一个名为的 ViewModelFooViewModel应该连接到名为的 ViewFooView

使用 nuget 安装(对于 ASP.NET)

Install-Package Knockout.Bootstrap.TemplateStore.SystemWeb

它还被设计成易于在 SPA 中使用

演示 https://github.com/AndersMalmgren/Knockout.Bootstrap.Demo

于 2014-06-19T07:19:29.610 回答
0

这不是关于如何使用模板的解决方案,但这是我使用with binding拆分为文件的方式。

假设你有一个这样的页面:

<div data-bind="with: block1">
    <input data-bind="value: yourFirstInput" />
    <!-- more markeup -->
</div>
<div data-bind="with: block2">
    <select data-bind="options: dropDownlist2options"></select>
    <!-- more markeup -->
</div>

您可以将block1文件(我使用ascxblock2放入另一个文件中。
然后在你的视图模型中你有类似的东西:

var viewmodel = function () {
    var self = this;
    this.block1 = ko.observable();
    this.block2 = ko.observable();
}
var vm = new viewmodel();
ko.applyBindings(vm);

这样您就可以在整个页面上应用Bindings。将with binding处理空/未定义对象的检查,并仅在对象被实例化时显示块。

然后,当您想要显示时,您可以执行以下操作block1

vm.block1({ yourFirstInput: ko.observable('aa')});

实际上我是用映射插件来做的,这只是一个例子。

现场演示(注意在 setTimeout 之后 block1 是如何出现的)

于 2014-06-19T05:36:48.370 回答
0

我想出了一些可以使用 afterRender 回调的东西......虽然恕我直言有点混乱,但欢迎改进。

由于某种原因,afterRender 回调被调用了两次,第二次使用空对象,因此测试 hasOwnProperty('nodeType')。

isBound() 检查绑定是否已应用于元素 - 在应用绑定后尝试向元素添加自定义标记 CSS ('ko-applied') 类不能可靠地工作。

不确定是否确实需要复制 koElements 数组,但如果没有,我会在循环中得到未定义的元素 [i],因此模板加载器可能会在 afterRender 运行时异步更新数组。

<div id="templateDiv" data-bind="template: { name: currentView(), afterRender: applyTemplateBindings }"></div>

self.applyTemplateBindings = function(koElements) {
            var elements = koElements.slice();
            for (var i = 0, len = elements.length; i < len; i++) {
                var element = elements[i];
                if (element.hasOwnProperty('nodeType') && ! $(element).hasClass('infuser-loading') &&
                    ! isBound(element)) {
                    ko.applyBindings(self, element);
                }
            }
        };

        var isBound = function(node) {
            return !!ko.dataFor(node);
        };
于 2014-06-19T08:23:30.400 回答