How to use one AJAX handler for multiple purposes with callbacks? - javascript

I have a piece of code written in an object. You can see there is a customers object with a function for adding a new customer and a method for making AJAX calls
var sys = {
customers: {
addNew: function(ref, cb = null) {
if (!cb) { // so it can check if the call to this method was for requesting ajax request or handling its response . note i am sending the callback function reference same as the current
core.request({
d: $('form').serialize()
}, 'sys.customers.addNew', ref);
} else {
if (ref.status) {
$('.customers-list').append('<li>' + ref.customer.name + '</li>');
alert('success')
}
}
},
updateRowAfterAdd: function() {
// or i could use this for handling callback by passing its reference instead of the upper same function
}
},
request: function(p = {}, c = null, e = false) {
$.ajax({
url: "/to/my/server",
data: {
p: p
},
type: 'post',
dataType: 'json',
beforeSend: function() {
},
success: function(r) {
if (c != null)
(e ? eval("(" + c + "(r,e));") : eval("(" + c + "(r));"));
}
});
}
}
$(document).on('click', '.addNew', function() {
sys.customers.addNew($(this));
});
The idea in this example is to call the AJAX method by passing a callback function reference for handling the success response.
If you look at the addNew() method it is working in two ways. With the help of the second parameter, cb, it is determining that the call to this function was for sending an AJAX request or handling its response back.
I'm using eval() in the success callback which I know is evil, so I want to understand how I can do this without using eval()?
I have multiple things running on my page which need AJAX calls and I don't want to rewrite each of them.
I also need this for AJAX's beforeSuccess() method as well.

The design pattern you're using seems to be a needless abstraction which is causing more problems that it solves.
A better idea would be to have a central 'service' layer which makes the requests to your server side and handles the responses. If you wanted to abstract this further you could have other domain logic abstractions to handle AJAX requests and responses through a single class, however at that stage I would argue you're far better off using an existing framework to do this for you.
A strong recommendation would be to use Angular, given that its MVC pattern is where you're heading anyway.
If you did want to roll your own simplistic version, then a simple example would look something like this:
$(document).on('click', '.addNew', function() {
services.customers.save($('form').serialize());
});
// in a service layer JS file, far away from UI logic...
let services = {
customers: {
save: requestData => {
$.ajax({
url: '/to/my/server',
type: 'post',
dataType: 'json',
data: $('form').serialize(),
success: services.customers.renderUi
});
},
renderCustomerUi: customerData => {
// optional: extract the UI update logic to your UI layer and pass in the callback as an argument
if (customerData.status) {
$('.customers-list').append('<li>' + customerData.customer.name + '</li>');
}
}
}
}

Related

Why is my Ajax callback being processed too soon?

I have a general ajax function which I'm calling from loads of places in my code. It's pretty standard except for some extra debugging stuff I've recently added (to try to solve this issue), with a global 'ajaxworking' variable:
rideData.myAjax = function (url, type, data, successfunc) {
var dataJson = JSON.stringify(data),
thisurl = quilkinUrlBase() + url;
if (ajaxworking.length > 0) {
console.log(thisurl + ": concurrent Ajax call with: " + ajaxworking);
}
ajaxworking = thisurl;
$.ajax({
type: type,
data: dataJson,
url: thisurl,
contentType: "application/json; charset=utf-8",
dataType: "json",
async: true,
success: function (response) {
ajaxworking = '';
successfunc(response);
},
error: webRequestFailed
});
};
Now, there's one section of my code where a second ajax call is made depending on the result of the first:
getWebRides = function (date) {
var rideIDs = [];
var intdays = bleTime.toIntDays(date);
rideData.myAjax("GetRidesForDate", "POST", intdays, function (response) {
rides = response;
if (rides.length === 0) {
$('#ridelist').empty(); // this will also remove any handlers
qPopup.Alert("No rides found for " + bleTime.DateString(date));
return null;
}
$.each(rides, function (index) {
rideIDs.push(rides[index].rideID);
});
GetParticipants(rideIDs);
});
},
'GetParticipants' (which also calls 'myAjax') works fine - most of the time. But in another part of my code, 'GetWebRides' is itself called directly after another ajax call - i.e. there are 3 calls, each successive one depending on the previous. The 'top-level' call is as follows:
rideData.myAjax("SaveRide", "POST", ride, function (response) {
// if successful, response should be just a new ID
if (response.length < 5) {
// document re-arrangement code snipped here for brevity
getWebRides(date);
}
else {
qPopup.Alert(response);
}
});
so, only when there are three successive calls like this, I'm getting the 'concurrent' catch in the third one:
GetParticipants: concurrent call with GetRidesForDate
and (if allowed to proceed) this causes a nasty probem at the server with datareaders already being open. But why is this only occurring when GetParticipants is called as the third in the chain?
I see, after some research. that there are now other ways of arranging async calls, e.g. using 'Promises', but I'd like to understand what's going on here.
Solved this.
Part of the 'document re-arrangement code' that I had commented out for this post, was in fact calling another Ajax call indirectly (very indirectly, hence it took a long time to find).

