How can I make many jQuery ajax calls look pretty? - javascript

I'm making many ajax call based web service.
I attached event listeners every dom elements. And every event handlers request ajax call in it.
By the way, my source code getting more dirty and complexity.
I want to reduce boilerplate code and look more simple with ajax calls.
How can I do that effectively?
The sample code looks like this:
button1
button2
button3
button4
$('.button1').on('click', function() {
$.ajax({
url: '/api/1/resource1',
data: {
value1: 'value1',
value2: 'value2'
},
success: function (response) {
$('.some_dom1').html(Handlebars.resource({items:response.items}));
}
});
});
$('.button2').on('click', function() {
$.ajax({
url: '/api/1/resource2',
data: {
value1: 'value1',
value2: 'value2'
},
success: function (response) {
$('.some_dom2').html(Handlebars.resource({items:response.items}));
}
});
});
$('.button3').on('click', function() {
$.ajax({
url: '/api/1/resource3',
data: {
value1: 'value1',
value2: 'value2'
},
success: function (response) {
$('.some_dom3').html(Handlebars.resource({items:response.items}));
}
});
});
$('.button4').on('click', function() {
$.ajax({
url: '/api/1/resource4',
data: {
value1: 'value1',
value2: 'value2'
},
success: function (response) {
$('.some_dom4').html(Handlebars.resource({items:response.items}));
}
});
});
Updated:
Every class name and ajax response handler is not same each other. Example code just shows boilerplate code and complexity. This is not the problem of class name or if else statements.

Make common function like this:
String.prototype.endsWith = function(suffix) {
return this.indexOf(suffix, this.length - suffix.length) !== -1;
};
var doAjax_params_default = {
'url': null,
'requestType': "GET",
'contentType': 'application/x-www-form-urlencoded; charset=UTF-8',
'dataType': 'json',
'data': {},
'beforeSendCallbackFunction': null,
'successCallbackFunction': null,
'completeCallbackFunction': null,
'errorCallBackFunction': null,
};
function doAjax(doAjax_params) {
var url = doAjax_params['url'];
var requestType = doAjax_params['requestType'];
var contentType = doAjax_params['contentType'];
var dataType = doAjax_params['dataType'];
var data = doAjax_params['data'];
var beforeSendCallbackFunction = doAjax_params['beforeSendCallbackFunction'];
var successCallbackFunction = doAjax_params['successCallbackFunction'];
var completeCallbackFunction = doAjax_params['completeCallbackFunction'];
var errorCallBackFunction = doAjax_params['errorCallBackFunction'];
//make sure that url ends with '/'
/*if(!url.endsWith("/")){
url = url + "/";
}*/
$.ajax({
url: url,
crossDomain: true,
type: requestType,
contentType: contentType,
dataType: dataType,
data: data,
beforeSend: function(jqXHR, settings) {
if (typeof beforeSendCallbackFunction === "function") {
beforeSendCallbackFunction();
}
},
success: function(data, textStatus, jqXHR) {
if (typeof successCallbackFunction === "function") {
successCallbackFunction(data);
}
},
error: function(jqXHR, textStatus, errorThrown) {
if (typeof errorCallBackFunction === "function") {
errorCallBackFunction(errorThrown);
}
},
complete: function(jqXHR, textStatus) {
if (typeof completeCallbackFunction === "function") {
completeCallbackFunction();
}
}
});
}
then in your code:
$('.button').on('click', function() {
var params = $.extend({}, doAjax_params_default);
params['url'] = `your url`;
params['data'] = `your data`;
params['successCallbackFunction'] = `your success callback function`
doAjax(params);
});

