Is there any way to intercept an ajax request being made via jquery, to insert additional variables into it?
I know that .ajaxStart() lets you register a callback which triggers an event whenever an ajax request begins, but what I'm looking for is a way to cancel that ajax request if it meets certain criteria (e.g url), insert some more variables into its content, and then submit it.
This is for a plugin for a 3rd party software whose own code can't be changed directly.
Edit: Seems like .ajaxSetup() lets you set some global variables related to ajaxRequests. If I registered a beforeSend function, would that function be able to cancel the request to make a different one on meeting certain criteria?
Figured it out, this was the code I used:
jQuery(document).ready(function()
{
var func = function(e, data)
{
//data.data is a string with &seperated values, e.g a=b&c=d&.. .
//Append additional variables to it and they'll be submitted with the request:
data.data += "&id=3&d=f&z=y";
return true;
};
jQuery.ajaxSetup( {beforeSend: func} );
jQuery.post('example.php', {a : 'b'}, 'json');
} );
To cancel the request, returning false from func seemed to work.
Related
Suppose I have a page called form.php. I then clicked a button called "add button". This button triggers an event that got detected by a jquery function. The jquery function makes an ajax call to add.php.
Inside add.php, there is code that checks if a particular record exist in the database. If it does find that the record exists, I want to do the following.
Send a response string "exist" to ajax.
The ajax, inside the .done() function, will execute a prompt that says "This record already exist, do you wish to overright"?
If the user canceled the prompt, nothing more should happened and the ajax call should be done.
If the user clicks "ok", I would like the php script to be notified of this and execute an update statement using the data from form.php.
I suspect this is impossible because after receiving a response from php, AFAIK there is no way for ajax to respond back to the php script that is currently executing.
Am I correct or there is a way to do this?
You have to add a parameter to your ajax request, like override with true and false. By default/first request you set it to false. Then the add.php does it's default and returns exists.
The the user makes his decision. If he want to override, you send the ajax request again with the override parameter to true. Your add.php will notice the parameter and does whatever it has to do.
Wrap your ajax handler in an own function with a done callback. So you can reuse the request as often as you want. Pretty easy, no double code needed as well ...
The .done() function of your first ajax call executes when the ajax call has finished successfully, so when your php script has finished completely.
If you want to do something else, you would need to make a new ajax request. That could be to the same or another script, sending in different / new / additional parameters.
Note that you have to make sure that the second script cannot be called without the first one finishing, for example by setting and checking an expiring session variable.
you can do something like this.
$.post('add.php',$(this).serialize())
.done(function(result){
var r = confirm("This record already exist, do you wish to overright");
if(result == 'exist'){
if (r == true) {
$.post('update.php',$(this).serialize()).done(function(r){
console.log(r);
});
} else {
return false;
}
}else{
console.log(result)
}
});
I several HTML elements that initiate ajax when clicked. How can I check which element was clicked inside the ajaxComplete event?
I tried event.target but it returns the entire document.
$( document ).ajaxComplete(function( event, xhr, settings ) {
if ( settings.url.indexOf("somelink/hello") > -1) {
console.log("return here element that initiated ajax")
}
});
Note: The tricky part - I don't have access to the ajax request that is sent on click. I can't configure the code that makes the request. I can only check when the ajax is complete.
I first need to run the ajaxComplete event then check which element initiated ajax because I need to add some html to that element. For this reason I'm trying to check in the ajaxComplete event.
The $.ajaxComplete() handler is not an object-specific handler; you attach it to the document to be notified whenever any AJAX request completes. From the jQuery docs:
If you must differentiate between the requests, use the parameters passed to the handler. Each time an ajaxComplete handler is executed, it is passed the event object, the XMLHttpRequest object, and the settings object that was used in the creation of the request.
So, since settings is a plain Object, you can extend it with a property that will then be passed to the handler, as you can see below with requestingObjectId. DEMO
var onComplete = function(event, jqXHR, ajaxOptions) {
alert("Requested with " + ajaxOptions.requestingObjectId);
};
var makeRequest = function() {
var id = $(this).attr('id');
$.ajax({
url: '/path/to/server',
data: { foo: 1 },
requestingObjectId: id
});
};
$('button').click(makeRequest);
$(document).ajaxComplete(onComplete);
Best and easiest way is to store the element in a global variable when you send the ajax call. Set it to the event.target.activeElement. Then in your ajaxComplete you can just access that var to change CSS etc.
Upon further consideration, if you use my solution you would have to limit it to one ajax request at a time. Otherwise a new ajax request would overwrite variable if the initial ajax request hadn't completed yet. I'd take Palpatime's answer.
An jQuery AjaxRequests is NOT send by an control itself. It is send by the code that the developer wrotes as event function onto this Dom element.
Means the best thing you can get is the callee of $.ajax() by overwriting and wrapping it. And only if the callee is not written in strict mode.
I would prefer to read the documentation of that framework who is building your controls or if it is built by another company/guy contact them.
As the element is not the direct caller of the $.ajax, I see no other way.
I am currently working on a web based time tracking software. I'm developing in grails, but this question is solely related to javascript and asynchronous requests.
The time tracking tool shall enable users to choose a day for the current month, create one or multiple activities for each day and save the entire day. Each activity must be assigned to a project and a contract.
Upon choosing "save", the partial day is saved to the database, the hours are calculated and a table is updated at the bottom of the page, showing an overview of the user's worked hours per month.
Now to my issue: There may be a lot of AJAX request. Patient users might only click the "create activity" button just once and wait until it is created. Others, however, might just keep clicking until something happens.
The main issue here is updating the view, although i also recognized some failed calls because of concurrent database transaction (especially when choosing "save" and "delete" sequentially). Any feedback on that issue -- requests not "waiting" for the same row to be ready again -- will be apreciated as well, yet this is not my question.
I have an updateTemplate(data, day) function, which is invoked onSuccess of respective ajax calls in either of my functions saveRecord(), deleteRecord(), pasteRecords(), makeEditable() (undo save). Here is the example AJAX call in jquery:
$.ajax({
type: "POST",
url: "${g.createLink(controller:"controller", action:"action")}",
data: requestJson,
contentType:"application/json; charset=utf-8",
async: true,
success: function(data, textstatus) {updateTemplate(data["template"], tag); updateTable(data["table"]);},
});
In the controller action, a JSON object is rendered as a response, containing the keys template and table. Each key has a template rendered as a String assigned to it, using g.render.
Now, what happens when I click on create repeatedly in very short intervalls, due to the asynchronous calls, some create (or other) actions are executed concurrently. The issue is that updateTemplate just renders data from the repsonse; the data to render is collected in the create controller action. But the "last" request action only finds the objects created by itself. I think this is because create actions are run concurrently
I figure there is something I'm either overcomplicating or doing something essentially wrong working with a page that refreshs dynamically. The only thing I found that helps are synchronous calls, which works, but the user experience was awful. What options do I have to make this work? Is this really it or am I just looking for the wrong approach? How can I make this all more robust, so that impatient users are not able to break my code?
*********EDIT:********
I know that I could block buttons or keyboard shortcuts, use synchronous calls or similar things to avoid those issues. However, I want to know if it is possible to solve it with multiple AJAX requests being submitted. So the user should be able to keep adding new activities, although they won't appear immediately. There is a spinner for feedback anyway. I just want to somehow make sure that before the "last" AJAX request gets fired, the database is up to date so that the controller action will respond with the up-to-date gsp template with the right objects.
With help of this Stackoverflow answer, I found a way to ensure that the ajax call -- in the javascript function executed lastly -- always responds with an up-to-date model. Basically, I put the javascript functions containing AJAX calls in a waiting queue if a "critical" AJAX request has been initiated before but not completed yet.
For that I define the function doCallAjaxBusyAwareFunction(callable) that checks if the global variable Global.busy is 'true' prior to executing the callable function. If it's true, the function will be executed again until Global.busy is false, to finally execute the function -- collecting the data from the DOM -- and fire the AJAX request.
Definition of the global Variable:
var Global = {
ajaxIsBusy = false//,
//additional Global scope variables
};
Definition of the function doCallAjaxBusyAwareFunction:
function doCallAjaxBusyAwareFunction(callable) {
if(Global.busy == true){
console.log("Global.busy = " + Global.busy + ". Timout set! Try again in 100ms!!");
setTimeout(function(){doCallAjaxBusyAwareFunction(callable);}, 100);
}
else{
console.log("Global.busy = " + Global.busy + ". Call function!!");
callable();
}
}
To flag a function containing ajax as critical, I let it set Global.busy = true at the very start and Global.busy = false on AJAX complete. Example call:
function xyz (){
Global.busy = true;
//collect ajax request parameters from DOM
$.ajax({
//desired ajax settings
complete: function(data, status){ Global.busy = false; }
}
Since Global.busy is set to true at the very beginning, the DOM cannot be manipulated -- e.g. by deletes while the function xyz collects DOM data. But when the function was executed, there is still Global.busy === true until the ajax call completes.
Fire an ajax call from a "busy-aware" function:
doCallAjaxBusyAwareFunction(function(){
//collect DOM data
$.ajax({/*AJAX settings*/});
});
....or fire an ajax call from a "busy-aware" function that is also marked critical itself (basically what I mainly use it for):
doCallAjaxBusyAwareFunction(function(){
Global.busy = true;
//collect DOM data
$.ajax({
//AJAX SETTINGS
complete: function(data, status){ Global.busy = false; }
});
});
Feedback is welcome and other options too, especially if this approach is bad practice. I really hope somebody finds this post and evaluates it, since I don't know if it should be done like that at all. I will leave this question unanswered for now.
let's say i have the following HTML page.
<A panel to display list of items>
Item A
Item B
Item C
...
<A panel to display details about item>
Details on item X:
I want to write the page so that whenever the user clicks on one of the items, the detail about the selected item will show up on the bottom panel.
However, few problems that I want to solve are:
How should I prevent duplicate requests. (like, user presses links to item A and B before the detail of item A returns from the server).
If I want to display "detail is now loading...", where should I put the code?
you can register click handlers, which fire ajax requests to load the data.
There are multiple ways to handle your concern about not firing multiple requests:
Just unregister the handlers on the other Items when you click one of them, and put them back when your request returns.
You can have an object in the scope of the request handlers that you set a property on, like 'requestInProgress', that you set to true and false appropriately. In other words, use closures.
Displaying 'detail is now loading' is simple -- you can just set the value of the Panel dom, or the innerhtml if you want, to that message before you fire the request, and set the actual returned value when the request returns.
Note that many js libraries, like jQuery, provide an API around making ajax requests that might simplify things. For example, the jQuery.ajax method takes an object literal that can contain the functions 'beforeSend', 'complete', 'success' so you do things before, after, and on the case of success (among other functions, check out http://api.jquery.com/jQuery.ajax/)
You can certainly do what you want with bare-metal js, but libraries can make your life easier.
About duplicate requests, I'd keep the last XMLHttpRequest object inside a lastRequest variable. Whenever I'm sending a new request I'd check this variable. If it exists, call lastRequest.abort(). I'd also check in your success callback that the request object is the same as your lastRequest, otherwise ignore it. In jQuery this would look like:
var lastRequest;
// Your click handler
$(".items").click(function () {
if (lastRequest) lastRequest.abort();
lastRequest = $.ajax({..., success: function (data, status, request) {
if (request != lastRequest) return; // Ignore, it was aborted
// Code to show the detail about the selected item
}});
// Code to show the in-progress message
});
I'm unsure of the best practice for modifying the DOM based on an ajax response. I'll try to let the code do the talking because it's hard to explain.
// page has multiple checkboxes
$("input[type='checkbox']").live('click', function {
var cb = $(this); // for the sake of discussion i need this variable to be in scope
$("form").ajaxSubmit({ dataType: "script" });
}
The server sends a response back, and the js gets eval'd and that means "cb" is out of scope.
What I've done so far is create a couple of helper functions:
var target = undefined;
function setTarget(val) {
target = val;
}
function getTarget() {
return target;
}
And that turns the first snippet of code into this:
// page has multiple checkboxes
$("input[type='checkbox']").live('click', function {
setTarget($(this));
$("form").ajaxSubmit({ dataType: "script" });
}
Then on the server's response I call getTarget where I need to. This seems hackish... Any suggestions?
It's unclear what you're actually trying to do, but I feel like you want to be looking at the success parameter for that AJAX call. The success callback function should execute in parent scope and do what you're looking for.
See 'success' on this page in the jQuery docs.
So what you are trying to do is get the form to submit the content via ajax whenever the user checks/unchecks a checkbox? And because there are several checkboxes, you need to find out which one triggered the submit, so you can change its value to whatever is stored on the server?
If you submit the entire form everytime, why don't you reply with all the checkboxes values, and then change each and every one of them? If not, get the server to reply with the id and the value of the checkbox, then use jquery to find the checkbox with that ID and then change it's value.
How about:
jQuery(function($) {
// give it scope here so that the callback can modify it
var cb,
cbs = $('input[type="checkbox"]');
cbs.live('click', function {
// taking away var uses the most recent scope
cb = $(this);
// disable checkboxes until response comes back so other ones can't be made
cbs.attr('disabled', 'true'); // 'true' (html5) or 'disabled' (xhtml)
// unless you are using 'script' for something else, it's best to use
// a callback instead
$('form').ajaxSubmit({
success : function(response) {
// now you can modify cb here
cb.remove(); // or whatever you want
// and re-enable the checkboxes
cbs.removeAttr('disabled');
}
});
}
});