【问题标题】:Can Knockout.js bindings apply to container tags AND descendants both?Knockout.js 绑定可以同时应用于容器标签和后代吗?
【发布时间】:2015-12-25 13:39:49
【问题描述】:

让我用一个简单的案例来设置问题。

我有一个 HTML 表格,其中的行由 observableArray 控制。效果很好。

但是,如果 observableArray 中有零个元素,我想要一行这样说。我尝试了这个标记,它“有点”有效:

<tbody data-bind="if: $root.data.contacts().length == 0">
    <tr>
        <td>There are no contacts specified yet.</td>
    </tr>
</tbody>

<tbody data-bind="foreach: $root.data.contacts">
        SNIP - a tbody with the rows is here when elements > zero
</tbody>

当我说“有点”时,我的意思是可见的。它确实出现在零元素处,并且确实像您所期望的那样在 > 零元素处消失。但是,当您打开 DOM 检查器(开发工具)并查看内存中的 DOM 时,您会发现有两个 tbody 部分,而不是一个。现在一个 tbody 当然总是空的,但是 两个 tbody 标签不是 HTML5 正确的,所以这个必须修复 这不是想要的标记.

作为一个 Knockout 新手,我尝试用一​​个虚拟元素来解决这个问题:

<!-- ko if: $root.data.contacts().length == 0 -->
<tbody>
    <tr>
        <td>There are no contacts specified yet.</td>
    </tr>
</tbody>
<!-- /ko -->

不幸的是,这对我们的构建过程不起作用:我们在压缩之前缩小 HTML,并且消除了 cmets。

我的印象是 KO 绑定适用于容器元素本身以及后代,但似乎并非如此。有没有办法告诉 KO 应用于容器元素以及子元素,或者我是否需要以某种方式更改标记而不是虚拟容器?

【问题讨论】:

  • 如果表中有两个tbody 标签,您会遇到什么问题?
  • 好吧,显然没有。我认为多个 元素是非法的,但阅读规范表明允许多个元素。我的问题仍然是关于绑定到容器的问题,但我对 HTML5 正确性的假设是错误的。我会更新。 w3.org/TR/html5/tabular-data.html#the-tbody-element
  • if 绑定控制包含的 元素,而不是绑定所在的元素。如果您检查现场演示,您可以在文档页面上看到这一点。 knockoutjs.com/documentation/if-binding.html 如果你想使用if,你可以有两个完整的表,每个表都包含在一个if-bound div中。
  • 这将始终产生两个 div,其中 div 是块级元素,可能会对标记产生负面影响,具体取决于用例。您在下面的答案中使用新的模板标记标记始终是干净的,没有孤立的元素。您可以在标记中使用单个可观察的字符串控件(可能很大)部分这一事实是设计的范式转变。

标签: javascript html knockout.js


【解决方案1】:

和你一样,我的第一选择是if 绑定的虚拟标签。但既然这不是一个选项,那么可交换模板呢?

var vm = {
  contacts: ko.observableArray()
};

ko.applyBindings(vm);

setTimeout(function() {
  vm.contacts(['One', 'Two', 'Three']);
}, 2500);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<template id="empty-body">
  <tbody>
    <tr>
      <td>There are no contacts specified yet.</td>
    </tr>
  </tbody>
</template>
<template id="normal-body">
  <tbody data-bind="foreach: contacts">
    <tr>
      <td data-bind="text:$data"></td>
    </tr>
  </tbody>
</template>
<table data-bind="template: contacts().length === 0 ? 'empty-body' : 'normal-body'"></table>

【讨论】:

  • 天哪!我大吃一惊@roy。真的很喜欢 +1 的想法(我对模板很难,但 swappable 模板是经典的)。干杯队友
  • @supercool 我们通常不会将模板名称视为变量,但它可以是可观察的。我已经用组件做过几次了。 stackoverflow.com/questions/31023028/…stackoverflow.com/questions/31860980/…
  • 一开始我不明白这一点,但8小时的睡眠对一切都有帮助! KO 模板文档中的相关部分是:“速记语法:如果您只提供一个字符串值,KO 会将其解释为要呈现的模板的 ID。”所以这就清楚地说明了为什么这个解决方案如此出色。荣誉@Roy - 这真是很棒的东西。主要问题的答案是“不,KO 只绑定到后代,没有办法让它不这样做”,以及“是的,有一种方法可以更改标记以产生所需的效果”。谢谢,很好的答案。
【解决方案2】:

Knockout-Repeat binding 将绑定应用于元素本身。它通过使用节点预处理器在运行时将带有repeat 绑定的元素包装在虚拟(基于注释的)元素中。

var vm = {
  contacts: ko.observableArray()
};

ko.applyBindings(vm);

setTimeout(function() {
  vm.contacts(['One', 'Two', 'Three']);
}, 2500);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script src="https://rawgit.com/mbest/knockout-repeat/master/knockout-repeat.js"></script>
<table>
  <tbody data-bind="repeat: !contacts().length && 1">
    <tr>
      <td>There are no contacts specified yet.</td>
    </tr>
  </tbody>
  <tbody data-bind="repeat: contacts().length && 1" data-repeat-bind="foreach: contacts">
    <tr>
      <td data-bind="text:$data"></td>
    </tr>
  </tbody>
</table>

【讨论】:

  • 这是一个非常有用的项目 - 感谢您发布它。事实上,我在质疑你的回答是否真的是对我问题的更好回答。在过去的几天里,模板翻转(当前选择的答案)并不是 100% 在公园里散步。模板翻转的期望是一次只有一个模板在 DOM 中(删除旧的,添加新的),但我已经证明实际上你不能指望它。由于订阅者执行的顺序不可控,因此可能会出现“添加新,删除旧”的情况,这可能会对绑定造成严重破坏。将研究并可能改变答案。
猜你喜欢
相关资源
最近更新 更多
热门标签