Just like I would inject new html on a div using google.script.run on a page without reloading, I have spent countless hours trying to do the same but with a google-visualization pie chart, I want to call as many times as needed this google.run.script through a callback.
This is the button with multiple callbacks:
<button onclick="google.script.run.withSuccessHandler(refresh).report(user());">Generate report</button>
OK, this is what user do:
<script>
function user() {
var user = $("input[type=radio]:checked").val();
return user;
}
</script>
Report() simply returns a new array of data for a chart, it will change depending on user.
Finally this is my chart script.
<script>
google.load("visualization", "1", {packages:["corechart"], "callback": refresh});
google.setOnLoadCallback(refresh);
function refresh(e) {
var data = google.visualization.arrayToDataTable(e);
var options = {
title: 'Chart',
is3D: true,
pieSliceText: 'label',
};
var chart = new google.visualization.PieChart(document.getElementById('piechart_3d'));
chart.draw(data, options);
}
</script>
My script works as intended all the way, except the end where I can't get it to update the chart everytime I click the button. Which loads new array data for the chart. I have simplified the code just for this post.
I managed to make it work as intended by parsing the callback value with JSON.parse(e), very hidden for me to figure it out.
Related
After much discussion and R&D, image cropping is not possible with Google APP scripts. So I decided to try one using the Canvas API.
I am trying to pass the value from server script(.gs) to the HTML file and get back the value in the server side script without opening HTML output as in sidebar or model/modelLess dialog box. You can say silently call HTML, complete the process and return the value to server script method.
I tried but getFromFileArg() is not running when i am running the callToHtml().
Is this possible with below script? what you will suggest?
Server side (.gs)
function callToHtml() {
var ui = SlidesApp.getUi();
var htmlTemp = HtmlService.createTemplateFromFile('crop_img');
htmlTemp["data"] = pageElements.asImage().getBlob();
var htmlOutput = htmlTemp.evaluate();
}
function getFromFileArg(data) {
Logger.log(data);
}
crop_img.html template :
<script>
var data = <?= data ?>;
//call the server script method
google.script.run
.withSuccessHandler(
function(result, element) {
element.disabled = false;
})
.withFailureHandler(
function(msg, element) {
console.log(msg);
element.disabled = false;
})
.withUserObject(this)
.getFromFileArg(data);
</script>
You cannot "silently" call the HTML this way, no.
The HTML needs to go to the user and the user is not inside of your web app, but Google's web app (Slides), so you have to play by their rules.
You need to use one of the available UI methods such as showSidebar. You could have the displayed HTML be a spinner or message like "processing..." while the JavaScript runs.
function callToHtml() {
var ui = SlidesApp.getUi();
var htmlTemp = HtmlService.createTemplateFromFile('crop_img');
htmlTemp["data"] = pageElements.asImage().getBlob();
ui.showSidebar(htmlTemp.evaluate());
}
Although I found similar posts elsewhere, I still cannot solve my issue.
I want to load locations on a html sidebar page on google spreadsheet, but the only example I find are hard-coded locations.
Here is an example, on HTML page (I removed API Key): this one works for me.
<body>
<div class="container">
<div id="map_div" style="width: 500px; height: 500px"></div>
</div> <!-- CLOSE CONTAINER -->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load("current", {
"packages":["map"],
"mapsApiKey": "xxxx"
});
google.charts.setOnLoadCallback(drawChart);
function drawChart(arrayToData) {
var data = new google.visualization.arrayToDataTable(
[["Lat", "Long","Nom"],
[45.7660889, 4.794056299999999, "Person1"],
[45.8167227, 4.8341048, "Person2"],
[45.7796433, 4.8037871, "Person3"],
[45.7780849, 4.921768399999999, "Person4"]]
);
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(data, {
showTooltip: true,
showInfoWindow: true
});
}//function drawChart() {
</script>
</body>
And I would like to have something looking like that, where data locations are not hard-coded but comes from spreadsheet data :
google.charts.setOnLoadCallback(drawChart);
var ss = SpreadsheetApp.openByUrl("xxxx");
var datatable = ss.getRange("listNamesAdresses");
function drawChart(arrayToData) {
var dataToArray=document.getElementById("listNamesAdresses");
var data = new google.visualization.arrayToDataTable(
dataToArray
);
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(data, {
showTooltip: true,
showInfoWindow: true
});
}//function drawChart() {
I'm aware this is not correct, but I tried many combinations, still cannot solve it. Your help is welcome !
Here is a sharable example of what I made :
Link to a copy of my Map Test
I adapted it from my spreadsheet but went out of my quota for my API key, so I could'nt test it yet. I hope this will be fine !
Many thanks in advance
EDIT 2 :
I followed ziganotschka's suggestions (thank you very much for your time) : I couldn't apply the HtmlCreateOutputFromFil("index.html") so I stuck to my code for displaying a sidepage Html. For the rest of it : I now have a map (first victory!).
But, it says : "no data points to show".
I checked on values return by getAddresses function, seems OK. For getting easier on it, I changed the function to an easier one : getGeoCodesAndNames. This one returns, as it says, geocode latitude, longitude, and name.
Here are my new code sample and link to the new version of the spreadsheet :
Gs-code :
function getGeoCodesAndNames(){
//get addresses and names list
var namesAddresses=ss.getRange("ListNamesAddresses");
var a_values=namesAddresses.getValues();
Logger.log(a_values);
/* returns
[[Lat, Lon, Name],
[45.7660889, 4.79405629999999, person1],
[45.8167227, 4.8341048, person2],
[45.7796433, 4.8037871, person3],
[45.7780849, 4.921768399999999, person4]]
*/
return a_values;
}//function getGeoCodesAndNames(){
function testMap2(){
var template = HtmlService.createTemplateFromFile("carte2");
var html = template.evaluate();
html.setTitle("Display Map 2").setHeight(550).setWidth(550);
SpreadsheetApp.getUi().showModalDialog(html, "Locations")
}//function testCarte1(){
and HTML code :
<script type="text/javascript">
window.onload = google.script.run.withSuccessHandler(onSuccess).getGeoCodesAndNames();
function onSuccess (arrayToData){
google.charts.load("current", {
"packages":["map"],
"mapsApiKey": "AIzaSyC4WPcWGMZRoqSAfZ0F4RzvWtN6Jy7hmdE"
});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable(arrayToData);
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(data, {
showTooltip: true,
showInfoWindow: true
});
}// function drawChart() {
}//function onSuccess (arrayToData){
</script>
And here is the link to the new spreadsheet version :
TestMap2
Do I need to publish it as a web app if I just want to have a side page ? On previous projects I had, I could add datas from a side-page to a spreadsheet without it. In the oppposite ways, can you confirm I need to do it ? I tried, did not change anything on my current result : maybe I made something wrong.
Many thanks again for your help !
EDIT 3 :
I finally got it : My geocode/address function were not returning a proper format for coordinates, because of two things :
1) I'm using French typing, ie dot are replaced with commas in numbers
2) I had to add one more """ symbol at beginning and ending of each string part in the array.
Here is the correct function (might be improved, but..does the job):
function getGeoCodesAndNames(){
//get addresses and names list
var namesAddresses=ss.getRange("ListNamesAddresses");
var a_values=namesAddresses.getValues();
for (var i=0;i<a_values.length;i++){
for (var j=0;j<a_values[0].length;j++){
var value=a_values[i][j];
if (typeof value == "string"){
a_values[i][j]="\"" + value + "\"";
}
}
}
return a_values;
}//function getGeoCodesAndNames(){
Many thanks to the people who helped me !
To combine using the Visualization API in Javascript with retrieving spreadsheet data with Apps Script - you need to use a WebApp
Use google.script.run to pass data from the serverside to clientside
Sample:
code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("index.html");
}
function getDatafromSheet(){
var ss = SpreadsheetApp.openByUrl("xxxx");
// getNamedRanges()[0] works if you have only one named range
var datatable = ss.getNamedRanges()[0].getRange().getValues();
return datatable;
}
index.html
<body>
<div class="container">
<div id="map_div" style="width: 500px; height: 500px"></div>
</div> <!-- CLOSE CONTAINER -->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
window.onload = google.script.run.withSuccessHandler(onSuccess).getDatafromSheet();
function onSuccess (arrayToData){
google.charts.load("current", {
"packages":["map"],
"mapsApiKey": "AIzaSyDCKzjezYeUDd2ugtFnzokCIpV1YpLmmEc"
});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.arrayToDataTable(arrayToData);
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(data, {
showTooltip: true,
showInfoWindow: true
});
}
}
</script>
</body>
So I'm basically developing an automated click-paste-and-upload system for mutiple texts and files inside a google page.
This method helped me get the instances of objects that I'm looking for: buttons, textboxes, richtextboxes, etc.
Now I want to work with them.
So for example I know the id of a button , and the function subscribed to its click event. How do I trigger the click event from the extension ? I've tried injecting a script with the click event handler (discovered with DOM inspector) at "document_startup" but I don't get an error or anything else.
Here's the content script! The loggerhead function should have inserted the script but I don't think it did. What might be the reason for the blow code not giving anything?
// Runs a function for every added DOM element that matches a filter
// filter -- either function(DOM_node){/*...*/}, returns true or false
// OR a jQuery selector
// callback -- function(DOM_node){/*...*/}
function watchNodes(filter, callback){
observer = new MutationObserver( function (mutations) {
mutations.forEach( function (mutation){
if(typeof filter === "function"){
$(mutation.addedNodes).filter(
function(i){ return filter(this); }
).each(
function(i){ callback(this); }
);
} else {
$(mutation.addedNodes).filter(filter).each(
function(i){ callback(this); }
);
}
});
});
// For every added element, a mutation will be processed
// with mutation.taget == parent
// and mutation.addedNodes containing the added element
observer.observe(document, { subtree: true, childList: true });
}
function loggerhead(node) {
console.log("passhead");
//also inject jquery
var jqueryEl = document.createElement('script');
jqueryEl.setAttribute('src', chrome.extension.getURL('jquery-1.11.1.min.js'));
jqueryEl.setAttribute('type', 'text/javascript');
var scriptEl = document.createElement('script');
scriptEl.setAttribute('src', chrome.extension.getURL('script.js'));
scriptEl.setAttribute('type', 'text/javascript');
node.appendChild(jqueryEl);
node.appendChild(scriptEl);
}
watchNodes("head", loggerhead);
// method not working
//var gmailHead = jQuery("head", document).get(0);
script.js contains the function of subscribed to the click event of the button that I've managed to find through the DOM inspector:
function Cdb(b){return function(){if(Vbb()){return Ddb(b,this,arguments)}else{var a=Ddb(b,this,arguments);a!=null&&(a=a.val);return a}}}
You should try to call the existing click handler like
buttonElement.click()
I have been trying to dynamically generate a check box from a value which is in JSON array from a JSON store.
{"MODULECATOGERY":[{"Menu":"MSU"},{"Menu":"SCHEDULE"},{"Menu":"MARKET_DASHBOARD"},{"Menu":"FE_REFERENCE"},{"Menu":"QC_TOOLS"},{"Menu":"QUICKQC_VOICE"},{"Menu":"QUICKQC_DATA"},{"Menu":"MARKETQC_VOICE"},{"Menu":"MARKETQC_DATA"},{"Menu":"SURGERY"},{"Menu":"FILE_INVENTORY"},{"Menu":"MARKET_TRACKER"},{"Menu":"DRIVE_ROUTE_TRACKER"},{"Menu":"TICKETS"},{"Menu":"TICKET_TRACKER"},{"Menu":"ASSETS"},{"Menu":"METRICS"},{"Menu":"DAILY_STATUS"},{"Menu":"DAILY_PROCESSING"},{"Menu":"WEEKLY_WORKFLOW"},{"Menu":"CUSTOMER_QUESTIONS"},{"Menu":"KPI_PERFORMANCE_METRICS"},{"Menu":"COLLECTION_METRICS"},{"Menu":"OPERATIONS_DASHBOARD"},{"Menu":"PRODUCTION_DASHBOARD"},{"Menu":"SUPPORT_DASHBOARD"},{"Menu":"REVENUE_TRACKER"},{"Menu":"DEPLOYMENT_TRACKER"},{"Menu":"TICKETS"},{"Menu":"TICKET_TRACKER"},{"Menu":"ASSET_MANAGEMENT"},{"Menu":"GENERATE_SHIPMENT"},{"Menu":"SHIPMENT_TRACKER"},{"Menu":"RESOURCES"},{"Menu":"SCHEDULE"},{"Menu":"TRACKER"}]}
How can a get values associated with "Menu" in the above JSON.? If i can have each and every value into an array then i can dynamically assign these to generate a check box group.
Thanks in Advance.
You can iterate your store:
store.each(function(record) {
var menu = record.get('Menu');
});
Edit:
Since you're saying this doesn't work with dynamic data I think you iterate it before it has completed loading. To be sure to handle the iteration after the load you can do the following:
store.on({
//Listener that fires everytime after your store has loaded
load: function() {
store.each(function(record) {
var menu = record.get('Menu');
//do stuff
});
}
});
store.load();
If you only want to execute the code the first time your store loads you can use the callback function on the load() method:
store.load(function() {
store.each(function(record) {
var menu = record.get('Menu');
//do stuff
});
});
I am using OpenLayers to display dynamically loaded images. The code I am using is:
var map;
function init(strURL) {
map = new OpenLayers.Map('map');
var options = { numZoomLevels: 3,
isBaseLayer: true, };
var graphic = new OpenLayers.Layer.Image(
'City Lights',
strURL + "?sc=page",
new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
new OpenLayers.Size(580, 288),
options
);
// graphic.events.on({
// loadstart: function () {
// OpenLayers.Console.log("loadstart");
// },
// loadend: function () {
// OpenLayers.Console.log("loadend");
// }
// });
var jpl_wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://t1.hypercube.telascience.org/cgi-bin/landsat7",
{ layers: "landsat7" }, options);
map.addLayers([graphic, jpl_wms]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.zoomToMaxExtent();
}
I am calling this function on a button click event and passing the strURL (sources of the images) at that time. The result is, with each click a different image is loaded and displayed on the web page but is not clearing the previous image. So I 5 different images on the webpage are shown with 5 clicks and so on.
My javascript knowledge is limited, so my apologies if this is a stupid question. How to stop this behavior? Thanks for any assistance.
Also, I didn't quite understand the lines:
var jpl_wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://t1.hypercube.telascience.org/cgi-bin/landsat7",
{ layers: "landsat7" }, options);
But I know these lines are needed since I'm getting js error if I remove them.
So currently you are calling init(strURL) with each button click? Divide that code into two parts:
On page load, create map and layer objects
On button click, just update URL of existing image layer. Image layer has setUrl(url) method for that: http://dev.openlayers.org/apidocs/files/OpenLayers/Layer/Image-js.html#OpenLayers.Layer.Image.setUrl
Here is sample, that should explain it: http://jsfiddle.net/mEHrN/6/
About var jpl_wms = ... - it creates WMS layer, that should display Landsat imagery (but that URL doesn't seem to work). If you remove it, remember also to remove it from map.addLayers:
map.addLayers([graphic]);
Probably this was reason, why you got JS error.