Can Knockout.js bindings apply to container tags AND descendants both?

自作多情 提交于 2019-12-18 09:32:01

问题


Let me setup the question with a simple case.

I have an HTML table, the rows of which are controlled by an observableArray. It works great.

If the observableArray has zero elements in it however, I want a single row to say so. I tried this markup, which "kind of" works:

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

When I say "kind of", I mean VISIBLY. It really does show up at zero elements and really does go away at > zero elements like what you would expect. However when you open the DOM inspector (dev tools) and look at the DOM in memory, you find that there are TWO tbody sections, not one. Now one tbody is always empty of course, but two tbody tags is not HTML5 correct, so this must be fixed this is not the desired markup.

Being a Knockout newbie, I tried to fix this problem with a virtual element:

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

Unfortunately this doesn't work for our build process: we minify HTML prior to compression and comments get eliminated.

I was under the impression that KO bindings applied to the CONTAINER ELEMENT ITSELF as well as descendants, but this seems to not be so. Is there a way to tell KO to apply to container elements as well as children, or do I need to change the markup in some way OTHER THAN a virtual container?


回答1:


Like you, my first choice would be virtual tags for an if binding. But since that's not an option, how about swappable templates?

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>



回答2:


The Knockout-Repeat binding applies the binding to the element itself. It does so by using a node preprocessor to wrap elements with the repeat binding in virtual (comment-based) elements at run time.

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>


来源:https://stackoverflow.com/questions/32820859/can-knockout-js-bindings-apply-to-container-tags-and-descendants-both

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!