I'm an old 'C' programmer and with Javascript always struggle populating my data following ajax calls; i.e. I always resort to using global references. I'd rather be able to pass in the objects I need to update. Here's one example of what I do now - 'app' is the global (I'd have used a pointer in C :))
treeMapp.login = function ( dialog_div, form_div, server_call ) {
// validate the fields
if ( 0 ) {
}
else {
// send to server & parse JSON response (single line)
var jqxhr =
$.getJSON( server_call,
$( "#" + form_div ).serialize() )
.done( function( data, status ) {
if( status == 'success' ) {
// hack!?
app.user.username = data.username;
app.user.organisation = data.organisation;
app.user.loggedIn = true;
//close the dialog
$( '#' + dialog_div ).dialog('close');
}
else {
// login failed
alert( "login failed!" );
}
})
.fail( function() {
alert( "login: server error" );
}); // end var jqxhr =
} // end else (field validation ok)
}; // end treeMapp.login()
What's the best way of updating passed in parameters?
thanks
mini
You can pass app as an argument to your treeMapp.login function, then within the scope of it it would be local.
treeMapp.login = function ( dialog_div, form_div, server_call, app )
You could assign the data object returned by your jQuery result to app.user, thus avoiding the need for element by element assignment.
i.e. app.user = data
However, normally you ensure the global object can self initialise through a method, or you pass a reference to the global object to the method so it can initialise. Directly using assignment to global variables is (with a few exceptions) poor programming in Javascript as in any other language
UPDATE: the following shows an amalgamation of the answers...
treeMapp.login = function ( dialog_div, form_div, server_call, theapp ) {
var app = theapp; // may be needed for scope issue..
// validate the fields
if ( 0 ) {
}
else {
// send to server & parse JSON response (single line)
var jqxhr =
$.getJSON( server_call,
$( "#" + form_div ).serialize() )
.done( function( data, status ) {
if( status == 'success' ) {
// not a hack
$.extend(this.app.user, data, { loggedIn: true })
//close the dialog
$( '#' + dialog_div ).dialog('close');
}
else {
// login failed
alert( "login failed!" );
}
})
.fail( function() {
alert( "login: server error" );
}); // end var jqxhr =
} // end else (field validation ok)
}; // end treeMapp.login()
Related
I'm trying to avoid making two ajax calls by using .ajax.params() to get the last set of ajax parameters and returning the table data from my first call.
I then pass in my own json to datatables following this pattern
datatable.clear();
datatable.rows.add(newDataArray);
datatable.draw();
from this question.
However my table has ajax set so when draw() is called another ajax call is fired which defeats the point of passing in the data myself. What I need is a way to suppress the ajax call while redrawing the table.
An alternative would be to write my own ajax handling and manually add the data into datatables as above, however I think I would also have to create the ajax parameters myself which would be a pain.
I have created the following datatables plugin that makes it possible to do an ajax load with custom ajax settings for just one time.
var __reload = function ( settings, holdPosition, callback ) {
// Use the draw event to trigger a callback
if ( callback ) {
var api = new _Api( settings );
api.one( 'draw', function () {
callback( api.ajax.json() );
} );
}
if ( settings.oApi._fnDataSource( settings ) == 'ssp' ) {
settings.oApi._fnReDraw( settings, holdPosition );
}
else {
settings.oApi._fnProcessingDisplay( settings, true );
// Cancel an existing request
var xhr = settings.jqXHR;
if ( xhr && xhr.readyState !== 4 ) {
xhr.abort();
}
// Trigger xhr
settings.oApi._fnBuildAjax( settings, [], function( json ) {
settings.oApi._fnClearTable( settings );
var data = settings.oApi._fnAjaxDataSrc( settings, json );
for ( var i=0, ien=data.length ; i<ien ; i++ ) {
settings.oApi._fnAddData( settings, data[i] );
}
settings.oApi._fnReDraw( settings, holdPosition );
settings.oApi._fnProcessingDisplay( settings, false );
} );
}
};
jQuery.fn.dataTable.Api.register( 'ajax.loadOnce()', function ( ajax, callback, resetPaging ) {
return this.iterator( 'table', function ( ctx ) {
store = ctx.ajax;
ctx.ajax = ajax;
__reload( ctx, resetPaging===false, callback );
ctx.ajax = store;
} );
} );
This makes it possible to combine the datatables parameters with custom data and a new url as such
ajax = {
url: url,
data: function (d){
d.value = value;
}
};
table.ajax.loadOnce(ajax);
I had a similar issue. The easiest work around I found was temporarily disabling the AJAX and server side processing, make your changes, and then reset the values like shown below:
function set_datatable_ajax_processing(table, bool) {
table.settings()[0].oFeatures.bServerSide = bool;
table.settings()[0].ajax = bool;
}
set_datatable_ajax_processing(table, false)
// table edits and/or draw done here ...
set_datatable_ajax_processing(table, true)
I've got next code that fills page with data I get from server every 10 sec:
var refreshFunction = function() {
//get data from server
$.get("<c:out value="${baseURL}"/>/js/getCounts", function(data) {
vardata = data;
});
if (vardata != null) {
//divide
countArray = vardata.split("/");
//if server returns "-" or new array with new structure
if ((vardata == "-" || length != countArray.length)
&& !(document.getElementById('requestForm') instanceof Object)) {
//reload
location.reload();
clearInterval(refreshId);
} else {
//fill page with data
for (var j = 0; j <= countArray.length; j++) {
console.log(countArray[j]);
$('#badge_' + (j + 1)).text(countArray[j]);
}
}
}
};
$(document).ready(function() {
refreshId = setInterval(refreshFunction, 10000);
});
The code works, but if I open application and then turn off my server, script will never stop. I'm having
Failed to load resource: net::ERR_CONNECTION_REFUSED
How can I catch it and stop the script after that? I was trying to wrap code blocks with try and catch(e), but doesn't help.
Since the AJAX request is executed asynchronously, wrapping your code in try ... catch will not catch the exception. The exception happens after your code has finished executing.
You should handle the .fail case on the object returned by $.get to avoid seeing that error reported on the console:
var jqxhr = $.get("<c:out value="${baseURL}"/>/js/getCounts", function() {
vardata = data;
})
.done(function() {
alert( "second success" );
})
.fail(function() {
alert( "error" );
})
.always(function() {
alert( "finished" );
});
As a side note, you should put the complete body of your function inside the call back in $.get. Otherwise, you'll always be running your code with the old dataset, not the new one.
This problem doesn't show up in your code in the first execution because vardata is probably undefined and in Javascript land, undefined != null is false.
I'm using the .load() method in jQuery but I've realized that the request to my server should use ISO-8859-1 charset and not UTF-8. The problem is that I can't find how to set load method to use a different encoding. I read that .ajax method has 'content-type' setting to do this, but what about load method? I find load very useful when I need to update data from some divs without refreshing the page.
Any help would be appreciated, thanks.
Using ajaxSetup allows you to specify the settings for new ajax calls.
All subsequent Ajax calls using any function will use the new
settings, unless overridden by the individual calls, until the next
invocation of $.ajaxSetup().
with beforeSend you can provide a callback function to modify the XMLHttpRequest object before it's going to be send. jQuery Reference
Mozilla provides documentation about overrideMimeType():
Overrides the MIME type returned by the server. This may be used, for
example, to force a stream to be treated and parsed as text/xml, even
if the server does not report it as such.This method must be called
before send().
Borrowing code from this answer you could do:
$.ajaxSetup({
'beforeSend' : function(xhr) {
xhr.overrideMimeType('text/html; charset=ISO-8859-1');
},
});
//$('body').append('<div id=qqq>dfsdfsdf</div>')
//$('#qqq').load2('/index.php?showtopic=925 #post-29397','','','text/html; charset=utf-8')
//$('#qqq').load2('/index.php?showtopic=925 #post-29397','','','text/plain; charset=windows-1251')
//
jQuery.fn.load2 = function( url, params, callback, overrideMimeTypeVar) {
if ( typeof url !== "string" && _load ) {
return _load.apply( this, arguments );
}
var selector, type, response,
self = this,
off = url.indexOf(" ");
if ( off >= 0 ) {
selector = jQuery.trim( url.slice( off ) );
url = url.slice( 0, off );
}
// If it's a function
if ( jQuery.isFunction( params ) ) {
// We assume that it's the callback
callback = params;
params = undefined;
// Otherwise, build a param string
} else if ( params && typeof params === "object" ) {
type = "POST";
}
// If we have elements to modify, make the request
if ( self.length > 0 ) {
jQuery.ajax({
url: url,
// if "type" variable is undefined, then "GET" method will be used
type: type,
dataType: "html",
data: params,
// ++++++++++++++++++++++++++++++++++++++++++++++++++
beforeSend: function(x) {
if(x && x.overrideMimeType && overrideMimeTypeVar!=''){
x.overrideMimeType(overrideMimeTypeVar);
}}
// +++++++++++++++++++++++++++++++++++++++++++++++++++
}).done(function( responseText ) {
// Save response for use in complete callback
response = arguments;
self.html( selector ?
// If a selector was specified, locate the right elements in a dummy div
// Exclude scripts to avoid IE 'Permission Denied' errors
jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
// Otherwise use the full result
responseText );
}).complete( callback && function( jqXHR, status ) {
self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
});
}
return this;
};
As a learning exercise I've hacked together a script for an SO feature request (for the purposes of this question please ignore the merits or otherwise of that request). In the script I've encountered a technical issue that my limited javascript knowledge can't get past and I'd appreciate suggestions on how to resolve it.
To avoid spamming the server I use some search hacks to determine the number of answers and accepted answers for a tag. This involves using window.setTimeout() to callback to a function that sends a get request for each tag, increasing the timeout on each call to stagger the requests.
To get the results in a single request involves appending &pagesize=1 to the end of the url in the get request, so that the number of pages in the results gives you the total number of results without having to make any further requests.
A side affect of this approach is that subsequent page views use &pagesize=1 and I only see a single entry. I attempt to resolve this by firing another query with &pagesize=30 to reset it afterwards, but as it is all asynchronous the timing of the last query can result in the pagesize either being 1 or 30, depending on which request completes first. I've tried adding a further timeout and callback for this "reset" query but it hasn't really helped.
Is there a means to monitor the queries, waiting until all have been completed, then once they have all completed send the reset request? Or is there another approach that I could take?
You could make a call chain
Based on my previous idea of a ParallelAjaxExecuter, here's a SerialAjaxExecuter
$(function(){
var se = new SerialAjaxExecuter( function( results )
{
console.log( results );
}, 1000 );
se.addRequest( $.get, 'test.php', {n:1}, function( d ){ console.log( '1 done', d ); }, 'text' );
se.addRequest( $.get, 'test.php', {n:2}, function( d ){ console.log( '2 done', d ); }, 'text' );
se.addRequest( $.get, 'test.php', {n:3}, function( d ){ console.log( '3 done', d ); }, 'text' );
se.addRequest( $.get, 'test.php', {n:4}, function( d ){ console.log( '4 done', d ); }, 'text' );
se.execute();
});
var SerialAjaxExecuter = function( onComplete, delay )
{
this.requests = [];
this.results = [];
this.delay = delay || 1;
this.onComplete = onComplete;
}
SerialAjaxExecuter.prototype.addRequest = function( method, url, data, callback, format )
{
var self = this;
this.requests.push( {
"method" : method
, "url" : url
, "data" : data
, "format" : format
, "callback" : callback
} );
var numRequests = this.requests.length;
if ( numRequests > 1 )
{
this.requests[numRequests-2].callback = function( nextRequest, completionCallback )
{
return function( data )
{
completionCallback( data );
setTimeout( function(){ self.execute( nextRequest ); }, self.delay );
}
}( this.requests[numRequests-1], this.requests[numRequests-2].callback )
}
}
SerialAjaxExecuter.prototype.execute = function( request )
{
var self = this;
if ( 'undefined' == typeof request )
{
request = this.requests[0];
var lastRequest = this.requests[this.requests.length-1];
lastRequest.callback = function( completionCallback )
{
return function( data )
{
completionCallback( data )
self.onComplete( self.results );
}
}( lastRequest.callback )
}
request.method( request.url, request.data, function( r )
{
return function( data )
{
self.results.push( data );
r.callback( data );
}
}( request ) )
}
I didn't bake in a sleep period between requests, but that could certainly be added. Added the timeout
Note: this example is littered with console.log() calls for which you need firebug, or just remove them.
I'm not sure if I fully understand the problem but why not chain the requests rather than using a setTimeout? So at the end of the response handler of one request fire off the next request.
Append &pagesize= to every link on page that would need it with the pagesize you're currently using.
I have a tightly coupled javascript, where in there are series of if-else checks and multiple ajax calls are made. The ajax calls are nested type. My problem is I am in a deep nested ajax callable function and I want to get out from there gracefully.
The snippet of the code is .
function showSubscriptionLightBox() {
$.get("/ajax/get_subscription_lightbox_content.php?feed_id=" + feedid, function(data) {
//Work on the data we receive... and check whether user is logged in.
if(userLoggedIn) {
//Make one more ajax call
$.get("/ajax/is_user_subscribed.php?feed_id=" + feedid, function(data) {
//Work on data again.... and check if user is subscribed.
if(userSubscribed) {
//Then there is popup which comes up, a part of same page and it has a button name "task".
document.getElementById('task').onclick = function() {
if(document.getElementById('email_mode').checked) {
$.ajax({
url : "ajax/is_user_email_verified.php?user_id="+userID,
success : function(data) {
if(!data)
return;
var response;
response = eval("response = " + data);
if(!response)
return;
if(response['email_status'] == 0) {
//Exit from here
}}}
......
other part of code..
I want to exit gracefully from javascript, when the response['email_status'] == 0
Please tell me, how to do this??
I tried the return statement, but it took me to the enclosing function and not outside the script.
Thanks,
Amit
For what it is worth, here is some code from one of my applications. It syncs records using JSONP and AJAX. It first gets an array of object ids from a remote server. It then fetches the record for the object id at the zero index from the host server. Then it sends the record it receives to the remote server. At that point, it continues the process by starting the process with an incremented index into the array of ids. It terminates when the index reaches the end of the array.
(function( $ ) {
$.getJSON( 'http://remote.com/admin/record_ids.js?callback=?', function( data ) {
var set_record = function( index ) {
if ( index < data.length ) {
$.get( 'record_get.json', { contact_id: data[ index ] }, function( data ) {
$.getJSON( 'http://remote.com/admin/record_save.js?callback=?', data, function() {
set_record( index + 1 );
});
}, 'json');
}
};
set_record( 0 );
});
})( jQuery );
As you can see, when you want to get out gracefully, you just don't call. I can't imagine why you can't just return to stop your code.
There's a funny trick you can always use in JavaScript to escape the call stack: setTimeout(). It's useful in many situations, not just this, it is often used to work around DOM event related bugs in browsers as well.
$.ajax(
{
url: 'lol.php',
success: function(data)
{
setTimeOut(function()
{
// Your code comes here
}, 0); // 0 will ensure that it gets executed immediately
}
});
I know that with Prototype you could do this with try/catch blocks. You could throw an object from within one of the inner functions and it will travel up the call stack for other functions to intercept.