I'm looking for a way to initialize a var after an ajax call. The problem is that the ajax call is in an another file.
Here is my code :
file1.js
$(document).ready(function () {
getKanbans();
});
function getKanbans() {
var kanbans = RequestSender.getKanbans();
console.log(kanbans); // print undefined
}
RequestSender.js
class RequestSender {
static getKanbans() {
$.ajax({
url: './ajax/select_kanbans.php',
type: 'GET',
success: RequestSender.initKanbanList
});
}
static initKanbanList(data) {
var result = JSON.parse(data);
var kanbans = [];
for (var i = 0; i < result.kanbans.length; ++i) {
var currentKanban = result.kanbans[i];
kanbans.push(new Kanban(currentKanban['Name'], currentKanban['Status']))
}
console.log(kanbans); // correctly displayed
return kanbans;
}
}
I just use jQuery, all my files are included. I think that the problem come from the fact that ajax is async but I don't know how to fix that.
in your example ajax call started but kanbans still undefined
function getKanbans() {
//ajax call started but kanbans still undefined
var kanbans = RequestSender.getKanbans();
console.log(kanbans); // print undefined
}
so you should complete execution after ajax call finished you can do that with the help of promises
for more information Check this
function getKanbans(url) {
var promiseObj = new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log("xhr done successfully");
var response = xhr.responseText;
var responseJson = initKanbanList(response);
resolve(responseJson);
} else {
reject(xhr.status);
console.log("xhr failed");
}
} else {
console.log("xhr processing going on");
}
}
console.log("request sent succesfully");
});
return promiseObj;
}
function initKanbanList(data) {
var result = JSON.parse(data);
var kanbans = [];
for (var i = 0; i < result.kanbans.length; ++i) {
var currentKanban = result.kanbans[i];
kanbans.push(new Kanban(currentKanban['Name'], currentKanban['Status']))
}
console.log(kanbans); // correctly displayed
return kanbans;
}
$(document).ready(function () {
// to call it
getKanbans('./ajax/select_kanbans.php').then(function (kanbans) {
console.log(kanbans);
}, function (error) {
console.log(error);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
This is called modules in javascript. You can implement them using link tags directly. But you are much better served by libraries like RequireJS and others. Then you can do things like:
require(['./RequestSender'], function (RequestSender) {
var rs = new RequestSender();
... //whatever
});
Here is a good SO question that explains modules well: How do I include a JavaScript file in another JavaScript file?
Then here is error, value of var good not changing. What i'm doing wrong?
More details for stackoverflow! More details for stackoverflow! More details for stackoverflow!
function set(id, edate)
{
var good = true;
$.post("ajax.php", { func: "set", id: id, edate: edate},
function(data){
data = JSON.parse(data);
if(data.error) {
good = false;
if(data.emessage=='SqlError') { alert('Ошибка выполнения запроса, перезагрузите страницу и попробуйте снова.'); }
}
else {
if(data.emessage=='AddOk') { alert('Запись о выданной зарплате успешно создана!'); }
else if(data.emessage=='DeleteOk') { alert('Выдача зарплаты ОТМЕНЕНА успешно!'); }
}
}
);
return good;
}
the issue is that you have an asynchronous function (AJAX POST) which manipulates your variable good.
You can not return the variable directly, instead consider using the following:
function set(id, edate, callback)
{
$.post("ajax.php", { func: "set", id: id, edate: edate},
function(data){
data = JSON.parse(data);
if(data.error) {
callback(false);
}else{
callback(true);
}
}
);
}
usage:
set(id, edate, function(good){
});
What makes $.post asynchronous?
Jquery source for $.post:
function (url, data, callback, type) {
// Shift arguments if data argument was omitted
if (jQuery.isFunction(data)) {
type = type || callback;
callback = data;
data = undefined;
}
// The url can be an options object (which then must have .url)
return jQuery.ajax(jQuery.extend({
url: url,
type: method,
dataType: type,
data: data,
success: callback
},
jQuery.isPlainObject(url) && url));
}
which calls JQuery.ajax:
function (url, options) {
// If url is an object, simulate pre-1.5 signature
if (typeof url === "object") {
options = url;
url = undefined;
}
// Force options to be an object
options = options || {};
var transport,
// URL without anti-cache param
cacheURL,
// Response headers
responseHeadersString, responseHeaders,
// timeout handle
timeoutTimer,
// Url cleanup var
urlAnchor,
// To know if global events are to be dispatched
fireGlobals,
// Loop variable
i,
// Create the final options object
s = jQuery.ajaxSetup({},
options),
// Callbacks context
callbackContext = s.context || s,
// Context for global events is callbackContext if it is a DOM node or jQuery collection
globalEventContext = s.context && (callbackContext.nodeType || callbackContext.jquery) ? jQuery(callbackContext) : jQuery.event,
// Deferreds
deferred = jQuery.Deferred(),
completeDeferred = jQuery.Callbacks("once memory"),
// Status-dependent callbacks
statusCode = s.statusCode || {},
// Headers (they are sent all at once)
requestHeaders = {},
requestHeadersNames = {},
// The jqXHR state
state = 0,
// Default abort message
strAbort = "canceled",
// Fake xhr
jqXHR = {
readyState: 0,
// Builds headers hashtable if needed
getResponseHeader: function (key) {
var match;
if (state === 2) {
if (!responseHeaders) {
responseHeaders = {};
while ((match = rheaders.exec(responseHeadersString))) {
responseHeaders[match[1].toLowerCase()] = match[2];
}
}
match = responseHeaders[key.toLowerCase()];
}
return match == null ? null : match;
},
// Raw string
getAllResponseHeaders: function () {
return state === 2 ? responseHeadersString : null;
},
// Caches the header
setRequestHeader: function (name, value) {
var lname = name.toLowerCase();
if (!state) {
name = requestHeadersNames[lname] = requestHeadersNames[lname] || name;
requestHeaders[name] = value;
}
return this;
},
// Overrides response content-type header
overrideMimeType: function (type) {
if (!state) {
s.mimeType = type;
}
return this;
},
// Status-dependent callbacks
statusCode: function (map) {
var code;
if (map) {
if (state < 2) {
for (code in map) {
// Lazy-add the new callback in a way that preserves old ones
statusCode[code] = [statusCode[code], map[code]];
}
} else {
// Execute the appropriate callbacks
jqXHR.always(map[jqXHR.status]);
}
}
return this;
},
// Cancel the request
abort: function (statusText) {
var finalText = statusText || strAbort;
if (transport) {
transport.abort(finalText);
}
done(0, finalText);
return this;
}
};
// Attach deferreds
deferred.promise(jqXHR);
// Remove hash character (#7531: and string promotion)
// Add protocol if not provided (prefilters might expect it)
// Handle falsy url in the settings object (#10093: consistency with old signature)
// We also use the url parameter if available
s.url = ((url || s.url || location.href) + "").replace(rhash, "").replace(rprotocol, location.protocol + "//");
// Alias method option to type as per ticket #12004
s.type = options.method || options.type || s.method || s.type;
// Extract dataTypes list
s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().match(rnotwhite) || [""];
// A cross-domain request is in order when the origin doesn't match the current origin.
if (s.crossDomain == null) {
urlAnchor = document.createElement("a");
// Support: IE8-11+
// IE throws exception if url is malformed, e.g. http://example.com:80x/
try {
urlAnchor.href = s.url;
// Support: IE8-11+
// Anchor's host property isn't correctly set when s.url is relative
urlAnchor.href = urlAnchor.href;
s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== urlAnchor.protocol + "//" + urlAnchor.host;
} catch(e) {
// If there is an error parsing the URL, assume it is crossDomain,
// it can be rejected by the transport if it is invalid
s.crossDomain = true;
}
}
// Convert data if not already a string
if (s.data && s.processData && typeof s.data !== "string") {
s.data = jQuery.param(s.data, s.traditional);
}
// Apply prefilters
inspectPrefiltersOrTransports(prefilters, s, options, jqXHR);
// If request was aborted inside a prefilter, stop there
if (state === 2) {
return jqXHR;
}
// We can fire global events as of now if asked to
// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
fireGlobals = jQuery.event && s.global;
// Watch for a new set of requests
if (fireGlobals && jQuery.active++===0) {
jQuery.event.trigger("ajaxStart");
}
// Uppercase the type
s.type = s.type.toUpperCase();
// Determine if request has content
s.hasContent = !rnoContent.test(s.type);
// Save the URL in case we're toying with the If-Modified-Since
// and/or If-None-Match header later on
cacheURL = s.url;
// More options handling for requests with no content
if (!s.hasContent) {
// If data is available, append data to url
if (s.data) {
cacheURL = (s.url += (rquery.test(cacheURL) ? "&" : "?") + s.data);
// #9682: remove data so that it's not used in an eventual retry
delete s.data;
}
// Add anti-cache in url if needed
if (s.cache === false) {
s.url = rts.test(cacheURL) ?
// If there is already a '_' parameter, set its value
cacheURL.replace(rts, "$1_=" + nonce++) :
// Otherwise add one to the end
cacheURL + (rquery.test(cacheURL) ? "&" : "?") + "_=" + nonce++;
}
}
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if (s.ifModified) {
if (jQuery.lastModified[cacheURL]) {
jqXHR.setRequestHeader("If-Modified-Since", jQuery.lastModified[cacheURL]);
}
if (jQuery.etag[cacheURL]) {
jqXHR.setRequestHeader("If-None-Match", jQuery.etag[cacheURL]);
}
}
// Set the correct header, if data is being sent
if (s.data && s.hasContent && s.contentType !== false || options.contentType) {
jqXHR.setRequestHeader("Content-Type", s.contentType);
}
// Set the Accepts header for the server, depending on the dataType
jqXHR.setRequestHeader("Accept", s.dataTypes[0] && s.accepts[s.dataTypes[0]] ? s.accepts[s.dataTypes[0]] + (s.dataTypes[0] !== "*" ? ", " + allTypes + "; q=0.01" : "") : s.accepts["*"]);
// Check for headers option
for (i in s.headers) {
jqXHR.setRequestHeader(i, s.headers[i]);
}
// Allow custom headers/mimetypes and early abort
if (s.beforeSend && (s.beforeSend.call(callbackContext, jqXHR, s) === false || state === 2)) {
// Abort if not done already and return
return jqXHR.abort();
}
// Aborting is no longer a cancellation
strAbort = "abort";
// Install callbacks on deferreds
completeDeferred.add(s.complete);
jqXHR.done(s.success);
jqXHR.fail(s.error);
// Get transport
transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);
// If no transport, we auto-abort
if (!transport) {
done(-1, "No Transport");
} else {
jqXHR.readyState = 1;
// Send global event
if (fireGlobals) {
globalEventContext.trigger("ajaxSend", [jqXHR, s]);
}
// If request was aborted inside ajaxSend, stop there
if (state === 2) {
return jqXHR;
}
// Timeout
if (s.async && s.timeout > 0) {
timeoutTimer = window.setTimeout(function () {
jqXHR.abort("timeout");
},
s.timeout);
}
try {
state = 1;
transport.send(requestHeaders, done);
} catch(e) {
// Propagate exception as error if not done
if (state < 2) {
done(-1, e);
// Simply rethrow otherwise
} else {
throw e;
}
}
}
// Callback for when everything is done
function done(status, nativeStatusText, responses, headers) {
var isSuccess, success, error, response, modified, statusText = nativeStatusText;
// Called once
if (state === 2) {
return;
}
// State is "done" now
state = 2;
// Clear timeout if it exists
if (timeoutTimer) {
window.clearTimeout(timeoutTimer);
}
// Dereference transport for early garbage collection
// (no matter how long the jqXHR object will be used)
transport = undefined;
// Cache response headers
responseHeadersString = headers || "";
// Set readyState
jqXHR.readyState = status > 0 ? 4 : 0;
// Determine if successful
isSuccess = status >= 200 && status < 300 || status === 304;
// Get response data
if (responses) {
response = ajaxHandleResponses(s, jqXHR, responses);
}
// Convert no matter what (that way responseXXX fields are always set)
response = ajaxConvert(s, response, jqXHR, isSuccess);
// If successful, handle type chaining
if (isSuccess) {
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if (s.ifModified) {
modified = jqXHR.getResponseHeader("Last-Modified");
if (modified) {
jQuery.lastModified[cacheURL] = modified;
}
modified = jqXHR.getResponseHeader("etag");
if (modified) {
jQuery.etag[cacheURL] = modified;
}
}
// if no content
if (status === 204 || s.type === "HEAD") {
statusText = "nocontent";
// if not modified
} else if (status === 304) {
statusText = "notmodified";
// If we have data, let's convert it
} else {
statusText = response.state;
success = response.data;
error = response.error;
isSuccess = !error;
}
} else {
// Extract error from statusText and normalize for non-aborts
error = statusText;
if (status || !statusText) {
statusText = "error";
if (status < 0) {
status = 0;
}
}
}
// Set data for the fake xhr object
jqXHR.status = status;
jqXHR.statusText = (nativeStatusText || statusText) + "";
// Success/Error
if (isSuccess) {
deferred.resolveWith(callbackContext, [success, statusText, jqXHR]);
} else {
deferred.rejectWith(callbackContext, [jqXHR, statusText, error]);
}
// Status-dependent callbacks
jqXHR.statusCode(statusCode);
statusCode = undefined;
if (fireGlobals) {
globalEventContext.trigger(isSuccess ? "ajaxSuccess" : "ajaxError", [jqXHR, s, isSuccess ? success : error]);
}
// Complete
completeDeferred.fireWith(callbackContext, [jqXHR, statusText]);
if (fireGlobals) {
globalEventContext.trigger("ajaxComplete", [jqXHR, s]);
// Handle the global AJAX counter
if (! (--jQuery.active)) {
jQuery.event.trigger("ajaxStop");
}
}
}
return jqXHR;
}
Jquery events:
$.fn.ajaxStart = function ( handler ) { return this.on('ajaxStart', handler); };
$.fn.ajaxStop = function ( handler ) { return this.on('ajaxStop', handler); };
$.fn.ajaxComplete = function ( handler ) { return this.on('ajaxComplete', handler); };
$.fn.ajaxError = function ( handler ) { return this.on('ajaxError', handler); };
$.fn.ajaxSuccess = function ( handler ) { return this.on('ajaxSuccess', handler); };
$.fn.ajaxSend = function ( handler ) { return this.on('ajaxSend', handler); };
jQuery.ajaxSetup:
function (target, settings) {
return settings ?
// Building a settings object
ajaxExtend(ajaxExtend(target, jQuery.ajaxSettings), settings) :
// Extending ajaxSettings
ajaxExtend(jQuery.ajaxSettings, target);
}
jQuery.ajax settings:
{
"url": "http://james.padolsey.com/jquery/#v=git&fn=",
"type": "GET",
"isLocal": false,
"global": true,
"processData": true,
"async": true,
"contentType": "application/x-www-form-urlencoded; charset=UTF-8",
"accepts": {
"*": "*/*",
"text": "text/plain",
"html": "text/html",
"xml": "application/xml, text/xml",
"json": "application/json, text/javascript",
"script": "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
},
"contents": {
"xml": {},
"html": {},
"json": {},
"script": {}
},
"responseFields": {
"xml": "responseXML",
"text": "responseText",
"json": "responseJSON"
},
"converters": {
"text html": true
},
"flatOptions": {
"url": true,
"context": true
},
"jsonp": "callback"
}
xhr:
jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
// Support: IE6+
function() {
// XHR cannot access local files, always use ActiveX for that case
return !this.isLocal &&
// Support: IE7-8
// oldIE XHR does not support non-RFC2616 methods (#13240)
// See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
// and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
// Although this check for six methods instead of eight
// since IE also does not support "trace" and "connect"
/^(get|post|head|put|delete|options)$/i.test( this.type ) &&
createStandardXHR() || createActiveXHR();
} :
// For all other browsers, use the standard XMLHttpRequest object
createStandardXHR;
var xhrId = 0,
xhrCallbacks = {},
xhrSupported = jQuery.ajaxSettings.xhr();
// Support: IE<10
// Open requests must be manually aborted on unload (#5280)
// See https://support.microsoft.com/kb/2856746 for more info
if ( window.attachEvent ) {
window.attachEvent( "onunload", function() {
for ( var key in xhrCallbacks ) {
xhrCallbacks[ key ]( undefined, true );
}
});
}
ultimately jQuery.ajaxSettings.xhr is what decides what type of object to create for communicating with the server.
All modern browsers support the XMLHttpRequest object (IE5 and IE6 use
an ActiveXObject).
The XMLHttpRequest object is used to exchange data with a server
behind the scenes. This means that it is possible to update parts of a
web page, without reloading the whole page.
quoted from
"If you use XMLHttpRequest from an extension, you should use it
asynchronously. In this case, you receive a callback when the data
has been received, which lets the browser continue to work as normal
while your request is being handled."
You can make an XMLHttpRequest synchronous and JQUERY allows you to do this (reffer to jquery.ajax.settings["asyc"]); However, asyc is enabled by default.
JQUERY ASYNC FALSE EXAMPLE
JQUERY ASYNC TRUE EXAMPLE
I have an AngularJS app, with a scope. Inside this scope there an array of Content objects, displayed through a view. In the view, for each content, there's a button Select, connected to the object's method selectToggle().
When this method is called, a $resource that will post some informations to an API, is created.
In the $resource.query callback I need to access the parent Content object, but since it's asynchronous, I loose the reference ('this' is not pointing to the Window object).
How can I pass a reference to the object who create the $resource in the callback function?
Thanks
function Content (informations) {
this.selected = false;
this.spinnerEnabled = false;
this.error = null;
this.selectToggle = function () {
this.spinnerEnabled = true;
ContentResource = $resource(
'/en/api/content',
null,
{'query': {'method': 'POST', isArray: false}}
).query({
'type': this.type,
'extId': this.id
}, function (response) {
// Here this is pointing to Window, not to the Content object
// so the following set are useless.
if (response.status == "success") {
this.selected = ! this.selected;
} else if (response.status == "error") {
this.error = response.message;
} else {
this.error = "Request failed";
}
this.spinnerEnabled = false;
});
};
}
You can store a reference to the Content object as such:
function Content (informations) {
this.selected = false;
this.spinnerEnabled = false;
this.error = null;
var self = this; // <-- Store reference for use later
// ...
Then, in your callback you can refer to self:
if (response.status == "success") {
self.selected = ! self.selected;
} else if (response.status == "error") {
self.error = response.message;
Is it possible to get a reference to the triggerElement that invoked the Ajax request in the onSuccess function?
<%=Ajax.ActionLink("x", a, r, New AjaxOptions With {.OnSuccess = _
"function(context) {alert('get triggerElement reference here?');}" })%>
The page is rendered to:
x
So let's have a look at Sys.Mvc.AsyncHyperlink.handleClick inside Scripts\MicrosoftMvcAjax.debug.js:
Sys.Mvc.AsyncHyperlink.handleClick = function Sys_Mvc_AsyncHyperlink$handleClick(anchor, evt, ajaxOptions) {
/// omitted doc comments
evt.preventDefault();
Sys.Mvc.MvcHelpers._asyncRequest(anchor.href, 'post', '', anchor, ajaxOptions);
}
So the ActionLink is rendered to an anchor ("a") tag, with an "onclick" event, which uses Sys.Mvc.AsyncHyperlink.handleClick with this as one of the parameters, mapped to anchor.
Then there's this Sys.Mvc.MvcHelpers._asyncRequest call with anchor as the fourth parameter. Let's have a look in Sys.Mvc.MvcHelpers._asyncRequest:
Sys.Mvc.MvcHelpers._asyncRequest = function Sys_Mvc_MvcHelpers$_asyncRequest(url, verb, body, triggerElement, ajaxOptions) {
/// omitted documentation
if (ajaxOptions.confirm) {
if (!confirm(ajaxOptions.confirm)) {
return;
}
}
if (ajaxOptions.url) {
url = ajaxOptions.url;
}
if (ajaxOptions.httpMethod) {
verb = ajaxOptions.httpMethod;
}
if (body.length > 0 && !body.endsWith('&')) {
body += '&';
}
body += 'X-Requested-With=XMLHttpRequest';
var requestBody = '';
if (verb.toUpperCase() === 'GET' || verb.toUpperCase() === 'DELETE') {
if (url.indexOf('?') > -1) {
if (!url.endsWith('&')) {
url += '&';
}
url += body;
}
else {
url += '?';
url += body;
}
}
else {
requestBody = body;
}
var request = new Sys.Net.WebRequest();
request.set_url(url);
request.set_httpVerb(verb);
request.set_body(requestBody);
if (verb.toUpperCase() === 'PUT') {
request.get_headers()['Content-Type'] = 'application/x-www-form-urlencoded;';
}
request.get_headers()['X-Requested-With'] = 'XMLHttpRequest';
var updateElement = null;
if (ajaxOptions.updateTargetId) {
updateElement = $get(ajaxOptions.updateTargetId);
}
var loadingElement = null;
if (ajaxOptions.loadingElementId) {
loadingElement = $get(ajaxOptions.loadingElementId);
}
var ajaxContext = new Sys.Mvc.AjaxContext(request, updateElement, loadingElement, ajaxOptions.insertionMode);
var continueRequest = true;
if (ajaxOptions.onBegin) {
continueRequest = ajaxOptions.onBegin(ajaxContext) !== false;
}
if (loadingElement) {
Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), true);
}
if (continueRequest) {
request.add_completed(Function.createDelegate(null, function(executor) {
Sys.Mvc.MvcHelpers._onComplete(request, ajaxOptions, ajaxContext);
}));
request.invoke();
}
}
So the original anchor is now triggerElement, but as you can see, this parameter is never used in the function's body.
So, if you want to have some kind of a "formal" (or documented) reference to triggerElement - no such thing.
But hey, it's JavaScript, so you can access almost anything as long as the browser did not move to another page, including the call stack. For instance:
<script type="text/javascript">
function a(p, q)
{
b();
}
function b() {
var x = arguments.caller[1];
alert(x); // boo!
}
a(789, "boo!");
</script>
So eventually you can hack it and access the original anchor. I suggest you do the following:
Write a function to be invoked in the
OnBegin.
Inside this function, access
the original triggerElement, and
add it as a property to the original
ajaxOptions (which can be accessed,
too)
Then, in the OnSuccess function,
access your hacked property of
ajaxOptions.