Updating a computed observable only sometimes - ajax

I have a computed observable that makes AJAX calls based on other data (in a computed observable). The resulting data is used to populate part of the UI. Sometimes that part of the UI is hidden and I'd like to avoid the AJAX calls when it's hidden. Right now I have the following, but it updates whenever isVisible becomes true:
this.loadData = ko.computed(function() {
if (this.isVisible()) {
this.isProcessing(true);
var self = this;
$.when.apply($, ko.utils.arrayMap(this.parent.data.filteredSelectedDatasetLinks(), function(datasetLink) {
return $.ajax({
url: datasetLink.getDownloadUrl('.json'),
success: function(data) {
//... do stuff with the data
}
});
}))
.done(function() {
self.isProcessing(false);
});
}
}, this);
So obviously I need to split this up somehow, but I haven't figured out how to do it. To reiterate, when isVisible is false, no updates should happen. When isVisible is true, updates happen whenever filteredSelectedDatasetLinks changes. When isVisible becomes true, updates happen if filteredSelectedDatasetLinks changed while it was false.

Presumably you want to call your ajax when the filteredSelectedDatasetLinks is changed (and only if visible?). I think the best way to do this is to make that dependency explicit using the subscribe function... (I have simplified slightly and fixed issue with your final 'this')
this.filteredSelectedDatasetLinks.subscribe(function() {
if (this.isVisible()) {
this.isProcessing(true);
var self = this;
$.when.apply($, ko.utils.arrayMap(this.filteredSelectedDatasetLinks(), function(datasetLink) {
return $.ajax({
url: datasetLink.getDownloadUrl('.json'),
success: function(data) {
//... do stuff with the data
}
});
}))
.done(function() {
self.isProcessing(false);
});
}
}, this);
The issue with your original attempt is that ko.computed runs the function once and automatically works out which observables it needs to subcribe to. In your case this included the isVisible observable (which is not what you wanted). But making it explicit with the subscribe call directly you no longer have to worry about isVisible firing the callback.

Here is what I ended up using based on RP Niemeyer's comments.
this.trackData = ko.computed(function() {
this.parent.data.filteredSelectedDatasetLinks(); // for notification
this.isDataDirty(true);
}, this);
this.loadData = ko.computed(function() {
if (this.isVisible() && this.isDataDirty()) {
this.isDataDirty(false);
this.isProcessing(true);
var self = this;
$.when.apply($, ko.utils.arrayMap(this.parent.data.filteredSelectedDatasetLinks.peek(), function(datasetLink) {
return $.ajax({
url: datasetLink.getDownloadUrl('.json'),
success: function(data) {
//... do stuff with the data
}
});
}))
.done(function() {
self.isProcessing(false);
});
}
}, this);

Related

How to fadIn with ajax request?

I've this code, i'd like to replace the 'ul.hello-list' content with the answer datas. I'm trying
$.ajax({
type: "POST",
url: "home/hello.php",
data: {
"action": "hello_action"
},
success: function(datas) {
if (datas) {
$("ul.hello-list").fadeOut();
// It doesn't work
datas.fadeIn();
}
}
});
Thxs
So from your comment, I believe you can do:
success: function(datas) {
if (datas) {
var $newContent = $(datas).hide();
var $oldList = $("ul.hello-list");
var $container = $oldList.parent(); // Replace this with whatever your container is
$oldList.fadeOut();
$newContent.appendTo($container).fadeIn();
}
}
The issue is that datas is not inherently a jQuery object, so you cannot call jQuery functions on it. Furthermore, it needs to be attached to the DOM before it can be faded in.

Dirtychange change event in a formpanel

I have a formpanel, which displays information after clicking on a company in my grid.
this is the handler for my clickevent on the grid:
var onCompanyGridClickHandler = function (grid, rec) {
Ext.Ajax.request({
url: '../GetCompany',
params: { id: rec.get('id') },
success: function (res) {
//Fill Fields with values
companyFormValues = JSON.parse(res.responseText);
companyInfoFormPanel.getForm().setValues(companyFormValues);
}
});
};
So everytime I click on the grid, the form gets new values.
How can I implement a dirty change listener, which reminds me, when I changed a form value, to save the changes.
I tried to fire the isdirty on a beforeclick event on the grid, but it didn't work, and all I get is a dirtychange on every form that changes.
This should work.
var onCompanyGridClickHandler = function (grid, rec) {
form = companyInfoFormPanel.getForm();
if(!form.isDirty()){
Ext.Ajax.request({
url: '../GetCompany',
params: { id: rec.get('id') },
success: function (res) {
//Fill Fields with values
companyFormValues = JSON.parse(res.responseText);
companyInfoFormPanel.getForm().setValues(companyFormValues);
}
});
}
else{
Ext.Msg.alert("Warning", "Please save the data!!")
}
};
Simple Working fiddle for reference.
Was missing trackResetOnLoad:true
Updated Fiddle : Updated

Converting a series of synchronous AJAX requests to asynchronous requests

