DISCLAIMER - I've reviewed the existing SO entries and cobbled together something that should work but still does not.
I have the following function. Basically it is sending a pair of values to a webservice with the results coming back in JSON:
getPicklist: function () {
var xhrArgs = {
url: 'myUrl',
postData: dojo.toJson({
'opportunityId': 'myOppId',
'loggedInUserId': 'myUserId' //App.context.user.$key
}),
headers: {
"Content-Type": "application/json"
}
}
var deferred = dojo.xhrPost(xhrArgs);
deferred.then(
function (data) {
var jsonResponse = dojo.fromJson(data);
picklistName = jsonResponse.PicklistName;
if (!picklistName) {
picklistName = "defaultPickListName";
}
return picklistName;
},
function (error) {
alert("Could not load picklist " + error);
});
;
//return picklistName; -- null
}
My understanding after reading this:
anonymous js function with xhrpost dojo not returning data
Was that adding a variable outside of this function scope, along with using dojo.deferred, would fix the issue. I tried placing a var outside of the function, and assigned the object to the picklistName variable.
However, I still cannot get this function's result (the picklistName variable).
Can someone please clarify what I am doing wrong, and how I can fix it?
EDIT - After making the changes Thomas Upton suggested, I am closer but I am getting a strange error.
I added the following function after getPicklist:
returnPicklistName: function () {
this.getPicklist().then(function (picklistName) {
return picklistName;
})
},
Because all I really want is the picklist (there's JSON that I want really but I will settle just for the picklist for now).
This throws the following error in Chrome dev tools - Uncaught TypeError: Object [object Object] has no method 'getPicklist'.
What else did I miss? Thanks.
Instead of returning picklistName at the end of getPicklist, you need to return a promise -- here, the result of then() -- and add a callback that will receive the picklistName when the deferred resolves.
getPicklist: function () {
// ...
var deferred = dojo.xhrPost(xhrArgs);
return deferred.then(
function(data) { /* get picklistName from data */ return picklistName; },
function(error) { /* ... */ }
);
}
Then, when you call getPicklist:
this.getPicklist()
.then(function(picklistName) {
// Use picklistName here
});
Related
I'm trying to send a "large" table in OfficeJS:
functionfile.html loaded from manifest route
<script>
(function (){
"use strict";
Office.initialize = function (reason) {
$(document).ready(function() {
$("#send-data-button").click(send_data);
});
};
function send_data() {
return Excel.run( function(context) {
var data = context.workbook.worksheets.getItem("SheetName")
.getRange("A1:K3673").load("values");
return context.sync().then( function() {
// 2d table is correctly seen
// $("body").append(data.values);
// Just gets lost in ajax call
$.ajax({
type: "GET",
url: mysite,
data: {"accessData": data.values},
}).done( function(success) {
$("body").append("All Done");
}).fail( function(error) {
$("body").append("Error == " + JSON.stringify(error));
});
return context.sync();
});
});
}
})();
</script>
<div> <button id="send-data-button"> Send </button></div>
However i'm not sure how to send this, on the backside I have a flask server catching the request and was hoping I could just use pandas.read_json but no matter how I try to send this i'm getting different errors. Here's the printout of flask.request when data.values[0][0]:
CombinedMultiDict([ImmutableMultiDict([('update_date', '43191'), ('accessData', 'Channel')]), ImmutableMultiDict([])])
And when I try data.values[0] I get a list of values, which is what i'd expect
CombinedMultiDict([ImmutableMultiDict([('update_date', '43191'), ('accessData[]', 'Channel'), ... <All my column headers>, ImmutableMultiDict([])])
But when I try to send the 2D array with just data.values I get an error message in ajax.fail:
Error == {"readyState":0,"status":0,"statusText":"error"}
I also tried JSON.stringify(data.values) and got the same error message:
Error == {"readyState":0,"status":0,"statusText":"error"}
I even tried to take each column and convert them to some kind of list as nested keys inside accessData but I was getting the same error message. Any help would be greatly appreciated.
Ideally, you should isolate the getting-data-from-Excel part from your ajax call part. Right now, the two are intertwined, which makes it both harder to help debug, and just conceptually less clean.
For the Excel part, you should be able to do:
function getExcelData(){
return Excel.run( function(context) {
var data = context.workbook.worksheets.getItem("SheetName")
.getRange("A1:K3673").load("values");
return context.sync()
.then(function() {
return data.values;
});
})
}
This will free you up to then do:
getExcelData().then(function(values) {
$.ajax(...)
});
Note that range.values returns just a regular 2D array, nothing special. So you can try out your ajax call independently of the Excel call (which is yet another reason to separate those out)
I have the following function to check a users session to see if they're staff or not. Now, I know there are better ways to do this, but I'm trying to make a simple application that's tied with a forum software.
function isStaff(callback) {
$.ajax({
url: url
}).done(function(data) {
var session = $.parseJSON(data);
if (session.is_staff === 1) {
callback(true);
} else {
callback(false);
}
});
}
Let's say I'm using this function in, like so, when compiling a "post" (Handlebars).
function compilePost(post) {
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: function() {
isStaff(function(response) {
return response;
});
}
}
var html= template(context);
return html;
}
Problem here, is that the request to check if a user is staff doesn't complete the request until after the function is ran.
I know with Promises is an alternative to async: false, where request is made and the response comes back before the function finishes.
But I have no idea how I can convert this into a promise. I've tried to learn it but I'm stuck at the concept. Can someone explain this to me? Thanks.
First, let's simplify the compilePost function. This function should know how to compile a post in a synchronous manner. Let's change the isStaff fetching to a simple argument.
function compilePost(post, isStaff) {
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: isStaff
}
var html= template(context);
return html;
}
Now, let's create a new method, with a single purpose - checking if a user is member of the staff:
function checkForStaffMemebership() {
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
success: function (data) {
var session = $.parseJSON(data);
if (session.is_staff === 1) {
resolve(true);
} else {
resolve(false);
}
}
});
});
}
This function wraps your original ajax call to the server with a promise, whenever the $.ajax call gets a response from the server, the promise will resolve with the answer whether the user is a staff member or not.
Now, we can write another function to orchestrate the process:
function compilePostAsync(post) {
return checkForStaffMemebership()
.then(function (isStaff) {
return compilePost(post, isStaff);
});
}
compilePostAsync finds out whether the user is a staff member or not. Then, it's compiling the post.
Please notice that compilePostAsync returns a promise, and thus if you used to have something like:
element.innerHTML = compilePost(post);
Now, you should change it to something like:
compilePostAsync(post).then(function (compiledPost) {
element.innerHTML = compiledPost;
});
Some notes:
This is only an example, it surely misses some things (proper error handling for example)
The isStaff and checkForStaffMemebership (original and new) do not get any argument, I guess you'd figure out how to pass the userId or any other data you might need
Read about promises, it's a useful tool to have, there is a lot of data about it on the web, for example: MDN.
As per the documentation you dont need to wrap the ajax with a promise which already implements promise. Instead chain the response as explained below.
The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving them all the properties, methods, and behavior of a Promise (see Deferred object for more information)
You can do something like below by chaining the response:
function isStaff(url, post) {
return $.ajax({
url: url,
dataType:"json"
}).then(function(resp){
//resp = $.parseJSON(resp); /*You dont require this if you have respose as JSON object. Just Specify it in 'dataType'*/
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: resp.is_staff === 1 ? true : false
};
return template(context);
});
}
isStaff(url, post).done(function(template){
/*Your compiled template code is available here*/
}).fail(function(jqXHR, textStatus, errorThrown){
console.log("Error:"+textStatus);
});
Note: Be sure to implement error callbacks also. Because you may never know what
went wrong :)
Simple explanation about promise with $.defer:
For understanding i have created the Fiddle similar to your requirement.
Explanation:
Basically Promise is been introduced to attain synchronous execution of asynchronous JS code.
What do you mean by Async or Asynchronous code?
The code that is executed may return a value at any given point of time which is not immediate. Famous example to support this statement would be jquery ajax.
Why is it required?
Promise implementations helps a developer to implement a synchronous code block which depends on asynchronous code block for response,. like in ajax call when i make a request to server asking for a data string, i need to wait till the server responds back to me with a response data string which my synchronous code uses it to manipulate it , do some logic and update the UI.
Follow this link where the author has explained with detailed examples.
PS: Jquery $.defer implements or wraps promise in quite a different way. Both are used for the same purpose.
let basedataset = {}
let ajaxbase = {};
//setting api Urls
apiinterface();
function apiinterface() {
ajaxbase.createuser = '/api/createuser'
}
//setting up payload for post method
basedataset.email = profile.getEmail()
basedataset.username = profile.getGivenName()
//setting up url for api
ajaxbase.url = ajaxbase.createuser
ajaxbase.payload = basedataset;
//reusable promise based approach
basepostmethod(ajaxbase).then(function(data) {
console.log('common data', data);
}).catch(function(reason) {
console.log('reason for rejection', reason)
});
//modular ajax (Post/GET) snippets
function basepostmethod(ajaxbase) {
return new Promise(function(resolve, reject) {
$.ajax({
url: ajaxbase.url,
method: 'post',
dataType: 'json',
data: ajaxbase.payload,
success: function(data) {
resolve(data);
},
error: function(xhr) {
reject(xhr)
}
});
});
}
A solution using async await in js would be like this:
async function getMyAjaxCall() {
const someVariableName = await ajaxCallFunction();
}
function getMyAjaxCall() {
return $.ajax({
type: 'POST',
url: `someURL`,
headers: {
'Accept':'application/json',
},
success: function(response) {
// in case you need something else done.
}
});
}
I have this angular controller:
app.controller('FeedCtrl', function ($scope, Profile) {
$scope.getID = function() {
Profile.getUID()
.then(function (data) {
if (data !== null) {
console.log(data.id); // returns correct id
$scope.data = data;
} else {
console.log("Could not retrieve id");
}
}, function (error) {
console.log(error);
});
console.log($scope.data); // logs: undefined
return $scope.data; // logs: undefined
};
var somedata = $scope.getID();
console.log(somedata); //just returns undefined
});
And this Factory that the controller uses for a JSON request.
module.factory('Profile', function($http, $localStorage) {
return {
getUID:function(){
return $http.get("https://graph.facebook.com/v2.2/me", {params: { access_token: $localStorage.accessToken, fields: "id,name,gender,location,website,picture,relationship_status", format: "json" }})
.then(function(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return $q.reject(response.data);
}
}, function(response) {
// something went wrong
return $q.reject(response.data);
});
}
};
});
The Question
I am unable to change the value of $scope.data for use outside the $scope.getID function but inside the rest of the FeedCtrl.
If you look on the comments you see what I am getting returned in the console logs.
I've tried to understand this problem by searching here in StackOverflow and Google but it seems that I don't understand the $scope concept of AngularJS.
I am grateful for any push in the right direction.
That's a classic mistake, the code you're calling is asynchronous, look at your console and watch the order of your logs, the log that will return the correct id will be the last one because it will be called only after the promise has been resolved.
Is this enough of a push in the right direction for you?
A very simple example, but it's the same principle.
setTimeout(function(){
document.write('A2: Timeout is done');
}, 5000);
document.write('A1: Called timeout, this gets logged before A2 even though my line number is higher.');
That is not a scope problem, it's a time problem. You are trying to use the value before it exists.
In the controller you can only use the value after the result has arrived, i.e. in the callback for the then method. The return statement runs before there is a result, so you can't return it.
Once you have made an asynchronous call, the result has to be handled asynchronously. You can return a Future object to handle the result when it arrives, but you can never make a function that makes an asynchronous call and returns the result itself.
Your code is asynchronous. I wrote a response to this exact same problem in this post.
Returning after ajax call prints false
i have SendRequest object and that class has a function like
request: function(args)
{
return $.ajax.apply(null, args);
};
then a lot of class use SendRequest object to get server response
var prom = SendRequest.request(
{
type: 'GET',
url: _this.uri
});
return $.when(prom).done(function(response)
{
.... do something
});
My goal is in SendRequest.request need to check on window.localStorage first.
Whether already has a value or not, if has not any value before then send the request.
Otherwise, if already value on localStorage then return $.ajax() object with that saved value before.
request: function(args)
{
var ls = window.localStorage;
var savedResponse = ls.getItem(args.url);
if (savedResponse !=== null)
{
var result = $.ajax();
result.responseText = savedResponse;
result.readyState = 4;
result.status = 'OK';
return result;
}
else
{
return $.ajax.apply(null, args);
}
};
but unfortunately its did not work :(
I've been looking but can not find some case like me
I already try this way to
how to fool jqXHR to succeed always
but its not help much
The reason this doesn't work is that although you've created a fake jqXHR object that's the response from $.ajax, that object isn't actually the parameter that's supplied to the .done callback - it's actually the third parameter.
Also, IMHO you shouldn't really use $.when to "promisify" a single non-promise object. It's intended to handle synchronisation between multiple promises and the fact that it wraps each non-promise into a new promise is just a side effect.
Instead, you should create a new promise that is already "resolved" with the appropriate data:
if (savedResponse != null) {
return $.Deferred(function() {
// retrieve relevant fields from localStorage
...
this.resolve([data, textStatus, jqXHR]);
}).promise();
} else {
// perform real AJAX request
return $.ajax.apply($, args);
}
Alternatively, consider just memoizing the return data itself (for successful calls), not the whole response triple.
You may also find this presentation that I gave at jQuery UK 2013 of use - http://www.slideshare.net/RayBellis/memoizing-withindexeddb
Can any one help me what im wrong in
function separateerror()
{
var jqxhr = $.get("/errormsg.txt", function(data) {
line = data;
array = line.split(',');
getmsg=array[0];
})
return getmsg;
}
i need to return "getmsg" to another function, but its not working, where as if i add alert inbetween
function separateerror()
{
var jqxhr = $.get("/errormsg.txt", function(data) {
line = data;
array = line.split(',');
getmsg=array[0];
})
//alert(getmsg)
return getmsg;
}
the value returns, what wrong im doing in code?
$.get is an async call and what happens is that if you add an alert in between, you give time for the async call to finish and then your value is already set by the time the separateerror function finishes. Otherwise, the separateerror function returns before the $.get call finishes and then your getmsg is probably still a null value.
While working with async calls you should not use this simple model of calling functions and expecting theirs values in return, you should use callback functions to accomplish that the right way.
EDIT:
Also, you should handle the $.get error callback, in case something happens and you can not load the txt file.
function separateerror()
{
var jqxhr = $.get("/errormsg.txt", function(data) {
line = data;
array = line.split(',');
getmsg=array[0];
dosomething(getmsg);
})
.error(function() { alert("error"); });
}
function dosomething(msg) { ... }
Insert that to the top of your script. That /should/ fix it.
$.ajaxSetup({
async: false
});