Use a common class:
button1
button2
button3
button4
add listener to this class:
$('.button').on('click', function() {
//find the index of the element and use it
var btnNumber= $( ".button" ).index(this)+1;
$.ajax({
url: '/api/1/resource'+btnNumber,
data: {
value1: 'value1',
value2: 'value2'
},
success: function (response) {
$('.some_dom'+btnNumber).html(Handlebars.resource({items:response.items}));
}
});
});
You can also use any kind of attribute and use it later for any data or parameter
like :
button1
button2
button3
button4
and then use it for any purpose
$('.button').on('click', function() {
var dVal=$(this).attr('abc');
//use dVal any where you want.
alert(dVal);
});

You can follow this change:
button1
button2
button3
button4
Make use of data-* attribute to hold the specific url for each link which is going to be clicked. Now just one change and you would have a common click event for all ajax calls:
$('.button').on('click', function() {
var url = $(this).data('url');
var index = $(this).index();
$.ajax({
url: url,
data: {
value1: 'value1',
value2: 'value2'
},
success: function(response) {
$('.some_dom'+index).html(Handlebars.resource({items: response.items}));
}
});
});
As per your update in your question you can try doing this:
success: function(response) {
if(url.indexOf('1') != -1){
$('.some_dom1').html(Handlebars.resource({items: response.items}));
}else if(url.indexOf('2') != -1){
$('.some_dom2').html(Handlebars.resource({items: response.items}));
}else if(url.indexOf('3') != -1){
$('.some_dom3').html(Handlebars.resource({items: response.items}));
}
}
In success you have to check if the current url of ajax call is having something different to other calls, so if that matches then you can differentiate it within the if/else if conditions.

You can give a common button class to all tags and you can have a single event handler. Then on the basis of other class name you can make calls using different url and data.
button1
button2
button3
button4
var ajaxFunc = function(url, data, successFun){
$.ajax({
url: url,
data:data,
success: successFun
});
}
$('button').on('click', buttonClick);
function buttonClick (){
var elem = $(event.target),
url='', data;
if(elem.hasClass('button1')){
url = '/api/1/resource1';
data = {
value1: 'value1',
value2: 'value2'
}
ajaxFunc(url, data, successFun1);
} else if(elem.hasClass('button2')){
url = '/api/1/resource2';
data = {
value1: 'value1',
value2: 'value2'
}
ajaxFunc(url, data, successFun2)
} else if(elem.hasClass('button3')){
url = '/api/1/resource3';
data = {
value1: 'value1',
value2: 'value2'
}
ajaxFunc(url, data, successFun3)
}
else if(elem.hasClass('button4')){
url = '/api/1/resource4';
data = {
value1: 'value1',
value2: 'value2'
}
ajaxFunc(url, data, successFun4)
}
}
function successFun1(evt){
}
function successFun2(evt){
}
If you are not doing anything different then you can go for only one success function.

The template for anchor tag can be like this
<a data-url="url" data-dataId="dataKey" data-success="functionName" onclick="ajax_call(this);">button</a>
or
button
Then the javascript and jQuery part.
Store all the parameters needed to pass for each request with a key, this key must match the html data attribute.
var myBigJsonObj = {
data1 : { "foo": "bar" },
data2 : { "foo": "bar", "foo1": "bar1"}
};
A user defined success function. Similarly can have error function.
function success() {
// Do some stuff here
alert('Success');
}
Finally the ajax request call.
function ajax_call(obj) {
var url = obj.getAttribute('data-url');
var data = myBigJsonObj[obj.getAttribute('data-dataID')];
var success = obj.getAttribute('data-success');
$.ajax({
url: url,
data: data,
success: window[success].call();
});
}