How can I make second AJAX request in the function below asynchronous instead of synchronous? result is a string that should start with 'start of string' and end with 'end of string' but in the middle of the string will be the results of an initial AJAX request that is being iterated.
Foo = {
foo: function() {
$(document).on("change", '.foo', function(e) {
e.preventDefault();
$.ajax({
url: "foo.php",
success: function(rows) {
$.each(rows, function() {
var result = 'start of string'; // START
$.ajax({
url: "bar",
async: false, // I DON'T want this
success: function(data) {
result += data; // MIDDLE
}
});
result += 'end of string'; // END
});
}
});
});
}
}
Thank you.
You can take advantage of jquery deferred objects. You can have two different ajax calls in different functions and then you can use .done() method to make sure you get the final string once both the ajax calls have been completed.
Read more here:
http://api.jquery.com/category/deferred-object/
http://api.jquery.com/deferred.done/
In jquery the way to chain asynchronous calls which are executed one after other, is using promise.then(), which was also called promise.pipe() in previous jquery versions.
Foo = {
foo: function() {
$(document).on("change", '.foo', function(e) {
e.preventDefault();
var param1 = {url: "foo.php"};
var param1 = {url: "bar"};
$.ajax(param1)
.then(function(rows) {
var result = 'start of string'; //START
var fn = function(data) {
result += data;
}
var last, first;
$.each(rows, function() { // rows should be iterable
if (!last) {
first = last = $.ajax(param2).done(fn)
} else {
last = last.then(function(res) {
return $.ajax(param2).done(fn);
});
}
});
last.done(fn).done(function() {
result += 'end of string'; // END
});
return first;
});
});
}
}
The easiest way to do this is indeed by using deferred objects, the example of your JavaScript using the jQuery when and done deferred statements;
$.when(ajaxCallOne()).done(ajaxCallTwo(rows));
function ajaxCallOne() {
return $.ajax({
url : "foo.php",
async: true
});
}
function ajaxCallTwo(rows) {
var result = 'start of string';
$.each(rows, function() {
$.ajax({
url : "bar",
async : true,
success : function(data) {
result += data; // MIDDLE
}
});
result += 'end of string'; // END
});
return result;
}
I'm not a JavaScript nor jQuery expert, but I think you should look at those deferred objects.
http://api.jquery.com/category/deferred-object/
http://api.jquery.com/jQuery.when/
I think you should look at the javascript promise design pattern
let me explain little to you:
This promise is a sort of proxy, representing the future result of the
operation. You would then register a callback on the promise, which
will be executed by the promise once the operation does complete and
the result is available.
Using jQuery you can define promise like this:
var promise = $.getJSON('url');
then using your promise variable you can define what to do when your request is done or failed or even do some function in case it failed or succeded.
promise.done(function(s) {alert('done successfully'); });
promise.fail(function(){ alert('get failed!'); });
promise.always(function(){ alert('this 'll executed anyway!'); });
there are plenty of tutorials on it
Promises and Deferred objects in jQuery
jQuery: Deferred Object
javascript promises
What is the benefit of a 'promise' abstraction in CommonJS?

Conditionally pause Javascript to wait for ajax

The variable ajaxdata is modified within the success function, if that hasn't been done yet, I would like to wait 2 seconds, then continue without it.
The use case is for a jqueryui autocomplete field. The autocomplete source is an ajax request, but if the user types quickly, and exits the field before the list loads, the field remains unset. Using the 'change' event on the autocomplete I check if the user entered a valid option without selecting it, but this doesn't work if the source hasn't loaded when the change event fires. So I would like to put a delay in the change function which waits, if the source (stored in the variable 'ajaxdata') is empty.
code:
input.autocomplete({
source: function (request, response){
$.ajax(
{
type: "GET",
url: "/some/url",
dataType: "json",
success: function(data){
response($.map(data,function(item){
return{
label: item.label,
value: item.value
}
}));
ajaxdata = data;
}
}
);
// ajaxopts = ajaxsource(request,response,ajaxurl,xtraqry)
},
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
var enteredString = $(this).val();
var stringMatch = false;
if (ajaxdata.length==0){
/// THIS IS WHERE I NEED A 2 SECOND DELAY
}
var opts = ajaxdata;
for (var i=0; i < opts.length; i++){
if(opts[i].label.toLowerCase() == enteredString.toLowerCase()){
$(this).val(opts[i].label);// corrects any incorrect case
stringMatch = true;
break;
}
}
}
},
});
Edit:
To be more specific about the problem: This delay needs to be conditional. Meaning that if the data is already loaded (either because it came from a static source, or from an earlier ajax call) I do not want to have a delay.
If I'm understanding you properly, I think you just want to check and see if ajaxdata has been populated; but if it hasn't, only wait two more seconds and then just proceed without it.
Try this:
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
if (ajaxdata.length==0){
/// THIS IS WHERE I NEED A 2 SECOND DELAY
//pass in 'this' so that you can use it
setTimeout(function() {correctCase(this);}, 2000);
}
}
}
. . . . .
function correctCase(inThis){
//I'm not sure what this variable does. do you really need it???
var stringMatch = false;
var enteredString = $(inThis).val();
//you still want to be sure that ajaxdata is not empty here
if (ajaxdata.length==0){
var opts = ajaxdata;
for (var i=0; i < opts.length; i++){
if(opts[i].label.toLowerCase() == enteredString.toLowerCase()){
$(inThis).val(opts[i].label); // corrects any incorrect case
stringMatch = true; //this variable doesn't seem to do anything after this???
break;
}
}
}
}
I'm not really sure what it is you're trying to do, but I'm pretty sure something like this would be a better way of doing it :
input.autocomplete({
source: function(request, response) {
return $.ajax({
type: "GET",
url: "/some/url",
dataType: "json"
});
},
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
var enteredString = this.value;
var stringMatch = false;
//make sure ajax is complete
this.source().done(function(data) {
var opts = $.map(data, function(item) {
return {
label: item.label,
value: item.value
}
});
for (var i = 0; i < opts.length; i++) {
if (opts[i].label.toLowerCase() == enteredString.toLowerCase()) {
$(this).val(opts[i].label); // corrects any incorrect case
stringMatch = true;
}
}
});
}
}
});​
By default, JavaScript is asynchronous whenever it encounters an async function, it queued that function for later.
But if you want a pause js(ajax call or anything) for you can do it use promises
Case 1: output hello(will not wait for setTimeout)
https://jsfiddle.net/shashankgpt270/h0vr53qy/
//async
function myFunction() {
let result1='hello'
//promise =new Promise((resolve,reject)=>{
setTimeout(function(){
resolve("done");
result1="done1";
}, 3000);
//});
//result = await promise
alert(result1);
}
myFunction();
case 2: output done1(will wait for setTimeout)
https://jsfiddle.net/shashankgpt270/1o79fudt/
async function myFunction() {
let result1='hello'
promise =new Promise((resolve,reject)=>{
setTimeout(function(){
resolve("done");
result1="done1";
}, 3000);
});
result = await promise
alert(result1);
}
myFunction();

