Jason
Jason

Reputation: 600

Delaying Knockout's visible binding?

I have a shopping cart. When it's empty, I want to show a div with a "Empty" message. When it has items in it, I want to show a div with the items. Currently, I have something like this...

<div style="display: none" data-bind="visible: items().length == 0">Empty</div>
<div style="display: none" data-bind="visible: items().length > 0">Not Empty</div>

...and this...

function ViewModel() {
    var self = this;

    self.items = ko.observableArray([]);
};

Now, I'm using AJAX to load in the items which means there is an initial period of uncertainty as to if there are or aren't items in the cart. This means the page starts by showing the "Empty" div and then quickly, upon seeing that there are items, replacing it with the "Not Empty" div—a process which looks clunky on screen.

How can I suspend the visible function on the div until my AJAX is done and I know the item count?

One thing I thought of (seen working here) is to create a separate property showEmpty...

<div style="display: none" data-bind="visible: showEmpty">Empty</div>
<div style="display: none" data-bind="visible: items().length > 0">Not Empty</div>

...and to set it to undefined by default. This means visible won't resolve until showEmpty is defined. I then define it within the AJAX call...

self.items = ko.observableArray([]);
self.showEmpty = ko.observable(undefined);

// Fake AJAX
setTimeout(function() {
    self.items([1]);        
    self.showEmpty(self.items().length == 0);
}, 2000);

This 100% works but it presents a new problem: I am now manually updating an observable. As seen below, when I want to add or remove items to/from my array, those functions must now worry themselves with updating the showEmpty property.

self.addItem = function() {
    self.items.push(1);
    self.showEmpty(false);
};

self.removeItem = function() {
    self.items.pop();
    self.showEmpty(self.items().length == 0);
};

I feel like the entire purpose of KO is to avoid this kind of thing. What I would like, then, is some way to set only an initial delay on the resolution of the div's visible function. After that initial wait, I would like the visible function to update based on the .length of an observable array.

Thanks!

Upvotes: 1

Views: 1870

Answers (3)

avaholic
avaholic

Reputation: 120

If you don't want to add an observable, you can move setTimeout outside your viewModel and then call the ajax function that is in your ViewModel from there.

var ViewModel = function() {
    // other code

    self.fake_ajax = function() {
    };
}

var vm = new ViewModel()
ko.applyBindings(vm);

setTimeout(function() {
    vm.fake_ajax();
}, 3000);

Here's a fiddle.

Upvotes: 0

Jason
Jason

Reputation: 600

@pquest gave me the solution in a comment so I'm putting it here in detail for other people to see later on.

As seen in my updated fiddle, I added a loadingDone observable and included a reference to it in the visible binding...

<div style="display: none" data-bind="visible: items().length == 0 && loadingDone">Empty</div>
<div style="display: none" data-bind="visible: items().length > 0">Not Empty</div>

In my script I was able to remove all of the manual updating...

function ViewModel() {
    var self = this;    

    self.items = ko.observableArray([]);
    self.loadingDone = ko.observable(false);

    // Fake AJAX
    setTimeout(function() {
        self.items([1]);        
        self.loadingDone(true);
    }, 2000);

    self.addItem = function() {
        self.items.push(1);
    };

    self.removeItem = function() {
        self.items.pop();
    };
};

Nice! I still wish I could accomplish the same without adding the additional observable but this is a lot better than what I had so I'm happy.

Upvotes: 3

M.Bush
M.Bush

Reputation: 1112

(Not enough rep to comment, sorry) Unless you absolutely HAVE to use knockout for your visibility, why not do it with jQuery instead using the .show() and .hide() functions for the div worked in to your AJAX. Check for the observable item length at the end of your AJAX call and .show() if its greater than 0, otherwise it stays hidden.. (unless you need that "empty" message there)

Upvotes: 0

Related Questions