You can make an async function that contains the ajax API call. Then from everywhere, you can simply call and use the 'then' method for success and error response implementation.
Like here I will give you a very basic example:
// req is JSON type
// SERVICE_URL is global declared service URL or you can pass it as an argument
async function apiCall(req) {
var form_data = new FormData;
for (var key in req) form_data.append(key, req[key]);
let myPromise = new Promise(function (myResolve, myReject) {
$.ajax({
url: SERVICE_URL,
type: 'POST',
data: form_data,
processData: false,
contentType: false,
success: function (result) {
myResolve(result);
},
error: function (error) {
myReject(error);
}
});
});
return await myPromise;
}
Then from everywhere where you are required to use this ajax call use in asynchronous way, like:
// form is the form reference passed from onsubmit method from HTML
// username & password are input fields with 'name' attribute as 'username' & 'password'
function saveForm(form) {
event.preventDefault();
const req = {
username: form.username.value,
password: form.password.value
}
apiCall(req,).then(
function (value) { console.log('async success:', value) },
function (error) { console.log('async error:', error) }
)
}
This is a completely asynchronous implementation and since it's a common function hence is a most cleaner way to do it.

Related

Working with two URLs in ajax

I have to get values from two different URLs and then to merge it. I know it would much better if i'll get all of the data in one URL, but that's how i've got and i need to work with it.
I want to print out the value of a_value, but it's been printed out while b hasn't returned his value. I've read some articles of how to make the functions synchronous but still don't know how to implement it into my code, and don't know what is the best solution for my case. I'm pretty new with JavaScript and still need some help and guiding.
function any_function() {
$.ajax(
{
url : '/url1',
type: "GET",
success:function(data, textStatus, jqXHR)
{
$("#print").html(a(data));
}
});
}
function a(data){
x = 'any value' //`do something with data and insert to this variable`
a_value = x + b(`some id that extracted from data`)
return a_value
}
function b(id){
$.ajax({
url: '/url2',
type: 'GET',
success: function (data, textStatus, jqXHR) {
b_value = c(data, id)
}
});
return b_value
}
function c(data, id){
//do something with `data` and return the value
return c_value
}
function f() {
var request1 = $.ajax({
url : '/url1',
type: 'GET'
});
var request2 = $.ajax({
url: '/url2',
type: 'GET'
});
$.when(request1, request2).done(function(result1, result2){
data1 = result1[0]
data2 = result2[0]
// r1 and r2 are arrays [ data, statusText, jqXHR ]
// Do stuff here with data1 and data2
// If you want to return use a callback or a promise
})
}
This can be done in a synchronous-looking fashion with promises:
$.get(url1)
.then(function(data1){
return $.get(url2)
})
.then(function(data2){
return $.get(url3);
})
.then(function(data3){
// All done
});
You just need to make the second call in the success handler of the first one:
function any_function() {
$.ajax({
url : '/url1',
type: "GET",
success:function(data, textStatus, jqXHR) {
$("#print").html(a(data));
b("someId");
}
});
}
function a(data){
x = 'any value' //`do something with data and insert to this variable`
a_value = x + b(`some id that extracted from data`)
return a_value;
}
function b(id){
$.ajax({
url: '/url2',
type: 'GET',
success: function (data, textStatus, jqXHR) {
b_value = c(data, id);
return b_value;
}
});
}
function c(data, id){
//do something with `data` and return the value
return c_value
}

Reusing a JavaScript AJAX call before another AJAX call

