Reputation: 747
I've a dropdown button which should be avaiable after ajax request will be finished.
<div class="form-input">
<label class="">Sort by:</label>
<select name="orderby" class="selectpicker" data-bind="value: sortBy, optionsCaption: 'Default', disable: waiting">
<option value="some_value">some_option</option>
<option value="some_value">some_option</option>
</select>
</div>
On page requested, it initially load data
$(function() {
//Initialization
var vm = new ArticleViewModel();
initialLoadArticles(vm);
ko.applyBindings(vm, $("#article-plugin")[0]);
});
function ArticleViewModel() {
var self = this;
//options =>
this.articles = ko.observableArray([]);
this.pageSize = 12;
this.sortBy = ko.observable('asc');
this.currentPage = ko.observable(1);
this.waiting = ko.observable(true);
this.totalPages = 0;
this.initMode = true;
this.timerId = null;
this.viewTemplate = ko.observable('listview-template');
if (this.viewTemplate() === "listview-template") {
this.pageSize = 4
} else {
this.pageSize = 12
};
this.sortBy.subscribe(function(event) {
console.log(event);
self.optionChanged();
loadArticles(self);
});
this.optionChanged = function() {
this.currentPage(1);
}
this.setCardView = function() {
self.viewTemplate('cardview-template');
loadArticles(self);
}
this.setListView = function() {
self.viewTemplate('listview-template');
loadArticles(self);
}
}
function initialLoadArticles(vm) {
vm.waiting(true);
var params = {
page: vm.currentPage(),
size: vm.pageSize,
sortby: vm.sortBy()
};
api.ajax.get(api.urls.article.getArticles, params, function(response) {
console.log('waiting: ' + vm.waiting());
if (response.success) {
vm.articles(response.data.items);
vm.waiting(false);
}
});
}
Well, on a page it display all articles, but dropdown button still blocked and I don't what exactly could be the problem of that.
Upvotes: 0
Views: 284
Reputation: 338208
I'd suggest a few changes to your viewmodel, featuring automatic loading via a subscription.
I think you always want to set waiting
to false after loading, independent of whether the request was a success or not. Also think about low-level request errors, you need to add a handler for those.
function ArticleViewModel() {
var self = this;
self.articles = ko.observableArray();
self.pageSize = ko.observable();
self.sortBy = ko.observable('asc');
self.currentPage = ko.observable();
self.waiting = ko.observable(true);
self.viewTemplate = ko.observable();
// API
self.setCardView = function() {
self.viewTemplate('cardview-template');
self.pageSize(12);
self.currentPage(1);
};
self.setListView = function() {
self.viewTemplate('listview-template');
self.pageSize(4);
self.currentPage(1);
};
// compute Ajax-relevant parameters
self.ajaxParams = ko.pureComputed(function () {
return {
page: self.currentPage(),
size: self.pageSize(),
sortby: self.sortBy()
};
}).extend({ rateLimit: { timeout: 10, method: 'notifyWhenChangesStop' } });
// auto-load when params change
self.ajaxParams.subscribe(function (params) {
self.waiting(true);
api.ajax.get(api.urls.article.getArticles, params, function (response) {
if (response.success) {
self.articles(response.data.items);
}
self.waiting(false);
});
});
// set inital view (also triggers load)
self.setListView();
}
$(function() {
var vm = new ArticleViewModel();
ko.applyBindings(vm, $('#article-plugin')[0]);
});
More strictly speaking, you I'd advice against true
or false
as the "loading" indicator. It's technically possible that more than one Ajax request is running and this would be a race condition. The first request that comes back resets the "loading" state, and the next one still overwrites the viewmodel data. Either use a counter, or prevent new requests while there is a pending one.
The rateLimit
extender makes sure that a rapid succession of changes to the parameters, like what happens when setListView()
is called, does not cause multiple Ajax requests.
If your Ajax requests are done by jQuery internally, I would suggest the following setup to be able to make use of the done
, fail
and always
promise handlers:
function ApiWrapper() {
var self = this;
function unwrapApiResponse(response) {
if (response.success) {
return new $.Deferred().resolve(response.data).promise();
} else {
return new $.Deferred().reject(response.error).promise();
}
}
self.getArticles = function (params) {
return $.get('articleUrl', params).then(unwrapApiResponse);
};
// more functions like this
}
var api = new ApiWrapper();
and in your viewmodel:
self.ajaxParams.subscribe(function (params) {
self.waiting(true);
api.getArticles(params).done(function (data) {
self.articles(data.items);
}).fail(function (err) {
// show error
}).always(function () {
self.waiting(false);
});
});
Upvotes: 2