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)
Related
I have been writing many web apps with php & mysql & jquery & bootstrap and now it's time address this problem. How to write shorter ajax queries(posting) ?
If I want to write code that works and takes care of many problems, it's too long for every ajax call.
Is there a better way or some library / wrapper that makes the code SHORTER and FASTER to write, but does atleast all these stuff
I looked popular axios, but it seems even worse
//JUST an example code, too complicated
var $btnStatusElem = $("#passwordreset").button('loading');
$.ajax({
type: "POST",
cache: false,
url: "pwreset.php",
data: postdata
success: function(data) {
$btnStatusElem.button('reset');
try {
var datajson = JSON.parse(data);
}
catch (e) {
alert('Unexpected server error');
return false;
};
if (datajson['success'] == true) {
//do the OK stuff
} else {
//show the error code, and stuff
return false;
}
},//success
error: function(msg) {
alert('ERROR');
$('#passwordreset_result').html(msg);
}
});
For my code, ajax query, i want it to do these steps:
1. Disable the submit button while posting (re-enable also after 15 seconds and not just leave it disabled until page refresh)
2. It sends json, expects json to return
3. If server has some error, it DOES NOT return json but error. Then the code will halt all js execution if i dont use try...catch. This is pain to write each time
4. If server returns validation error or some other expected error, i have to detect this and show to the user
5. If all ok, do the stuff
As with any refactoring, identify and isolate the repetitive code and pass in the unique bits. In this case, for example, you could isolate the ajax call and json parsing into a function and pass in the url, data, etc.
That function could return a promise that resolves/rejects as appropriate.
Given the doRequest function below (pseudocode, untested and would probably need a bit of tweaking for real-world use), you could then use it all over the place with fewer keystrokes:
doRequest('pwreset.php', postdata, button)
.then(result => {
// do something with the result
})
.catch(error => {
// deal with the error
});
or
try {
const result = await doRequest('pwreset.php', postdata);
// do something with result
}
catch (e) {
// handle error
}
All of the boilerplate stuff is isolated in doRequest.
async function doRequest(url, data, button, type = "POST") {
return new Promise((fulfill, reject) => {
$.ajax({
type,
url,
data,
cache: false,
success: function(data) {
$btnStatusElem.button('reset');
try {
const datajson = JSON.parse(data);
} catch (e) {
return reject(e);
};
return datajson['success'] == true ?
fulfill(datajson) :
reject(datajson);
}, //success
error: function(msg) {
return reject(msg);
}
});
})
}
As #mister-jojo says, you might also want to consider using the [fetch api] instead of jQuery, but the same principle applies.
Trying to solve problem were my object has old snapshot of itself and refuses to update itself when I replace it with new data using AJAX.
var user, status;
function getData(){
var userData = getUserData(),
orderStatus = getOrderStatus(),
allDone = $.when(userData, orderStatus);
allDone.then(function(data, data2){
status = parseInt(data2[0]);
user = JSON.parse(data[0]);
console.log("User", user);
});
}
function getUserData() {
return $.ajax({
type: "POST",
url: functionsPath,
data: {
action: 'getUserData'
}
});
}
function getOrderStatus() {
return $.ajax({
type: "POST",
url: functionsPath,
data: {
action: 'getOrderStatus'
}
})
}
//onclick event
function verify(){
console.log(user);
}
Here is screenshot of the problem: This first one is from console. I call this inside getData() function. Take a closer look into user.content.page4
Next image is taken from network tab. As you can see data is different when it not. There are "symbols" with values. Why are these two different?
This is problem when I try to call user within verify() since it has the old snapshot of itself and doesn't return my symbols etc. It really feels like a console bug. Symbols are listed in database.
EDIT
I think I got more closer to the problem. Result loses its "Symbol-1", "Symbol-2" and so on immediately when I parse it. So when I parse user.content page4 loses Symbol properties.
Before parsing I have it, but soon as I parse it I lose every symbol properties and their values.
First of all, I apologize if this question sounds similar to others. I've done a lot of research and I guess I can't piece together my situation and issues other people have with this API.
I am creating an chrome app that basically demonstrates the ability to store and retrieve data from the Chrome Storage API. Once I'm comfortable doing this, I can implement this in a larger application I'm making.
However, I can't seem to get the basic app working. Here's the relevant code:
"storage.js"
function Store(key,data) {
console.log("Data: " + data);
chrome.storage.local.set({ key: data }, function () {
console.log("Secret Message Saved!");
});
}
function Retrieve() {
chrome.storage.local.get(function (data) {
console.log("The data stored is:" + data);
});
}
"master.js" (main script)
var myKey = "secretMessage";
var myData = "Pssst!";
Store(myKey, myData);
Retrieve(myKey);
console.log("Done!");
I'm at a loss. The output I get is:
Data: Pssst!
Done!
Secret Message Saved!
The data stored is:[object Object]
It appears that either I'm storing wrong or retrieving wrong.
I've looked through the documentation. Maybe I'm just not understanding the concept of the API and what it is able to store and retrieve. I'm fairly new to coding in the javascript language. Any help would be much appreciated!
Thanks!
First,
chrome.storage.local.set & chrome.storage.local.get
are asynchronous methods, you have to wait till chrome.storage.local.set stores data in storage, then you should call chrome.storage.local.get.
Second,
chrome.storage.local.get is returning an object. You can view object by using .toString() or JSON.stringify(data)
function Store(key,data, callback) {
console.log("Data: " + data);
chrome.storage.local.set({ key: data }, function () {
console.log("Secret Message Saved!");
callback(true);
});
}
function Retrieve(success) {
chrome.storage.local.get(function (data) {
console.log("The data stored is:" + JSON.stringify(data));
});
}
var myKey = "secretMessage";
var myData = "Pssst!";
Store(myKey, myData, Retrieve);
console.log("Done!");
I've used this callback method without a problem.
function loadSelectedStyle(){
chrome.storage.local.get('defaultColorSet', function (result) {
console.log(result.defaultColorSet);
defaultColors(result.defaultColorSet);
});
}
If you just write 'result' to the console log, you can see the entire object, by adding the key to result you get the data.
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
});
Not sure if my question is subjective/objective but as a JavaScript newbie i'm encountering this problem quite a lot. So here I go.
I'm used to write C#, so my JavaScript structure looks like C#. And just that, that gives problems I think ;-)
Let's give a simple example where I met my problem again today:
MyLibrary.fn.InitAddEntityForm = function () {
$('a#btnAddEntity').click(function () {
//post data and receive object with guid and isPersisted boolean
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png");
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
};
//////*****/////
//SOME FUNCTION THAT SENDS MY FORM AND RETURNS AN OBJECT WITH TRUE VALUE AND POSTED ENTITY ID
/////*****//////
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
}, "json"
);
return persistedObject;
};
Okay, thats it. Everything looks okay right? Browser says no.
I tried to debug it using firebug, looping over my code line by line, and that way the browser does what I want: Execute a new function to show the next panel in my wizard.
After placing a lot of Console.logs() in my code I figured out that this must be something about timing in JavaScript. In C# the code executes line by line, but apparently JavaScript doesn't.
By placing that Console.log("test") I noticed that "test" appeared in my console before "Post status: Success!".
So here's my question, how should I write my JavaScript code so I have control over the way the browser executes my code?
Should I really replace the code below to the end of my CheckAndSendAddEntityForm()?
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("fout");
}
Is this how I have to write JavaScript: One big domino effect or am I just doing something wrong?
$.post is a shortcut for an AJAX call, AJAX is by definition asynchronous, which means it won't wait on a response before continuing processing. If you switch it to a regular AJAX() method, there is an async option you can set to false, which will make it behave as you are expecting.
Alternatively you can also define a function to execute on successful return of the AJAX request, in which you can call the next step in your process chain.
The AJAX call is asychronous; that means that the callback method exposes by $.post will be executed when the request completes, but your javascript will continue executing as soon as the invoke to $.post finishes. If you want to do something after the ajax call is done, you need to provide a callback method and do something else, ex:
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl, callback) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
callback(); // This is where you return flow to your caller
}, "json"
);
};
Then you invoke like so:
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png", function()
{
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject .gdPronoId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
JavaScript is single-threaded. If you have asynchronous functionality, a simple boolean semaphore variable will help not to allow invocations of a function while some processes are running.
If you want to execute asynchronous tasks one by one (like a domino line), you will need to use callback functions.
What you're encountering is the "asynchronous" bit of AJAX. If you want to physically (as in the line line by line in the Javascript file) you can use the .success,.pipe or .done jQuery methods to add a callback to process the data further. Don't embed your callbacks if you can help it, or you will get a "domino effect" as you call it.