Prevent duplicate $.ajax calls using result cache

A similar question has been asked before, but I don't believe it overcomes the challenges in this case because my function calls are all together, so please bear with me (I'll delete the question if appropriate).
I have a number of dashboard widgets that each make an $.ajax call, receive a JSON result and then process that to render a Google chart. The widgets can be used multiple times, so there are some duplicated AJAX calls occurring, e.g.
RenderChart('/api/LoginCount?DaysPrevious=7', 'ColumnChart'); // some parameters removed, for brevity
RenderChart('/api/LoginCount?DaysPrevious=7', 'AreaChart');
RenderChart('/api/LoginCount?DaysPrevious=7', 'Table');
The problem is that this generates multiple calls to the same URL, which is extremely wasteful. I saw in the linked question that an object can be used to cache the results, but when I applied this, it didn't seem to work because the second call to RenderChart (immediately after the first) saw there was no data (yet) in the cache, and called the URL again.
My code is:
function LoadDataFromApi(apiUrl) {
return $.ajax({
type: 'GET',
url: apiUrl,
dataType: "json",
success: function (data) { }
});
}
function RenderChart(apiUrl, chartElementId, chartType, chartOptions) {
$.when(LoadDataFromApi(apiUrl)).done(function (data) {
var el = $('#' + chartElementId);
try {
var arrayOfArrays = BuildGoogleArrayFromData(data); // Transform JSON into array of arrays (required by Google Visualization)
$(el).empty();
if (arrayOfArrays.length == 0) { // Data found?
$(el).append('<p class="noData">No data was found.</p>');
} else {
var wrapper = new google.visualization.ChartWrapper({ // alert(wrapper.getChartType()); // wrapper is the only way to get chart type
chartType: chartType,
dataTable: google.visualization.arrayToDataTable(arrayOfArrays, false),
options: chartOptions,
containerId: chartElementId
});
wrapper.draw();
}
}
catch (ex) {
$(el).append('<p class="error">An error occurred: ' + ex.message + '</p>');
}
});
}
Ideally it would be good to cache the arrayOfArrays value, as at this point all additional processing is also complete. However, getting JavaScript to see what other API calls are in progress, and wait for them is where I'm struggling. Is this possible to achieve?
If anyone can handhold me into achieving both I'll put a bonus on the question. I read about promises, but I need to support IE9+.
I can think of making a cache map with URL as its key, and the AJAX request as its value. We can change your LoadDataFromApi function to leverage this cache, and return appropriate AJAX request, if exists, else make a new request.
Following is a snippet of how it can be done.
var requestCache = {};
function LoadDataFromApi(apiUrl) {
if (!requestCache[apiUrl]) {
requestCache[apiUrl] = $.ajax({
type: 'GET',
url: apiUrl,
dataType: "json"
});
}
return requestCache[apiUrl];
}
This way, you can call LoadDataFromApi without any limit, and chain your promise handlers like this:
LoadDataFromApi('http://fake.url')
.then(function(data) {
// use the data in one widget
})
LoadDataFromApi('http://fake.url')
.then(function(data) {
// use this data in another widget
})
// ... and so on
This way the AJAX call for a particular URL will be made only once, and the result will be shared among the promise handlers.

Passing the 'response' or 'data' AJAX return into a namespace function

