Reputation: 600
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
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
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
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