backbone: issue an ajax call before resetting a collection

Right now I have a collection that fetches value, and after that every view attached to the reset event get rendered again
the problem is that I also have to issue another query to fetch the total number of records retrieved, and only after that ajax call is completed the reset event should be triggered
is more clear with a bit of code:
fetch: function() {
options = { data: this.getParams() };
this.fetch_total();
return Backbone.Collection.prototype.fetch.call(this, options);
},
fetch_total: function() {
var that = this;
var options = {
url: this.url + '/count',
data: this.getParams(),
contentType: 'application/json',
success: function(resp, status, xhr) {
that.total = parseInt(resp);
return true;
}
};
return $.ajax(options);
}
as you can see, I have to issue a get to localhost/myentity/count to get the count of entities...
The thing is I need the collection.total varaible to be updated before refreshing the views, that means I need both request, the GET to localhost/myentity and to localhost/myentity/count, to be completed before refreshing all the views...
any idea how can I achieve it???
If your $ of choice is jQuery>1.5, you could take advantage of the deferred object to manually trigger a reset event when both calls have completed. Similar to your answer, but a bit more readable and without chaining the calls:
fetch: function() {
options = {silent: true, data: this.getParams()};
var _this = this;
var dfd_total = this.fetch_total();
var dfd_fetch = Backbone.Collection.prototype.fetch.call(this, options);
return $.when(dfd_total, dfd_fetch).then(function() {
_this.trigger('reset', _this);
})
},
fetch_total: function() {
// what you have in your question
}
And a Fiddle simulating these calls http://jsfiddle.net/rkzLn/
Of course, returning the results and the total in one fetch may be more efficient, but I guess that's not an option.
I think #nikoshr's answer is a good one so that you don't have to modify your API. If you think that you want to lessen your calls to the server, then consider returning an object from that endpoint that has paging information.
{
count: 1243,
page: 3,
per_page: 10,
results: [
...
]
}
and then overriding the collection's parse functionality
parse: function(res) {
this.count = res.count;
this.page = res.page;
this.per_page = res.per_page;
// return the collection
return res.results;
}
RESOURCES
http://backbonejs.org/#Collection-parse
I think I found a way to do it. What I did was to silently fire the fetch call, without triggering the 'reset' event
There, from the callback, I issue the fetch of the total (GET to localhost/myentity/count)
and from the total callback, I finally trigge the reset event
in code is something like this:
fetch: function() {
var that = this;
options = {
// will manually trigger reset event after fetching the total
silent: true,
data: this.getParams(),
success: function(collection, resp) {
that.fetch_total();
}
};
return Backbone.Collection.prototype.fetch.call(this, options);
},
fetch_total: function() {
var that = this;
var options = {
url: this.url + '/count',
data: this.getParams(),
contentType: 'application/json',
success: function(resp, status, xhr) {
that.total = parseInt(resp);
// manually trigger reset after fetching total
that.trigger('reset', that);
return true;
}
};
return $.ajax(options);
}
This is my first attempt, I wonder if there's an easier way

Resources