I am trying to clean up my code by namespacing which is new to me. I have a very basic app that makes an Ajax request and then does a load of stuff with the response. The code is starting to look messy so I want to namespace it and then call the namespace function that takes the AJAX response as the argument.
1. Can and should this be done?
2. If so how, here is the code
var GETDATA = {
myAlert: "this variable is the property of a namespace",
// response?: ???? do i need to declare response var here somehow
myNSFunction: function () {
alert(this.myAlert)
}
//theFunctionIWant: function (response??) {
// takes the response from ajax request
// does some stuff to it.
//}
};
$(document).ready(function() {
$( "#my-form" ).submit(function( event ) {
$.ajax({
type: 'GET',
url: "http://localhost:3000/DATA"
})
.done(function(response) {
//GETDATA.theFunctionIWant();
});
});
Any help much appreciated.
var GETDATA = {
myAlert: "this variable is the property of a namespace",
myNSFunction: function () {
alert(this.myAlert)
},
theFunctionIWant: function (response) {
console.log(response);
}
};
$(document).ready(function () {
$("#my-form").submit(function (event) {
$.ajax({
type: 'GET',
url: "http://localhost:3000/DATA"
})
.done(function (response) {
GETDATA.theFunctionIWant(response);
}
});
});
Should you do this?
Difficult to pass a judgment without understanding your overall goal and the scale of things, but mostly, no. You have to ask yourself, what benefit does this really offer? I mean, the handler could even be a function itself, not necessarily namespaced. What is the justification for namespacing it?
Will you have a group of response processing functions that are similar? Are you trying to build a store of received responses and related functions that act on the responses? Are you worried about conflicts resolution of some sort that requires a namespace to resolve with? etc.
A better approach: Consider closures for a private scope.
var RequestProcessors = (function() {
var thisIsPrivate = "Ok",
thisIsPrivateToo = "Fine";
return {
processorOne: function(resp, arg) { /* logic here */ },
processorTwo: function(resp, arg) { /* logic here */ }
}
})();

How to call multiple PHP functions through ajax, best practices?

I have some ajax calls in multiple JavaScript functions. Each one does a post / get to a functions.php file
The functions.php has multiple functions that should correspond with the ones from JavaScript.
For example in js I have:
function one() {
$.ajax({
type: 'POST',
url: 'http://www.example.com/functions.php',
data: vals,
dataType:'json',
success: function (data) {
alert(data);
}
});
}
function two() {
$.ajax({
type: 'POST',
url: 'http://www.example.com/functions.php',
data: othervals,
dataType:'json',
success: function (data) {
alert(data);
}
});
}
function three() {
$.ajax({
type: 'GET',
url: 'http://www.example.com/functions.php',
data: { get_param: 'user' },
dataType:'json',
success: function (data) {
alert(data);
}
});
}
In PHP I have something like this:
if (isset($_POST['city'])) { // this comes from the javascript vals json object
$city = $_POST['city'];
function one() {
// do something
return true;
}
}
function two() {
// do something
return true;
}
if (isset($_GET['get_param']) && $_GET['get_param'] == 'user'){
function three() {
// do something
return true;
}
}
Maybe the PHP side is a bit confusing the way I write it, but in the end I want the function one to only deal with the corespondent function from the PHP file. Obviously they don't need to have the same name.
The PHP functions can return true or false or 1 or 0, and that suppose to be the alerted data alert(data);.
If there is more confusion on what I want please let me know and I'll clarify.
Why not split the PHP functions in separate smaller scripts? They stand for different endpoints in your application and cannot be called together, so they should not be together.
Have you checked out a REST library style. It looks like your doing basically that but a bit more confusing. When i say REST library i do not mean a purely RESTful library, but instead each function in your back-end is navigable via url.
The way your doing it is fine (as long as the functions data does not depend on any other function data (as it could lead to some funny results)). Its just a lot easier to do more of a restful approach. Its fairly simple to set up a rest library.
I just find that doing the whole $_POST[...] and then keep doing it is just cumbersome over time and becomes harder and harder to manage, because there is always some new twist on what is needed then you end up with 100's of methods for taking care of calling back end functions.
MyApi = {
/**
* The API Version
*/
API_VERSION: "0.5",
SITE_URL: "http//myurl.com/",
/**
* The API URL
*/
apiURL: function(tokens) {
return MyApi.SITE_URL + MyApi.API_VERSION + "/api/" + MyApi.apiTokenizer(tokens);
},
/**
* The tokenizer for the API.
*/
apiTokenizer: function(tokens) {
var str = '';
$.each(tokens, function(key, value) {
str += value + "/";
})
return str;
}
}
Thats the javascript for producing new api links, then you could have something like
function callBackend(tokens) {
$.ajax({
type: 'POST',
url: MyLibrary.apiURL(tokens),
dataType:'json',
success: function (data) {
alert(data);
}
});
}
On your backend you would need an .htaccess file like so
RewriteRule 0\.5/(.*) api/_MyApi.php?v=0\.5&state=$1 [QSA]
Then you could write a back end switch statement that would take apart the state (delimiters would be "/") that would navigate you to the end time library call.
<?php
$state = MyParser::Parse($_REQUEST["state"]);
$output = array();
switch ($state) {
case "case1":
//do stuff
break;
}
echo json_encode($output);
?>
That way output is always handled the same way.
Just as a note. That was a very VERY simple and INCOMPLETE implementation, but i find that its a lot easier to maintain than a $_POST that goes to 1 of 100 different files that all have very similar output and all of that.
Cheers! Happy coding

Extjs ajax code refactor

I have this kind of ajax code repeated lot of places. How can I refactor this into a single method so it will still allow different behavior on success or failure.
Ext.Ajax.request({
url : 'ajax.php' ,
params : { action : 'getDate' },
method: 'GET',
success: function ( result, request ) {
Ext.MessageBox.alert('Success', 'Data return from the server: '+ result.responseText);
},
failure: function ( result, request) { Ext.MessageBox.alert('Failed', result.responseText);
}
});
MyAjaxRequest = Ext.extend ( Ext.Ajax.request, {
url : 'ajax.php' ,
params : { action : 'getDate' },
method: 'GET',
success: function ( result, request ) {
Ext.MessageBox.alert ('Success', 'Data return from the server: '+ result.responseText);
},
failure: function ( result, request) {
Ext.MessageBox.alert('Failed', result.responseText);
}
} );
by extending class (namespaces up to you) you still able to manipulate url, params, method, success, and failure. if not setup - defaults are there
Okay, this question is kind of old, but there arguably a more flexible way to do this. It's really important to realize that Ext.Ajax is a singleton -- that is, it is already a unique pre-instantiated class. "Extending" a singleton doesn't make much sense, and a separate function may be unnecessarily confusing and/or limiting later on.
You can add your own special Ajax request function like this:
Ext.Ajax.dateRequest = function(myObj){
// set the pre-configured parameters here
myObj.url = 'ajax.php';
myObj.params = { action: 'getDate'};
myObj.method = 'GET';
// call the original request function with the modified config object
this.request(myObj);
};
So now you can change your repeated Ajax requests to:
Ext.Ajax.dateRequest({
success: yourSuccessFunction
,failure: yourFailureFunction
});
The benefit to this is that you can easily add pre-configured parameters to your "dateRequest" function, AND you can add addition parameters to each Ajax request (like a different timeout) without rewriting anything.
EDIT: Yikes! I originally posted a solution below that I thought would "clone" Ext.Ajax, but it still merely overrode the singleton.
This is a quote by "Saki" (Ext guru) a couple of years ago. He's referring to a clone function he wrote for regular object/arrays:
The clone function is in no case meant to clone classes or
instantiated Ext objects. It is almost impossible as these install
event handlers almost always so cloning would definitely lead to
unpredictable results.
The singleton is a "instantiated Ext object" and thus cannot be extended or cloned easily. If you don't want to mess with Ext.Ajax directly, you can create a function (as already mentioned). Here is a somewhat more flexible form:
function dateRequest(myObj){
myObj.url = 'ajax.php';
myObj.params = { action: 'getDate'};
myObj.method = 'GET';
return Ext.Ajax.request(myObj);
}
Then call it with dateRequest({success: successFunc, failure: failFunc}).
This code will achieve the same result:
function callme (callback) {
Ext.Ajax.request({
url : 'ajax.php' ,
params : { action : 'getDate' },
method: 'GET',
success: callback,
failure: function ( result, request) { Ext.MessageBox.alert('Failed', result.responseText);
}
});
}
callme(function ( result, request ) {
Ext.MessageBox.alert('Success', 'Data return from the server: '+ result.responseText);
});

Categories

Resources