I have two buttons that both performs AJAX call:
$("#save").click(function() {
$.ajax({
type: "POST",
url: saveEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
})
$("#tag-as-final").click(function() {
$.ajax({
type: "POST",
url: finalizeEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
})
The requirement is that when the user click the finalize button, the system will first perform a save before actually tagging it as final. To reuse the code attached to the save button, I call the onclick listener of the save button before the actual AJAX call like this:
$("#tag-as-final").click(function() {
$("#save").click()
^^^^^^^^^^^^^^^^^^
$.ajax({
type: "POST",
url: finalizeEntryURL,
But it will not do "save-and-finalize-after" behavior since both AJAX calls are asynchronous. I need to run one after another, but cannot afford to make the AJAX call of the save button synchronous (I'm doing also a lot of other things while the tagging occurs occurs). I know this would be silly but I'm thinking something similar to...
$("#tag-as-final").click(function() {
$("#save").click().peformAsyc()
^^^^^^^^^^^^
$.ajax({
type: "POST",
url: finalizeEntryURL,
...that will force it to finish performing first the chained function before continuing, but I know that is not available. Is there any way to do this? My current work-around is placing the same save AJAX function inside the finalize AJAX function, though it doesn't allow me to code DRY (Don't Repeat Yourself):
$("#tag-as-final").click(function() {
$.ajax({
type: "POST",
url: saveEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
$.ajax({
type: "POST",
url: finalizeEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
},
error: function(r) {
...
}
})
})
It's pretty simple, you are better using jquery "promises". Like so:
var generalSettings = { }; //Settings for AJAX call.
var jqXHR = $.ajax(generalSettings); //Do AJAX call.
generalSettings.data = 'newdata'; //update generalSettings
jqXHR.done(function(data){
$.ajax(generalSettings); //New Petition with updated settings.
});
This is using ES6 promises and jQuery promises:
function doAjaxAsPromise(settings){
return new Promise(function(resolve){
var jqXHR = $.ajax(settings);
jqXHR.done(function(data){
resolve(data);
});
});
}
var settings = { };
var petition = doAjaxAsPromise(settings);
var secondpetition = petition.then(function(data){
//work with data
//new settings
var settings = { };
return doAjaxAsPromise(settings);
});
var thirdpetition = secondpetition.then(function(data){
//work with data
//new settings
var settings = { };
return doAjaxAsPromise(settings);
});
//If needed to reuse settings object outside promise scope:
//var settings = Object.create(settings);
Some other nice thing you can do for code reuse:
function save(settings) {
var prom = doAjaxAsPromise(settings);
return prom.then(function(data){
//do something with your data.
});
}
function tagAsFinal(savedPromise, settings){
return savedPromised.then(function(){
var prom = doAjaxAsPromise(settings);
return prom.then(function(data){
//work with data;
});
});
}
$('save').on('click', function(){
save(settings); //settings = $.ajax settings.
});
$('tagAsFinal').on('click', function(){
var generalSettings = { };
var settingsone = Object.create(generalSettings);
var settingstwo = Object.create(generalSettings);
var saved = save(settingsone); //$.ajax settings.
tagAsFinal(saved, settingstwo);
});
//Can still be reduced.

How to use q.js to chain backbone model?

I have the following:
var q = new app.models.OverwriteLineItemsProcess();
q.set('id', $("#process_id").val());
q.saveSource($("#source_quote").val());
q.lockSource();
saveSource is sending data to the backend using ajax. So is lockSource.
I want to execute in this SEQUENTIAL manner: saveSource >> lockSource.
How do I write the q.js to make it work?
By q.js, I mean https://github.com/kriskowal/q
UPDATE: added saveSource and lockSource
saveSource: function (quotation_id) {;
var type = "PUT";
var verb = "Updated";
var headers = {
'X-HTTP-Method-Override': type
};
var url = app.base_url + "/overwrite_line_items/" + this.id;
this.set('source_quote', quotation_id);
var data = this.toFormData();
var result = false;
var currentModel = this;
var settings = {
headers: headers,
type: type,
url: url,
data: data,
success: function(json) {
response = JSON && JSON.parse(json) || $.parseJSON(json);
console.log(response);
currentModel.lockSource();
$("#facebox-source-quote-status").html('<font color="green">SELECTED</font>');
},
error: function(response) {
$("#facebox-source-quote-status").html('<font color="red">UNABLE TO SELECT</font>');
},
dataType: 'json'
};
$.ajax(settings).done(function() {
});
},
lockSource: function () {
var type = "PUT";
var verb = "Updated";
var headers = {
'X-HTTP-Method-Override': type
};
var url = app.base_url + "/quotations/is_editable/" + this.attributes.source_quote;
var data = this.toFormData();
var result = false;
var currentModel = this;
var settings = {
headers: headers,
type: type,
url: url,
data: data,
success: function(response) {
console.log(response);
},
error: function(response) {
$("#facebox-source-quote-status").html('<font color="red">UNABLE TO SELECT</font>');
},
dataType: 'json'
};
$.ajax(settings).done(function() {
});
},
The jQuery.ajax function which you're using already returns a promise for its result. You just need to return that from your functions:
saveSource: function (quotation_id) {;
…
var settings = {
headers: headers,
type: type,
dataType: 'json', // jQuery will automatically parse it for you
url: url,
data: data
};
return $.ajax(settings).done(function() {
// ^^^^^^
$("#facebox-source-quote-status").html('<font color="green">SELECTED</font>');
// notice I did remove the currentModel.lockSource(); call from the callback
}, function() {
$("#facebox-source-quote-status").html('<font color="red">UNABLE TO SELECT</font>');
});
},
lockSource: function () {
…
var settings = // analoguous, no callbacks here
return $.ajax(settings).fail(function(response) {
$("#facebox-source-quote-status").html('<font color="red">UNABLE TO SELECT</font>');
});
}
Now you can easily chain them:
var q = new app.models.OverwriteLineItemsProcess();
q.set('id', $("#process_id").val());
q.saveSource($("#source_quote").val()).then(function(saveResponse) {
console.log(saveResponse);
return q.lockSource();
}).done(function(lockResponse) {
console.log(lockResponse);
});
You don't even need Q for that. If you want to use it, wrap the $.ajax() calls in a Q() invocation, as explained in the Converting JQuery Promises to Q section of the docs.

Loading remote data only once with Select2

As the title suggests I would like to load remote data once only.
I thought about loading a data with independent ajax call and set it "locally" at the control but wonder if there is more "built in" way to do so...
a solution can be found here:
https://github.com/ivaynberg/select2/issues/110
$("#selIUT").select2({
cacheDataSource: [],
placeholder: "Please enter the name",
query: function(query) {
self = this;
var key = query.term;
var cachedData = self.cacheDataSource[key];
if(cachedData) {
query.callback({results: cachedData.result});
return;
} else {
$.ajax({
url: '/ajax/suggest/',
data: { q : query.term },
dataType: 'json',
type: 'GET',
success: function(data) {
self.cacheDataSource[key] = data;
query.callback({results: data.result});
}
})
}
},
width: '250px',
formatResult: formatResult,
formatSelection: formatSelection,
dropdownCssClass: "bigdrop",
escapeMarkup: function (m) { return m; }
});
Edit:
I might have misinterpreted your question. if you wish to load all data once, then use that is Select2, there is no built in functionality to do that.
Your suggestion to do a single query, and then use that stored data in Select2 would be the way to go.
This is for Select2 v4.0.3:
I had this same question and got around it by triggering an AJAX call and using the data returned as the initialized data array.
// I used an onClick event to fire the AJAX, but this can be attached to any event.
// Ensure ajax call is done *ONCE* with the "one" method.
$('#mySelect').one('click', function(e) {
// Text to let user know data is being loaded for long requests.
$('#mySelect option:eq(0)').text('Data is being loaded...');
$.ajax({
type: 'POST',
url: '/RetrieveDropdownOptions',
data: {}, // Any data that is needed to pass to the controller
dataType: 'json',
success: function(returnedData) {
// Clear the notification text of the option.
$('#mySelect option:eq(0)').text('');
// Initialize the Select2 with the data returned from the AJAX.
$('#mySelect').select2({ data: returnedData });
// Open the Select2.
$('#mySelect').select2('open');
}
});
// Blur the select to register the text change of the option.
$(this).blur();
});
This worked well for what I had in mind. Hope this helps people searching with the same question.
To load data once:
Assumptions:
You have a REST API endpoint at /services that serves a JSON array of objects
The array contains objects which have at least a "name" and "id" attribute. Example:
[{"id": 0, "name": "Foo"}, {"id": 1, "name": "Bar"}]
You want to store that array as the global 'services_raw'
First, our function to load the data and create the global 'services_raw' (AKA 'window.services_raw'):
fetchFromAPI = function() {
console.log("fetchFromAPI called");
var jqxhr = $.ajax(
{
dataType:'json',
type: 'GET',
url: "/services",
success: function(data, textStatus, jqXHR) {
services_raw = data;
console.log("rosetta.fn.fetchServicesFromAPI SUCCESS");
rosetta.fn.refreshServicesSelect();
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("Error inside rosetta.fn.fetchServicesFromAPI", errorThrown, textStatus, jqXHR);
setTimeout(rosetta.fn.fetchServicesFromAPI(), 3000); // retry in 3 seconds
}
}
)
.done(function () {
console.log("success");
console.log(jqxhr);
})
.fail(function () {
console.log("error");
})
.always(function () {
console.log("complete");
});
// Perform other work here ...
// Set another completion function for the request above
jqxhr.always(function () {
console.log("second complete");
});
};
Second, our Select2 instantiation code which transforms our data into a format that Select2 can work with:
refreshServicesSelect = function () {
// ref: http://jsfiddle.net/RVnfn/2/
// ref2: http://jsfiddle.net/RVnfn/101/ # mine
// ref3: http://jsfiddle.net/RVnfn/102/ # also mine
console.log('refreshServicesSelect called');
$("#add-service-select-service").select2({
// allowClear: true
data: function() {
var arr = []; // container for the results we're returning to Select2 for display
for (var idx in services_raw) {
var item = services_raw[idx];
arr.push({
id: item.id,
text: item.name,
_raw: item // for convenience
});
}
return {results: arr};
}
});
};
Here's what the Select2 element in HTML should look like before your call the above functions:
<input id="add-service-select-service" type="hidden" style="width:100%">
To use all of this, call (in JS):
window.fetchFromAPI();
window.refreshServicesSelect();
Lastly, here's a JSFiddle where you can play with a similar thing: http://jsfiddle.net/RVnfn/102/
Basically, in my example above, we're just using ajax to populate the equivalent of window.pills in the Fiddle.
Hope this helps :)
Please reply if you know how to do this via the Select2 .ajax function, as that would be a bit shorter.
In my condition, it is working perfectly with the given code
$('#itemid').select2({
cacheDataSource: [],
closeOnSelect: true,
minimumInputLength: 3,
placeholder: "Search Barcode / Name",
query: function(query) {
// console.log(query);
self = this;
var key = query.term;
var cachedData = self.cacheDataSource[key];
if(cachedData) {
query.callback({results: cachedData});
return;
} else {
$.ajax({
url: "./includes/getItemSelect2.php",
data: { value : query.term },
dataType: 'json',
type: 'POST',
success: function(data) {
self.cacheDataSource[key] = data;
query.callback({results: data});
}
});
}
},
});
And my data return from the ajax is in this form
<?php
$arr = [
["id" => 1, "text" => "Testing"],
["id" => 2, "text" => "test2"],
["id" => 3, "text" => "test3"],
["id" => 4, "text" => "test4"],
["id" => 5, "text" => "test5"]
];
echo json_encode($arr);
exit();
?>

Ajax, prevent multiple request on click

I'm trying to prevent multiple requests when user click on login or register button. This is my code, but it doesn't work. Just the first time works fine, then return false..
$('#do-login').click(function(e) {
e.preventDefault();
if ( $(this).data('requestRunning') ) {
return;
}
$(this).data('requestRunning', true);
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
success: function(msg) {
//stuffs
},
complete: function() {
$(this).data('requestRunning', false);
}
});
});
Any ideas? Thanks!
The problem is here:
complete: function() {
$(this).data('requestRunning', false);
}
this no longer points to the button.
$('#do-login').click(function(e) {
var me = $(this);
e.preventDefault();
if ( me.data('requestRunning') ) {
return;
}
me.data('requestRunning', true);
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
success: function(msg) {
//stuffs
},
complete: function() {
me.data('requestRunning', false);
}
});
});
Use on() and off(), that's what they are there for :
$('#do-login').on('click', login);
function login(e) {
e.preventDefault();
var that = $(this);
that.off('click'); // remove handler
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize()
}).done(function(msg) {
// do stuff
}).always(function() {
that.on('click', login); // add handler back after ajax
});
});
In your ajax callbacks the context (this) changes from the outer function, you can set it to be the same by using the context property in $.ajax
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
context: this, //<-----
success: function(msg) {
//stuffs
},
complete: function() {
$(this).data('requestRunning', false);
}
});
You can disable the button.
$(this).prop('disabled', true);
I have also faced a similar problem.
Just adding $('#do-login').attr("disabled", true); gives me the solution.
$('#do-login').click(function(e) {
e.preventDefault();
$('#do-login').attr("disabled", true);
.........
.........
Here do-login is button id.
I've tried this and worked very fine for me, I was having trouble that $.ajax send more request until results return,
var settings = {
"url": "/php/auth/login.php",
"method": "POST",
"timeout": 0,
"async": false,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"data": jsondata, //data pass here is in JSON format
};
$.ajax(settings).done(function (ress) {
try{
console.log(ress, "Result from Ajax here");
}
catch(error){
alert(error);
console.log(ress);
}
});
async : false worked for me.
Thanks.
Or you can do it by $(this).addClass("disabled"); to you button or link and after click is performed, you can $(this).removeClass("disabled");.
// CSS
.disabled{
cursor: not-allowed;
}
// JQUERY
$('#do-login').click(function(e) {
e.preventDefault();
$(this).addClass("disabled");
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
context: this,
success: function(msg) {
//do more here
$(this).removeClass("disabled");
},
});
});
P.S. If you use bootstrap css, you do not need the css part.
I found the approach useful. I've implemented it as a general purpose function for jQuery with ES6.
export default function (button, promise) {
const $button = $(button);
const semaphore = 'requestRunning';
if ($button.data(semaphore)) return null;
$button.data(semaphore, true);
return promise().always(() => {
$button.data(semaphore, false);
});
}
Because $.ajax() returns a promise, you simply pass in the promise and the function takes care of the rest.
Roughly speaking, here's the usage.
import preventDoubleClick from './preventdoubleclick';
...
button.click(() => {
preventDoubleClick(this, () => $.ajax()
.done(() => { console.log("success") }));
});
This function can help you with control multi Ajax requests and it's has timeout function which can return flag status to 0 after ex. 10sec (In case the server took more than 10 seconds to respond)
var Request_Controller = function(Request_Name = '', Reactivate_Timeout = 10000)
{
var a = this;
a.Start_Request = function(){
if(window.Requests == undefined){
window.Requests = {};
}
window.Requests[Request_Name] = {'Status' : 1, 'Time': + new Date()};
}
a.End_Request = function(){
if(window.Requests == undefined){
window.Requests = [];
}
window.Requests[Request_Name] = undefined;
}
a.Is_Request_Running = function(){
if(window.Requests == undefined || window.Requests[Request_Name] == undefined){
return 0;
}else{
var Time = + new Date();
// Reactivate the request flag if server take more than 10 sec to respond
if(window.Requests[Request_Name]['Time'] < (Time - Reactivate_Timeout))
{
return 0;
}else{
return 1
}
}
}
}
To use it:
var Request_Flag = new Request_Controller('Your_Request_Name');
if(!Request_Flag.Is_Request_Running()){
Request_Flag.Start_Request();
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
success: function(msg) {
//stuffs
},
complete: function() {
Request_Flag.End_Request();
}
});
}
for prevent multiple ajax request in whole site. For example: If use ajax request in other ajax page, Using ajax in php loop, etc, Give you multiple ajax request with one result. I have solution:
Use window.onload = function() { ... }
instead of
$(document).ready(function(){ ... });
on the main index.php page. Its will be prevent all multi request. :)

Categories