Working with two URLs in ajax - javascript

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
}

Related

Returning the correct promise in angular

I'm having trouble returning the correct promise for a service in angular.
this is my function:
postToSP.post($scope.sharePointURL, data).then(function() {
$scope.gettingData = false;
$scope.yammerListName = "Successfully posted to SP";
}).catch(function(e){
//console.log("Error: ", e);
$scope.yammerListName = "Sorry we couldn't post to that page, please make sure your column names are EXACTLY the same!"
$scope.gettingData = false;
throw e;
});
And this is my service, i get the error: "Unable to get property 'then' of undefined or null reference". I know it's because i'm not returning the promise properly but I can't figure out how to do it correctly. Please help, thanks in advance.
app.service("postToSP", function($http) {
//Submit to SP function
this.post = function(originalurl,data){
console.log(data);
var url = originalurl.split("Lists/")[0];
var listname = originalurl.split("Lists/")[1].split("/")[0];
//if the row is checked send it, if not jump to the next row
//run the function, continue until the end and break
var i = 0;
return letsPost(i);
function letsPost (i) { //i<data.length; i++
if (data[i].checked == false) {
i++;
return letsPost(i);
} else {
var formattedText = document.getElementById("text"+i).innerHTML.toString() ;
var formattedCreated = document.getElementById("created"+i).innerHTML.toString();
var formattedLikes = document.getElementById("likes"+i).innerHTML.toString();
var formattedLinks = document.getElementById("links"+i).innerHTML.toString();
var uploadData = { //change this for input data
'__metadata': { 'type': 'SP.Data.' + listname + 'ListItem' },
'Title': i + "",
'Likes': formattedLikes,
'Post_x0020_Date': formattedCreated,
'Post_x0020_Links' : formattedLinks,
'Post_x0020_Text': formattedText
};
console.log(uploadData);
createListItem(url, listname, uploadData)
.done(function (columnData) {
console.log('Added row' + i);
// if there is more data
if (i < data.length) {
i++;
return letsPost(i);
//add new data and continue the function
} else {
return;
}
})
.fail(function (error) {
console.log(JSON.stringify(error));
alert("Error:" + JSON.stringify(error));
throw error;
});
//Function to get form digest token
function getFormDigest(webUrl) {
return $.ajax({
url: webUrl + "/_api/contextinfo",
method: "POST",
headers: { "Accept": "application/json; odata=verbose" }
});
};
//Function to create the list item
function createListItem(webUrl, listName, itemProperties) {
$.ajax({
url: url + "/_api/web/lists/getbytitle('" + listName + "')/items",
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
console.log(data.d.results);
},
error: function (data) {
console.log(data);
}
});
return getFormDigest(webUrl).then(function (data) {
return $.ajax({
url: webUrl + "/_api/web/lists/getbytitle('" + listName + "')/items",
type: "POST",
processData: false,
contentType: "application/json;odata=verbose",
data: JSON.stringify(itemProperties),
headers: {
"Accept": "application/json;odata=verbose",
"X-RequestDigest": data.d.GetContextWebInformation.FormDigestValue
}
});
});
};
};
};
};
});
in your function declare the promise first
this.post = function(originalurl,data){
var deferred = $q.defer();
the data that you want to return use
deferred.resolve(dataToReturn)
and at the end of your function add
return deferred.promise;
From what I understand your code, mistake you are doing is you are returning the promise returned from getFormDigest but also applying then function on it and returning another promise. If you dont return getFormDigest nothing will be returned since its async.
To solve it you can use angular $q library and return and independent promise. Resolve that promise in your then function where you are returning a promise and no need to return getFormDigest so only one promise will be returned and hopefully your problem will be resolved.
In simple way you can achieve it..i hope it make sense
//in your controller
yourService.addData(yourPayload);
.then(function (cou) {
$scope.data = cou.data;
});
//in your service
this.addData = function (data) {
var response = $http({
method: "POST",
url: 'your url',
data: data,
dataType: "json"
});
return response;
}

javascript promise return recursively to index

Ok, so I've got a loop like so:
underscore.each(dom.paramArray, function(value, i) {
fetchDataFromServerWithParams(value, i);
});
In my current example it loops 3 times, idealy like: 0,1,2
However, when I log the index of the function called, it logs: 1,0,2, why ?
And how can I get it to call the function recursively, so first it will process the function with index:0, then index:1, and lastly, index:2
I think it has something to do with the functions I am calling (buildResult and buildSubResult), but Im really not sure?
The function that is called by the loop looks like:
function fetchDataFromServerWithParams(param, index) {
//Create promise
let getData = $.ajax({
headers: {
Accept : "text/plain; charset=utf-8",
"Content-Type": "text/plain; charset=utf-8"
},
type: "GET",
url: configuration.apiEndpoint,
data: { id: param},
dataType: "json"
});
//When done processing promise, build result
getData.then(function (data) {
let generatedData;
console.log(index);
if(index === 0) {
generatedData = buildResult(data);
} else {
$.each($("ul.material a"), function( index, value ) {
var target = $(this).parent();
if($(this).data("id") == param) { //Refactor later to ===
target.parent().addClass("open-folder");
target.parent().parent().find("ul").addClass("open-folder");
generatedData = buildSubResult(data, target);
}
});
}
}), function(xhr, status, error) {
// Handle errors for any of the actions
handleError(error);
};
}
you can use async library or any other library for this purpose
also you can change the line
getData.then(function (data) {
to
return getData.then(function (data) {
and use this code instead of your underscore loop
(function sequenceCall(index){
fetchDataFromServerWithParams(dom.paramArray[index], index).then(function(){
sequenceCall(index+1);
});
})(0);

How can I make many jQuery ajax calls look pretty?

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.

How to change javascript variable at appropriate scope based on success/failure of ajax get request

I want to edit a javascript variable based on whether or not a file exists. Can I achieve something like this?
var url="Content/Features/column1.html";
var url2="";
$.ajax({
url: url,
error: function()
{
url2 = "something";
},
success: function()
{
url2 = "something else";
}
});
My understanding is that the typical issue here is the async in ajax. Should I be using a callback method or a promise in some fashion?
Maybe something like:
var url="Content/Features/column1.html";
var url2="";
var result = $.ajax({
url: url,
error: function()
{
url2 = "something";
handleData(url2);
},
success: function()
{
url2 = "something else";
handleData(url2);
}
});
response = function handleData( responseData ) {
return responseData;
}
Or even:
var url="Content/Features/column1.html";
var url2="";
function test(url) {
return $.ajax(
{
url: url
}
);
}
test().done(function(url2) {
url2= "something"
}).fail(function() {
url2= "something else"
});
How can I change url2 at the same scope in which it was declared? Or is there a way to return a value from a callback function and change url2 based on that?
Alternatively, is there a better way to check if this file exists? (And before you tell me that browsers don't allow ajax requests to local files, please know that I am using node-webkit, not a browser)
None of these attempts worked quite right, but please let me know if I'm close. Thank you so much for your time.
Since AJAX is async in nature you cannot return a value from your function and instead need to implement a callback. You could do something like this:
function getURL2(callback) {
callback = callback || function() {};
var url="Content/Features/column1.html";
var url2="";
$.ajax({
url: url,
error: function()
{
callback("something");
},
success: function()
{
callback("something else");
}
});
}
getURL2(function(url2) {
// the call is complete and url2 is set correctly!
}
Try
var url1 = "Content/Features/column1.html"
, res = {"url2": void 0} // `this` `context` within `$.ajax` call at `request`
, request = function request(url1, res) {
return $.ajax({
context: res // set `this` to `res` object
, url: url1
, method: "GET"
, success: function(data, textStatus, jqxhr) {
this.url2 = "something"; // `success`
return this // return `this` : `res`
}
, error: function(jqxhr, textStatus, errorThrown) {
this.url2 = jqxhr.status === 404 // `404` , `Not Found`
? "somethingElse" // `error`
// if `error` other than `404`,
// return `jqxhr.status`
: jqxhr.status;
return this // return `this` : `res`
}
});
};
request(url1, res).always(function(response) {
// `this` : `res`
console.log(response.status
, response.statusText
, response.state()
, this.url2); // if `error` , `somethingElse` , else `something`
});
See jQuery.ajax( [settings ] ) at context , deferred.always()
var url1 = "https://gist.githubusercontent.com/anonymous/9a6997f09de9b68c59b2/"
+ "raw/f7d7b756005ad6d2b88cf0211f78a2990d7d2dc7" + "123" // `404` `error`
, res = {"url2":void 0}
, request = function request(url1, res) {
return $.ajax({
context: res
, url: url1
, method: "GET"
, success: function(data, textStatus, jqxhr) {
this.url2 = "something";
return this
}
, error: function(jqxhr, textStatus, errorThrown) {
this.url2 = jqxhr.status === 404
? "somethingElse"
: jqxhr.status;
return this
}
});
};
request(url1, res).always(function(response) {
console.log(response.status
, response.statusText
, response.state()
, this.url2);
document.write(JSON.stringify(this))
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

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.

Categories