【问题标题】:How do you use native Knockout templates with the KO external template engine如何使用带有 KO 外部模板引擎的原生 Knockout 模板
【发布时间】:2014-08-09 09:56:35
【问题描述】:

我正在使用 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);
    

【问题讨论】:

    标签: javascript jquery knockout.js knockout-templating


    【解决方案1】:

    这不是关于如何使用模板的解决方案,但这是我使用 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放在一个文件里(我用ascx),block2放在另一个文件里。
    然后在你的视图模型中你有类似的东西:

    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')});
    

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

    Live demo(注意在 setTimeout 之后 block1 是如何出现的)

    【讨论】:

    • 嗯,问题是我可能有超过 50 个外部模板,所以我不想在调用 applyBindings() 之前将它们全部加载。您的小提琴有效,因为所有 DOM 节点(主文档和所有模板)都是在调用 applyBindings 之前创建的,不是吗?
    • 实际上重要的是在调用相关 DOM 元素上的 applyBindings 之前定义块。您可以在之后加载标记,但您必须在其上调用 applyBindings (ko.Applybindings(vm, domElement))
    【解决方案2】:

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

    使用 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>
    

    【讨论】:

    • 嗯,我已经弄清楚了。问题在于在对 applyBindings 进行初始调用后加载的外部模板(例如,在应用程序导航 1500 万次后,用户突然导航到加载新模板的视图)。
    • 试试这样的 ko.applyBindings(objectLateLoaded, $("#DivIdInWhichTemplateAdded")[0]);
    【解决方案3】:

    我已经为 KO 做了一个模板引擎

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

    它需要一个启用 Owin 的 Web 服务器,一旦配置它就知道名为 FooViewModel 的 ViewModel 应该连接到名为 FooView 的视图

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

    Install-Package Knockout.Bootstrap.TemplateStore.SystemWeb
    

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

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

    【讨论】:

      【解决方案4】:

      我想出了一些可以使用 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);
              };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-03-05
        • 1970-01-01
        • 2014-01-02
        • 1970-01-01
        • 2018-07-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多