Is it possible to set a Knockout dependent observable with AJAX POST - asp.net-mvc-3

Using Knockout 2.0 and MVC3 Razor forms, I am not able to make a dependent observable work when I introduce an ajax method. I have set up a set of observables that are a part of a calculation, and when I return the product of those observables, I am able to set a SPAN tag with the correct result. However, when I try to use the ajax method to handle those observables and return a result, I get unpredictable behavior. First, it appears the ajax POST does not pick up one of the observables when the INPUT fields are updated (var b POSTs to the action method as 0, but then is eventually updated), and then it seems that I am not able to set the result even when it evaluates correctly. It appears there is timing issue with either the observables or the ajax call. Although simply keeping performing the calculation in javascript works fine, my intent is to call ajax methods for more complicated logic. I have removed the call to ko.applybindings from doc.ready(), and also moved the SCRIPT methods to the bottom of the page- this was the only way I found to make this partly functional. My viewModel is set up as follows:
var viewModel = {
a: ko.observable(0),
b: ko.observable(1),
c: ko.observable(2),
// commented this out, since
// the dependent observable will handle this
// d: ko.observable(0)
};
In my dependent observable:
viewModel.d = ko.dependentObservable(function () {
var theResult = 0;
$('.theLabel').css("visibility", "visible");
theResult=viewModel.a() * viewModel.b() * viewModel.c();
// if we return here we get a valid result
return (theResult);
// prefer to call ajax method
// first check to ensure one variable is set
if (viewModel.a() > 0) {
$.ajax("/myCalculation/getResult", {
data: ko.toJSON(viewModel),
type: "post",
context: viewModel,
contentType: "application/json",
success: function (result) {
// can't set visibility here
$('.theLabel').css("visibility", "visible");
// the POST does not pick up some observables, or
// does not the set dependent observable at all
return result;
}
});
}
});

There is quite a bit wrong with the function you have setup.
1.) You are returning from your function before making your AJAX call. The code after your return statement will never execute.
2.) Even if you omit the first return statement, your AJAX call is Asynchronous... which means it will execute in the background and return control to the outer scope immediately. Since you don't have a return statement, then you are going to return undefined.
3.) The comment in your success callback suggests you are expecting the return statement to propagate all the way up to your computed observable. THAT return statement is only scoped to the callback, and not the outer observable. The return value will be used by jQuery, and your observable will long since have returned.
If you want an observable to call an AJAX function you need a seperate value to store the results of the asynchronous call.
Here is a simplified example:
var viewModel = function(){
var $this = this;
$this.a = ko.observable();
$this.b = ko.observable();
$this.results = ko.observable();
//No need to assign this computed observable to a variable
// because the results will be stored in '$this.results'
// we just need this to handle the automatic updates
ko.computed(function(){
var data = {
a: $this.a(),
b: $this.b()
};
$.post("/do/some/stuff", data, function(results){
$this.results(results);
});
});
};

Related

Does promise object always need callback function?

