Knockout default text when `foreach` is empty
Asked Answered
P

3

15

Bearing in mind that similar questions have been answered here, I was wondering how one might go about having default text or HTML display inside a Knockout data-bind='foreach: list' whenever the list is empty.

The solutions on the linked page don't quite seem to line up with this, and in any case I thought of another way to try to accomplish this with a custom binding like this:

text.default = {
  update: function (element, valueAccessor) {
      var $e = $(element),
          obs = valueAccessor();

      function _check_blank() {
         // the element has content - so we do nothing
         if ($e.text().trim()) {
            return;
         }
         // the element is empty;
         $e.text("Default Text")
      }
      // we use setTimeout to ensure that any other bindings complete 
      // their update
      setTimeout(_check_blank, 0);
  }
}

This seems to work reasonably well with simple observables but it does not work with the foreach binding, though in any case I think the extender suggestion in the above link is probably preferable for a few reasons -- the above code would have a number of caveats. Nevertheless, I threw this example in here because it somewhat highlights an alternative and food for thought.

All that being said, I would like to know what options there might be for providing a default in lieu of foreach content.

One is to provide a wrapper in a simple if, like this:

<!-- ko if: xyz().length -->
   // foreach
<!-- /ko -->
<!-- ifnot: xyz().length -->
   // default text
<!-- /ko -->

However this is not particularly elegant -- lots of code clutter.

Profusive answered 31/5, 2013 at 19:54 Comment(1)
you can show some certain div based on if the list if empty or notTelespectroscope
H
32

Knockout gives you if and ifnot bindings. You just have to step back a bit from the element with foreach; Its insides are just for each element, so when there are none, there aren't any insides.

<div data-bind="if: pets().length > 0">These are the pets:</div>
<div data-bind="if: pets().length == 0">There aren't any pets. To add a pet...</div>
<div data-bind="foreach: pets">

Editorial: Your question is important because empty lists are an opportunity to say something instead of showing a blank slate.

Hammon answered 31/5, 2013 at 21:8 Comment(2)
Thanks Tom. That's a great link to the blank slate. The foreach binding is just a wrapper for the template binding; the content and tag itself can be changed by the template handler arbitrarily. You might also note the virtual elements in the question that use if and ifnot --- the optimistic aspiration of the question was to have a solution that avoided the clutter of tags where the information being represented is semantically and logically a discrete, indivisible unit (that happens to be - or have - a list). Cheers.Profusive
an other example could be <div data-bind="visible: pets.length == 0">There are no items.</div>Perpetual
P
5

The Knockout 3 extension Knockout Punches provides a default handler that can be used something like this:

<span data-bind="text: name | default:'Nobody'"></span>

More reading: KO Punches Documentation

Profusive answered 29/10, 2013 at 1:31 Comment(0)
D
2

I know you have asked it long ago, but maybe today it can help someone; If you do it with an observable array or dependent observable (like filter results from a list), the above solution will not work.

You can use this method to force KO to go through this observable and see if it has changed using "with". Inside that loop, you should check $data length, if it's 0, there is no data to loop on ;)

<!--ko with: xyz -->
  <div class="nodata" data-bind="visible:$data.length==0">
     Sorry, no data
  </div>
<!--/ko-->
Dimphia answered 27/9, 2016 at 21:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.