4
votes

I'm using KnockoutJS and loading my view model via ajax. Before the load completes, I'd like to show a "Loading..." message, and if no data is loaded, I'd like to show a "No results." message. My initial attempt looks like this:

<ul data-bind="template: { name: 'mentions-template', foreach: mentions.data }">
    <li data-bind="visible: mentions.loaded() && mentions.data().length < 1">No mentions</li>
    <li data-bind="visible: !mentions.loaded()">Loading...</li>
</ul>


<script type="text/javascript">

    var viewModel = {
        mentions: {
            loaded: ko.observable(false),
            data: ko.observableArray()
        }
    }

    function loadData() {
       $.post(action, function(result) {
           viewModel.mentions.data = ko.mapping.fromJS(result);
           viewModel.mentions.loaded(true);
           ko.applyBindings(viewModel);
       });   
    } 

    ko.applyBindings(viewModel);
    loadData();    
</script>

I expected that the first li element would only show if viewModel.mentions.loaded was false and viewModel.mentions.data contained some items, and that the second li would show up until viewModel.mentions.loaded had been set to false, but both items are displayed at all times. What am I doing wrong?

2
I've had problems using ! in knockout bindings, have you tried == false instead? - sellmeadog
Just did, same result. It's like knockoutjs doesn't even touch the existing li elements when it does the binding. - Chris
Odd. Because I've had similar issues, I typically add a status property to my view model and bind it to a <span> in the ui; the actual message is determined in a ko.computable. - sellmeadog

2 Answers

5
votes

When you have these static items inside of the ul your list, they don't actually get bound, as Knockout just processes each item in the array.

One way to achieve what you want, would be to do:

<ul>
    <!-- ko template: { name: 'mentions-template', foreach: mentions.data } -->
    <!-- /ko -->
    <li data-bind="visible: mentions.loaded() && mentions.data().length < 1">No mentions</li>
    <li data-bind="visible: !mentions.loaded()">Loading...</li>
</ul>

Sample: http://jsfiddle.net/rniemeyer/gw7bM/

2
votes

I've had similar issues with unexpected behavior when using the visible binding, try something like this:

var ViewModel = {
  mentions: {
    loaded: ko.observable(),
    data: ko.observableArray(),
    status: ko.computed(function () {
      if (loaded() && data().length < 1)
        return 'No Mentions';

      else
        return 'Loading...';
    });
}

Update your view with a <span>:

<span data-bind="text: status, visible: loaded"></span>