I have a requirement that I need to create a service in Angular 1 and load data with an API call which can be accessed in controllers. I earlier tried by making a API call with $http service and assigning the data to the variable / object. I then injected the service and assigned the variable / object to the controller scope variable / object.
What I observed in the controller event loop is not same as service event loop and controller scope variable / object remains undefined. Later I got a solution to work by returning a promise from the service, and calling the promise in the controller, however I'm new to promises and not able to fully absorb that when I called a promise, I had to pass the function as argument which I think is a callback for the $http API call, but I'm uncertain how it's working under the hood. Can someone explain it?
//service code
this.getuserimages = function(uname) {
console.log("Username in UserImage Service: " + uname);
var promise = $http.get('/api/images/' + uname).then(function(response) {
this.userimages = response.data;
return this.userimages;
});
return promise;
}
// controller code
var userimagespromise = userImageService.getuserimages($routeParams.username);
userimagespromise.then(function(data) {
$scope.userimages = data;
Your code is a Promise chain.
I rewrited your code in a way that this chain is more clear, but the meaning is exactly the same:
$http.get('/api/images/' + uname)
.then(function(response) {
this.userimages = response.data;
return this.userimages;
})
.then(function(images) {
$scope.userimages = images;
});
You can read this flow in this way:
Please get me user images
And then, we they will be available (=> returned from the get and passed to the then function), save them in a variable and return them to the next element of the chain
And then, we they will be available (=> return from the previous promise), set them in the $scope
Please note that the entire flow is asynchronous because every Promise is "an object representing the eventual completion or failure of an asynchronous operation".
You can find more information in the Promise documentation.

How to avoid loop in Ajax call

I have function getData() which make an ajax call and retrive JSON data. On success i call another function which is marquee() . inside marquee on finish event i call getData() again, But each time getData() when get called, it increases it's request to mentioned file data.php, For example on first call it call once, Second call it request twice, and then twice become 4times,8times and more and more, how to avoid this?!
function getData()
{
$.get('data.php).done(function(response)
{
var data = JSON.parse(response);
if(data.Direction == "left")
{
$(".marquee").html("<span data-direction='"+data.Direction+"'>"+data.Message+"</span>");
}else if(data.Direction == "right"){
$(".marquee").html("<span data- direction='"+data.Direction+"'>"+data.Message+"</span>");
}
});
}
function marquee()
{
$(".marquee").marquee({duration : 10000}).bind("finished",function()
{
getData();
});
}
I hope i was clear... Appreciate each answer.
Every time you are calling marquee function, you are basically binding an event finished on to it. On multiple such function calls, you will have duplicate events. In your code setup, you need to unbind the function before binding it. Something like
$(".marquee").marquee({duration : 10000}).unbind("finished",getData).bind("finished",getData)
Ideally, you should bind only once so you do not have to unbind it again and again.

AJAX + jQuery Deferred: execution sequence

Task: get data from server with $.post, process them by method .success(), after that call some function.
var t;
$.when($.post("get_json.php", function(res) {
t = res;
}, 'json')).done(function() {
console.log(t);
});
Do I understand correctly that the Deferred method .done() is executed after .success is done (ie t = res)?
But why "console.log(t)" shows "undefined"?
Is .done() fires after request, but before .success()?
Passing a "success" callback to $.post() is an alternative to the (preferred) chaining of .done(...). Do one or the other, not both, then you don't need to worry about the execution order.
Also, unless you have a decent caching strategy for async data, you shouldn't be setting t as an outer var.
$.post("get_json.php", ...).done(function(t) {
console.log(t);
//do awesome things with t here
});
Caching would be something like this :
var asyncCache = {};
...
function get_t() {
return (asyncCache.t) ? $.when(asyncCache.t) : $.post("get_json.php", ...).done(function(t) {
asyncCache.t = t;
});
}
...
get_t().done(function(t) {
console.log(t);
//do awesome things with t here
}
$.when() is ONLY needed when you have multiple promises and you want to wait for all of them to complete. You simply don't need it at all for your single ajax call. You can just do it like this:
$.post("get_json.php").done(function(t) {
// use the results of the ajax call here
console.log(t);
});
In addition, your code example was using BOTH a success callback function AND a .done() handler. Pick one of the other, not both as they are different ways of getting a callback when the ajax call is done. I'd suggest the promise implementation above because it's more flexible. But, you could also use just the success handler:
$.post("get_json.php", function(t) {
// use the results of the ajax call here
console.log(t);
}, 'json');
Note, when you have an asynchronous operation like this, you need to consume the results of the ajax call in the success callback (or the promise callback) or call some function from there and pass it the data. You do not want to put the data into a global variable or a variable in a higher scope because other code will simply have no way of knowing when the data is ready and when it is not. Put your action in the callback.
If you have more than one ajax call that you want to wait for, then you can do it like this:
$.when($.post("get_json.php"), $.post("get_json2.php")).done(function(r1, r2) {
// use the results of the ajax call here
console.log(r1[0]); // results from first ajax call
console.log(r2[0]); // results from second ajax call
});

How can I handle async calls when validating with Backbone.js (uniqueness specifically)

This is relevant for either client or server side apps using backbone. I am attempting to create a validation function with uniqueness checks to MongoDB or some REST call (depending on environment). Both of these calls are async by nature; however, I think I actually need to make it block here for validation purposes. If I don't return anything the validate function will assume validation passed.
My code currently looks like this on the server side:
isUnique: function (key) {
var dfdFindOne = this.findOne({key: this.get(key)}),
dfd = new Deferred();
dfdFindOne.done(function (err, result) {
console.log(result);
dfd.resolve(true);
});
return dfd;
};
... some stuff here....
I feel like I can do some sort of wait till done functionality here before I return... perhaps not though. I wish backbone provided a callback function or something or accepted some sort of deferred type thing.
validate: function() {
var result = undefined;
if(!this.isUnique(key).done(function(){
result = "AHHH not unique!";
});
return result;
}
A possible solution might be to force mongodb's native node client to call things synchronously. I think I can do the same with rest calls... This is probably a bad solution though.
You could call the ajax request and set async:false in this way the return will have value. However to use async:false is evil because could appear as the browser is locked. For server side maybe there are not always workarounds for set async: false
My recommendation is to use your own validation flow instead of Backbone.validate flow, because the validation flow of Backbone was made thinking for synchronous validations only. You could try something like this:
//Code in your Model
isUnique: function (callback) {
var dfdFindOne = this.findOne({key: this.get(key)});
dfdFindOne.done(function (err, result) {
console.log(result);
callback(result);
});
};
validate: function(callback) {
this.isUnique(callback);
}
//trying to validate before save
model.validate(function(result){
if( result == 'whatexpected'){
model.save();
}
});

Call any webservice from the same $.ajax() call

Im creating a usercontrol which is controled client side, it has an javascript-file attatched to it. This control has a button and upon click a popup appears, this popup shows a list of my domain entities. My entities are fetched using a call to a webservice.
Im trying to get this popup usercontrol to work on all my entities, therefore i have the need to call any webservice needed (one per entity for example) with the same $.ajax() call.
I have hiddenfields for the webservice url in my usercontrol which you specify in the markup via a property. So far so good. The problem arise when i need some additional parameters to the webservice (other than pagesize and pageindex). Say for example that one webservice takes an additional parameter "Date".
At the moment i have my parameters set up like this:
var params = JSON.stringify({
pageSize: _this.pageSize,
pageIndex: _this.pageIndex
});
and then i call the webservice like so:
$.ajax({
webserviceUrl,
params,
function(result) {
//some logic
});
});
What i want to do is to be able to add my extra parameters (Date) to "Param" when needed, the specification of these parameters will be done via properties of the usercontrol.
So, bottom line, i have a set of default parameters and want to dynamically add optional extra parameters.
How is this possible?
Thanks in advance.
I know it won't be fast, but something like
var params;
function buildJsonParams(defaultParam1, defaultParam2, optionalParam1, ...)
{
string pString = "params = JSON.stringify({ pageSize: _this.pageSize, pageIndex: _this.pageIndex";
if (optionalParam1 != undefined)
{
pString += ",yourOptionParam: optionalParam1";
}
pString += "});";
eval(pString);
}
or if you don't know ahead of time what the optional parameters are you could use parallel arrays, one being the variable and the other the parameter name and pass those into a buildJSONParams like function to do the same kind of thing only it loops through the 2 arrays that get passed in.
something like this should work
var params = getDefaultParams();
// some condition to determine if extra needed?
params.specialDate = getSpecialDateParam();
// could also be written as params['specialDate'] = getSpecialDateParam();
$.ajax({
webserviceUrl,
JSON.stringify(params),
function(result) {
//some logic
});
});
// ****************************
function getDefaultParams () {
return {
pageSize: _this.pageSize,
pageIndex: _this.pageIndex
};
}

Resources