My Knockoutjs code goes as following:
function chainModel(){
var self = this;
this.total_count = ko.observable();
function get_total_count(number){
$.ajax({
type : "get",
url : "./XYZ/abc.php",
cache : false,
data : {number: number},
success : function(result){
self.total_count($.parseJSON(result));
},
error : function(jqXHR, textStatus, errorThrown){
console.log("Error ! Unable to get step " + $number + " count." + "Error: " + errorThrown + ", Status: " + textStatus);
}
});
}
}
ko.applyBindings(new chainModel());
Inside the get_total_count() function, I am assigning the ajax result to self.total_count observable. Instead, I would like to pass the observable too as an parameter to the get_total_count() function so that I can reuse the same function for more than one observable.
Here's a different approach which is using the the promise semantics of jQuery Ajax calls. Take a few minutes to familiarize yourself with jQuery Deferreds if the concept is new to you, it's worth it.
Quick note: By widely adopted convention, constructor names are PascalCase and all other names are camelCase in JavaScript. Don't use underscore_separated identifiers (nobody else does).
function ChainModel() {
var self = this;
self.totalCount = ko.observable();
self.otherCount = ko.observable();
function getCount(number) {
return $.get("./XYZ/abc.php", {number: number})
.fail(function (jqXHR, textStatus, errorThrown) {
console.error(
"Error ! Unable to get step " + number + " count." +
"Error: " + errorThrown + ", Status: " + textStatus
);
});
}
getCount(1).done(self.totalCount);
getCount(2).done(self.otherCount);
}
ko.applyBindings(new ChainModel());
Since knockout observables are in fact functions and calling them sets their value, you can directly use them as success callbacks in Ajax calls.
By returning the jqXHR object from getCount(), you get access to the promise functions it exposes. So instead of passing in the target observable into getCount() you could pass it to the .done() callback, thus assigning the result of the Ajax call to it. Effectively this is separation of concerns and makes your code more flexible.
Other notes:
You should not have to force cache: false in an Ajax call. Set the appropriate Cache-Control header on the server side and the browser won't cache the call.
jQuery automatically parses the response for you, manually calling $.parseJSON() is not necessary.
Isn't it as simple as passing the observable as an argument?
function chainModel(){
var self = this;
this.total_count = ko.observable();
get_total_count(this.total_count, this.number);
function get_total_count(observable, number){
$.ajax({
type : "get",
url : "./XYZ/abc.php",
cache : false,
data : {number: number},
success : function(result){
observable($.parseJSON(result));
},
error : function(jqXHR, textStatus, errorThrown){
console.log("Error ! Unable to get step " + number + " count." + "Error: " + errorThrown + ", Status: " + textStatus);
}
});
}
}
ko.applyBindings(new chainModel());
Are you calling this method "get_total_count()" via any event binding. If so, then you can pass entire data view model to this method.
<input data-bind="event:{ click: function(data,event){ get_total_count(data,event,total_count()); } }" />
Related
My code takes input from the users, and saves them by functions in local storage to global variables first_name, last_name and domain. I try to pass these variables to the hunterIO api in my code through the function ajaxing using jquery. For some reason the code throws up an unexpected identifier in the url part of the code.
document.querySelector('#url_But').addEventListener("click", ajaxing, false);
function ajaxing() {
$.ajax({
f_url = "https://api.hunter.io/v2/email-finder?
domain="+domain+"first_name="+first_name+"&last_name="+last_name+
"&api_key=[REDACTED]"
// Error gets thrown here ^^ 'Unexpected identifier'
url: f_url,
type: 'GET',
}).done(function(dataObj) {
console.log(dataObj.data.first_name);
}).fail(function(xhr, textStatus, errorThrown) {
alert("Error: " + xhr.status + " " + textStatus);
})
};
I am also worried that after fixing this issue another will be thrown up. Because learning api querying through jquery has been a journey from hell.
You have two issues. Firstly, you cannot define a variable inside an object. Move the f_url declaration outside of $.ajax(). Secondly, you cannot have line breaks in a string. You can either place it all on one line, concatenate the separate lines, or use a template literal. Try this:
document.querySelector('#url_But').addEventListener("click", ajaxing, false);
function ajaxing() {
let f_url = "https://api.hunter.io/v2/email-finder?domain=" + domain + "&first_name=" + first_name + "&last_name=" + last_name + "&api_key=[REDACTED]"
$.ajax({
url: f_url,
type: 'GET'
}).done(function(dataObj) {
console.log(dataObj.data.first_name);
}).fail(function(xhr, textStatus, errorThrown) {
alert("Error: " + xhr.status + " " + textStatus);
})
};
Finally note the missing & before the first_name property in the URL.
You must pass the url as first param in $.ajax() not declare it inside it as it is a function.
Just declare f_url before the $.ajax() and pass it to the url parameter.
I'm trying to rewrite an AJAX request of mine so I can debug it's responses, so I have moved code from the responses into individual functions. In my original code I was able to get the result returning from the AJAX call, and output it in the success response.
Since moving that code into a separate function and then trying to call that function in the success response, I get the error 'result is undefined'. I'm not familiar enough with JavaScript to know why this is happening, please help.
$.ajax({
type: 'GET',
url: '/api/auth/api/Homepage/GetLinks',
success:displayLinks(result, jqStatus, error),
error: showPolicyLinkError(result, jqStatus, error)
});
function displayLinks(result, jqStatus, error){
$('#numbercontrol').html(result);
console.log("Success Log - Satus:" + jqStatus + " Error:" + error);
}
function showLinkError(result, jqStatus, error){
$('#numbercontrol').html("<p>Unable to return any links.</p>");
console.log("Failure Log - Result: " + result + "Satus:" + jqStatus + " Error:" + error);
}
You should only pass function names without arguments:
$.ajax({
type: 'GET',
url: '/api/auth/api/Homepage/GetLinks',
success: displayLinks,
error: showLinkError
});
I was running some jQuery code when suddenly I saw through Firebug Console that when I force a 500 error, the code inside .fail() method, is not being fired:
$.ajax({
url: "someAction.do",
data: "param1=param¶m2=param",
dataType: "xml"
}).done(function(xml){
//some stuff with my data
}).fail(function(xml){
//some other different stuff with my data that is not being fired
});
In the other hand, in a Java Action, I set the HttpResponse Status to "500" when an error occurs, for example, POSTING an invalid field trough the Ajax call, like searching in a database for an email that is not present, and then show the error trough ajax:
<xml version=bla,bla...>
<item>
<name>message</name>
<value>Invalid input data</value>
</item>
</xml>.
Any ideas why? I just want to use the new methods fail() and done(). I know I could do this using the statusCode handler.
But I allways like to follow new trends if I could. At least I would like to try that.
Thank you very much!
An alternative approach , substitute direct call to .done() or .fail() by filtering responses through .always() initially , then calling .done() or .fail() through filtering deferred ; maintaining ability to process actual net errors with same fail() callback
// simulate server-side responses
var error = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<item>"
+ "<name>message</name>"
+ "<value>Invalid input data</value>"
+ "</item>";
var success = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<item>"
+ "<name>message</name>"
+ "<value>Success</value>" + "</item>";
// params
$.ajaxSetup({
beforeSend : function(jqxhr, settings) {
var d = decodeURIComponent(settings.data)
.match(/data=+.*/)[0]
.split(/=|&/);
var params = {};
params[d[0]] = {};
params[d[0]][d[1]] = d[2];
params[d[0]][d[3]] = d[4];
jqxhr.data = params;
}
});
var request = function (url, _xmlResponse, data1, data2) {
var _response = new $.Deferred(),
_request = $.ajax({
url: url,
type: "POST",
data: {
// responses
xml: _xmlResponse,
// params
data: "param1="+ data1 +"¶m2="+ data2
},
dataType: "xml"
});
// check `responseText` for "Invalid" ,
// check `textStatus` for actual net error , "error"
_request.always(function (xml, textStatus, jqxhr) {
console.log(xml, textStatus, jqxhr, "check");
// simulate server-side processing, cont.
// if both object passed to server ,
// `param1` and `param2` _do not_ have property
// with value of at `params` , `""` empty string - generic "error" ,
// _and_ returned `responseText` contain "Invalid" ,
// _or_ `textStatus` : `error` , `reject` `dfd` , call `.fail()`
// else `resolve `dfd` , call `.done()`
// check params
var test = [xml["data"] || jqxhr["data"]].every(function(v, k) {
return !(v.data["param1"] !== "" && v.data["param2"] !== "" )
}) ;
// check `xml` responseText for node for "Invalid" text ,
// generate `.fail()`
var check = $(xml.children)
.find("value").is(":contains(Invalid)");
return check && test || textStatus === "error"
// call `.fail()`
? _response.reject([xml, textStatus, jqxhr])
// call `.done()`
: _response.resolve([xml, textStatus, jqxhr])
});
return _response
.done(function (xml) {
// do `done()` stuff
console.log(xml[0], xml[1], xml[2]);
var msg = $(xml[0].children).find("value").html();
$("#success")
.html(msg + ", "
+ xml[2].data.data.param1
+ xml[2].data.data.param2
);
return this.promise()
})
.fail(function (xml) {
// do `.fail()` stuff
console.log(xml[0], xml[1], xml[2], "fail");
var msg = $(xml[0].children).find("value").html()
|| xml[1] + ", " + xml[2];
$("#error")
.html(msg);
return this.promise()
});
};
Could then call request() , without _xmlResponse param , which should handle both generated .fail() (if responseText contains "Invalid") and net error .fail() calls (e.g., statusCode 500; textStatus error) , and .done() calls .
request("someAction.do", "param1_param", "param2_param");
Optionally, chain returned promise from request at .always() , as data array containing request return values ; after being processed at .done() or fail()
request("someAction.do", "param1_param", "param2_param")
.always(function(data) {
// do `.done()` , `.fail()` stuff ,
// `data, textStatus, jqxhr`
console.log(data[0], data[1], data[2])
})
jsfiddle http://jsfiddle.net/guest271314/ek15krb5/
I think that depends on the parameter "xml", if:
1. xml is a real xml object ,it'll trigger 'done';
2. xml is not a xml object ,it'll trigger 'fail'
Even if you return a 500 error code ,it is still a valid xml object, So it'll trigger "done"
I tried to use this loop to read some urls to read their modified time:
var arr = [];
//... fill arr with push
for (var e in arr) {
nodename=arr[e].hostname;
node_json="/nodes/"+nodename;
html +='data';
xhr = $.ajax({
url: node_json,
success: (function(nn) {
$('#host_'+nn).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
})(nodename)
});
This already works a bit i I comment out the success line: I get calls to all node-files, and in Firebug, I can see the different modified times in the header of the calls.
At first I had a closure, (see How to generate event handlers with loop in Javascript?) And I only got the last line modified with all results. that's why I try to put the action in a separate function.
But that gives:
ReferenceError: xhr is not defined
$('#host_'+nn).append("last modified: " + xhr.getResponseHeader("Last-Modified")...
How do I get xhr into that function?
I aslo tried:
...
xhr[e] = $.ajax({
url: node_json,
success: add_result_to_info(nodename, e)
});
}
}
// outside loop
function add_result_to_info(nn, e) {
$('#host_'+nn).append("last modified: " + xhr[e].getResponseHeader("Last-Modified"));
}
source of the AJAX call: Get the modified timestamp of a file with javascript
If arr is truly an array, just use .forEach or even better .map (with a shim on older browsers) to encapsulate each iteration's scope without the need for additional closures:
var xhrs = arr.map(function(e) {
var nodename = e.hostname;
var node_json = "/nodes/" + nodename;
html +='data';
return $.ajax({
url: node_json
}).done(function(data, status, xhr) {
$('#host_'+nodename).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
});
});
The reason to use var xhrs = arr.map() instead of .forEach is that you then (for free) get the ability to call yet another callback once every AJAX request has completed:
$.when.apply($, xhrs).then(function() {
// woot! They all finished
...
});
your are directly executing the method and passing its result as the callback for the success callback.
the xhr is already passed as the 3rd argument so try
success: function(nn,status, xhr) {
$('#host_'+nn).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
}
if you have to pass the nodename as well, the you need to use a function that returns a function
success: (function(nn){
return function(data ,status, xhr) {
// you can use nodename here...
$('#host_'+nn).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
};
})(nodename)
I have created a function called save , when i call save function, I am getting success undefined or object Object
UPDATES:
updated to get he values jqxhr object that ajax returns
function save() {
return $.ajax({
type: "POST",
url: "foo.json",
data: json_data,
contentType: 'application/json',
success: function (data, textStatus, xhr) {
$('<div id="loading">Loading...</div>').insertBefore('#form');
},
error: function (jqXHR, textStatus, errorThrown) {
}
});
}
$(document).ready(function () {
$(function () {
$("#save").click(function () {
var jqxhr = save();
alert("success " + jqxhr.success);
alert("status " + jqxhr.status);
alert("status " + jqxhr.readyState);
});
});
});
For the upteenth time.
ajax is asynchronous.
Use a callback function.
Ninja edit by OP
First of all, there is no return statement within your save function, so it works as expected by returning undefined value.
Secondly, it won't work that way. You need to return the $.ajax call (which itself, returns a jqXHR object, where you can hook in and setup code for different events. Afterall, by default an Ajax request runs asyncronously.
in save()
return $.ajax({ ...
and later...
save().done(function( retValue ) {
alert('success ' + retValue);
});
Learn more about jQuerys Ajax and Deferred objects here and here.
May be this is causing the issue, as i see you are having 2 document ready handlers one in another.
/***/
$(function () { // <--------------------I think this doesn't has to be here,
// so remove it and try if this solves
// the issue
$("#save").click(function () {
var success = save();
alert("success " + success);
});
}); // <-------------------------------and this one too