Open a HTML file with data using Google Script - google-apps-script

I'm trying to use google script to display a bunch of data in a HTML file, however, my data doesn't seem to make it to the HTML file and I have no idea why. Can someone please tell me what I'm missing here?
Path: htmlList.html
<!DOCTYPE html>
<html>
<head>
<base target="_top" />
</head>
<body>
My HTML page
<? for(var i = 0; i <= (users.length -1); i++) { ?>
<p><?= users[i].firstName ?></p>
<? } ?>
</body>
</html>
Path: Code.js
function doGet(users) {
var html = HtmlService.createTemplateFromFile("htmlList");
html.users = users;
return html.evaluate().setTitle("Test my app");
}
function generateLinks() {
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var rr = spreadSheet.getLastRow();
var users = [];
for (var i = 3; i <= rr; i++) {
var firstName = spreadSheet.getRange(i, 1).getValue();
var user = {
firstName: firstName
};
users.push(user);
}
doGet(users);
}

You want to open new tab for own browser using the created HTML data, when you run the function at the script editor.
You are using the container-bound script.
If my understanding is correct, how about this modification?
Modification points:
In this modification, I used the following flow. Please think of this as just one of several answers.
By running runScript(), a dialog is opened.
The opened dialog runs a Javascript for opening new tab of the browser and open the URL of Web Apps.
At this time, generateLinks() is run from doGet(), and the values are retrieved and put to HTML data.
Close the dialog.
By this flow, when you run the function at the script editor, the created HTML is opened as new tab of your browser.
Modified script:
Please copy and paste the following script to the container-bound script of Spreadsheet. And then, please redeploy Web Apps as new version. At that time, as a test case, please set Execute the app as: and Who has access to the app: as Me and Anyone, even anonymous, respectively. In this case, you are not required to modify the script of HTML side.
function doGet() {
var html = HtmlService.createTemplateFromFile("htmlList");
html.users = generateLinks(); // Modified
return html.evaluate().setTitle("Test my app");
}
function generateLinks() {
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var rr = spreadSheet.getLastRow();
var users = [];
for (var i = 3; i <= rr; i++) {
var firstName = spreadSheet.getRange(i, 1).getValue();
var user = {
firstName: firstName
};
users.push(user);
}
return users; // Modified
}
// I added the following function. Please run this function.
function runScript() {
var url = ScriptApp.getService().getUrl();
var script = "<script>window.open('" + url + "', '_blank').focus();google.script.host.close();</script>";
var html = HtmlService.createHtmlOutput(script);
SpreadsheetApp.getUi().showModalDialog(html, 'sample');
}
When var spreadSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); is used, the 1st sheet of Spreadsheet is used. So when you want to retrieve the values from the specific sheet, for example, please modify to SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetName").
Note:
If you modified the script of Web Apps, please redeploy Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
References:
HTML Service: Create and Serve HTML
HTML Service: Templated HTML
Taking advantage of Web Apps with Google Apps Script

Assuming that your data on the spreadsheet looks something like this -
And the desired output looks something like this (you're free to modify the CSS in your .html file) -
You can achieve this by using the following code -
For Code.gs:
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
function getUsers() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var users = ss.getRange(1, 1, ss.getLastRow(), 1).getValues();
return users;
}
For Index.html file:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
function onSuccess1(users) {
var div = document.getElementById('userFirstNames');
div.innerHTML = users;
}
google.script.run.withSuccessHandler(onSuccess1).getUsers();
</script>
</head>
<body>
<div id='userFirstNames'></div>
</body>
</html>
Hope this helps.

Related

Populate Google Form using Google App Script on Form open

I have a google form to where I want to populate with some data from a spreadsheet when user is opening the form.
I have a ListItem which I populate it with spreadsheet data. Until now I used the Open(e) function and a trigger, but I just found out that this method is triggered only on form edit not on form open.
Do you have an idea how can I do that?
To have an idea on what I want, I have two files, Code.gs contains the main functions like onOpen and onFormSubmit, and ItemClass where I get my data and create the UI.
I set a console log to Open(e) function, but never triggers.
Code.gs
function onOpen(e) {
console.log({message: 'onOpen', initialData: e});
let items = getItems();
let form = FormApp.openById(PARAMS.formID);
form.setTitle('New Form')
createUI(form, items);
}
ItemsClass.gs
function getItems() {
var email = Session.getActiveUser().getEmail();
var allItems = SpreadsheetApp.openByUrl(PARAMS.sheetURL).getSheetByName("Items Stream").getDataRange().getDisplayValues();
var headers = allItems.shift();
var items = new Array;
for (var i = 0; i < allItems.length; i++) {
var first = allItems[i][1]
var second = allItems[i][2]
items.push(first + "&" + second)
}
return items;
}
The on Open trigger Google Forms works only when opening the form editor, not the actual form that the user fills out
To return to the user the updated data whenever he opens the form and allow him to modify the data, you should create a custom HTML form with Web polling.
Web Polling with setInterval allows to pull fresh data from the spreadsheet and update it in specified intervals
Apps Script WebApps allow you to combine Apps Script and HTML/Javascript which allows you easy interaction between serverside and UI - useful for creation of a custom HTML form
Use google.script.run to communicate between the two sides.
Simple sample pulling updated data from column A in a spreadsheet and allowing the user to modify the values:
code.gs:
var sheet = SpreadsheetApp.openById('XXX').getSheetByName("YYY");
function doGet(){
var html=HtmlService.createTemplateFromFile('index');
return html.evaluate();
}
function getValues() {
//get data from the first column
var data = sheet.getRange(1, 1, sheet.getLastRow(), 1).getValues();
var table = "";
for (var i = 0; i < data.length; i++) {
table +='<tr><td>' + data[i][0] + ' </td><tr>';
}
return table;
}
function writeToSheet(newValues) {
newValues = newValues.split(",");
var range = sheet.getRange(1, 1, newValues.length, 1);
newValues = newValues.map(function(row){return [row]});
range.setValues(newValues);
}
index.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<script>
function onSuccess(values){
document.getElementById("data").innerHTML=values;
}
function polling(){
//modify the interval of 2000 ms to any desired value
setInterval( function(){google.script.run.withSuccessHandler(onSuccess).getValues()},2000);
}
function updateValues(){
var newValues= document.getElementById("newValues").value;
google.script.run.writeToSheet(newValues);
}
</script>
<body onload="polling()">
<div> Values: </div>
<table id="data">
</table>
<div> If you want to modify the values in the spreadsheet, type in new values comma separated: </div>
<input type="text" id="newValues" ><br><br>
<input type="button" value="Confirm" onclick="updateValues()">
</body>
</html>
Deploy this WebApp and described in the documentation and paste the WebApp URL into a browser address bar.

Converting local GmailApp.sendEmail (GAS) to Web App script

I have a working Google apps script set up to send an automated email (GmailApp.sendEmail) when I click a button in the workbook.
However, I am trying to convert the script to Google Web App so that any users within my organization will have authorization to run the script and trigger the automated email by pressing the button.
I'm a little lost on how to adapt the local code from getui(). I know that I need to add a function such as doget(e) and deploy as a web app, but I'm not well-versed enough in Web App to edit the code.
Here is my working local code:
// This constant is written in column E for rows for which an email
// has been sent successfully.
var EMAIL_SENT = 'E-MAIL SENT';
var ui = SpreadsheetApp.getUi();
/**
* Sends non-duplicate emails with data from the current spreadsheet.
*/
function email(){
var rng = SpreadsheetApp.getActiveSheet().getRange('A2:F2')
var checkvalue = SpreadsheetApp.getActiveSheet().getRange('E2').getValue();
var email = rng.getValues()[0];
var data = rng.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailSent = checkvalue; // emailSent confirmation cell
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
GmailApp.sendEmail(email[0], email[1], email[2]);
SpreadsheetApp.getActiveSheet().getRange('E2').setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
Any help is much appreciated!
Try this:
Code.gs:
function email(){
var ss=SpreadsheetApp.openById("SpreadsheetId");//need spreadsheet id
var sh=ss.getSheetByName("SheetName");//when you open up a spreadsheet like this the active sheet is alway ss.getSheets()[0] the left most sheet so you should user get sheet by name instead.
var rg=sh.getRange('A2:F2');
var email=rg.getValues()[0];
if (email[4]!="EMAIL_SENT") {
GmailApp.sendEmail(email[0], email[1], email[2]);
sh.getRange('E2').setValue("EMAIL_SENT");
}
}
function doGet() {
return HtmlService.createHtmlOutputFromFile('html filename without extension');
}
html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input type="button" value="Send" onClick="google.script.run.email();" />
</body>
</html>
Client to Server Communication

Open Google sheet with script

I would like to open each file in a folder via script. Below is what I have tried. This is my first shot at any script in sheets.
This code runs but does not open file, I could be a mile off on this but I can't figure out how to make it open the files. Thanks for your help
function myFunction() {
var dApp = DriveApp;
var folder = dApp.getFoldersByName("test").next();
var filesIter = folder.getFiles();
while(filesIter.hasNext()) {
var files = filesIter.next();
var ss = SpreadsheetApp.open(files);
}
}
I simply want it to open all the files in a folder in this case "test". there are currently two google sheets in the folder.
Google Apps Script server-side code can't do actions on the client side like opening a spreadsheet on the user web browser but you could use HTML Service to use client side code to achieve your goal.
Related
Open a URL in a new tab (and not a new window) using JavaScript
Open URL in same window and in same tab
You can read open files from Scripts but not in the way that users can open files. You can open them on the server and read and/or write data but the file doesn't open up in edit mode like it does when you open it as a user.
Here's a script that opens up spreadsheets and reads their name and id and returns it to a webapp.
HTML:
<html>
<head><title></title>
</head>
<body>
<div id="data"></div>
<script>
window.onload=function(){
google.script.run
.withSuccessHandler(function(ssA){
var html='';
for(var i=0;i<ssA.length;i++) {
html+='<br />' + 'Name: ' + ssA[i].name + ' Id: ' + ssA[i].id;
}
document.getElementById('data').innerHTML=html;
})
.readSpreadsheetsInAFolder();
}
</script>
</body>
</html>
CODE.gs:
function readSpreadsheetsInAFolder() {
var ssA=[];
var folders=DriveApp.getFoldersByName("LinkedSpreadsheets");
while(folders.hasNext()){
var folder=folders.next();
var files=folder.getFilesByType(MimeType.GOOGLE_SHEETS);
while(files.hasNext()) {
var file=files.next();
var ss=SpreadsheetApp.openById(file.getId());
ssA.push({name:file.getName(),id:file.getId()});
}
}
return ssA;
}
This function was written for my account so you may have to modify the Folder Name to get it to work on your account.

Using Google AppScript, Is it possible to programmatically publish to the web a _single sheet_ within a Google Sheets document?

I would like, through Google Appscript, to (a) programmatically publish to the web selected sheets within a Google Sheets document, (b) obtain programmatically the URL where each sheet is published, and (c) have the published version of each sheet automatically update whenever the corresponding sheet is updated (this should happen automatically, right?). Right now, I can accomplish this only through File/Publish to Web...
The following question and answer is highly related to this question:
Google Sheets API: How to "publish to web" for embeddable sheet?
However, it appears to apply only to publishing an entire Google Sheets document, not a single sheet within a Google Sheets document. Any solution ideas would be most appreciated.
I have gained some insight into this question. It is possible to obtain a URL to a published HTML version of a single sheet in a Google Sheets document simply by modifying the URL used to access that sheet.
For example, here is the URL of a sheet I'm working on in Google Sheets, copied directly from my browser's URL bar:
https://docs.google.com/spreadsheets/d/1fTx3dUsvdbVKgP2nXs1LcyG_7oBp-MoFZTXn7MtdEZg/edit#gid=1711661074
I can then modify the URL as follows to get a published HTML version of that single sheet:
https://docs.google.com/spreadsheets/u/0/d/1fTx3dUsvdbVKgP2nXs1LcyG_7oBp-MoFZTXn7MtdEZg/htmlembed/sheet?gid=1711661074
Summary of URL modifications I made:
Replace "/d" after "spreadsheets" with "/u/0/d"
Replace "edit#" with "htmlembed/sheet?"
Other inferences one can make:
The long string after "/u/0/d" is the ID of the Google Sheets document.
The shorter string after "sheet?" is the ID of the single sheet within that document.
These new insights transform my question into a new one: namely, how can I programmatically obtain (through Google Appscript) the ID of the Google Sheets document I'm working on, together with the ID of the spreadsheet I'm working on?
Here's the answer:
To get the ID of the current Google Sheets document within Appscript:
var ss = SpreadsheetApp.getActiveSpreadsheet().getId();
To get the ID of the current sheet within the current Google Sheets document:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getSheetId();
I can now build a URL for a published html version of any single sheet within a Google Sheets document through string concatenation as follows:
var publishedURL = "https://docs.google.com/spreadsheets/u/0/d/" + ss + "/htmlembed/sheet?gid=" + sheet;
There's still one lingering issue, though: It appears that users of this published URL must manually refresh the browser in order to sync the HTML with the spreadsheet. At the present time, I do not have a solution to this problem, other than to request that users of the URL install an auto URL refresher or manually refresh the page periodically. I'd welcome any ideas on this.
It looks like you can publish individual sheets according to these dialogs:
It does update the published sheets although I've noticed quite a bit of delay in the process occasionally.
Since the Publish to the Web simply shows a readonly version of an html table that contains sheet values then you could do that with one webapp. Here's an example below that displays all sheets in tabular form.
A Webapp to display all sheets:
function publishAllSheets()
{
var ss=SpreadsheetApp.getActive();
var allShts=ss.getSheets();
var s='All my Sheets';
for(var i=0;i<allShts.length;i++)
{
var sh=allShts[i];
var rg=sh.getDataRange();
var vA=rg.getValues();
s+=Utilities.formatString('Sheet: %s <br /><table border="1">',allShts[i].getName());
for(var j=1;j<vA.length;j++)
{
s+='<tr>';
for(var k=0;k<vA[j].length;k++)
{
s+=Utilities.formatString('<td>%s</td>', vA[j][k]);
}
s+='</tr>';
}
s+='</table>';
}
return s;
}
function showAllMySheets()
{
var ui=HtmlService.createHtmlOutputFromFile('allsheets').setWidth(1000);
SpreadsheetApp.getUi().showModelessDialog(ui, 'All My Sheets')
}
function doGet()
{
var ui=HtmlService.createHtmlOutputFromFile('allsheets');
return ui.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
allsheets.html
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function(){
google.script.run
.withSuccessHandler(updateDiv)
.publishAllSheets();
});
function updateDiv(hl)
{
document.getElementById('c1').innerHTML=hl;
}
</script>
</head>
<body>
<div id="c1"></div>
</body>
</html>
Here's the code for getting any one of your sheets:
function getSheetNames()
{
var ss=SpreadsheetApp.getActive();
var allShts=ss.getSheets();
var shts=[];
for(var i=0;i<allShts.length;i++)
{
shts.push(allShts[i].getName());
}
return shts;
}
function getOneSheet(name)
{
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(name);
var rg=sh.getDataRange();
var vA=rg.getValues();
var s='';
s+=Utilities.formatString('Sheet: %s <br /><table border="1">',sh.getName());
for(var j=1;j<vA.length;j++)
{
s+='<tr>';
for(var k=0;k<vA[j].length;k++)
{
s+=Utilities.formatString('<td>%s</td>', vA[j][k]);
}
s+='</tr>';
}
s+='</table>';
return s;
}
onesheet.html
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function(){
google.script.run
.withSuccessHandler(updateSelect)
.getSheetNames();
});
function updateDiv(hl)
{
document.getElementById('c1').innerHTML=hl;
}
function updateSelect(vA)
{
var select = document.getElementById("sel1");
select.options.length = 0;
for(var i=0;i<vA.length;i++)
{
select.options[i] = new Option(vA[i],vA[i]);
}
}
function getSelectedSheet()
{
var name=$('#sel1').val();
google.script.run
.withSuccessHandler(updateDiv)
.getOneSheet(name);
}
console.log('MyCode');
</script>
</head>
<body>
<select id="sel1">
<option value="" selected></option>
</select>
<input type="button" value="Select" onClick="getSelectedSheet();" />
<div id="c1"></div>
</body>
</html>

Initiate a download from google apps script

I added a new menu item to my spreadsheet using google apps script. This menu item creates a file, but I'd like for it to initiate the download of the file after creating it.
Is this possible?
Remember, this is not a web app, but a menu item in my spreadsheet.
Thanks
Edit:
Thanks to Serge insas' suggestion, the following simple script works perfectly, and opens a download window with the link I need:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var csvMenuEntries = [ {name: "Zip all CSVs", functionName: "saveAsCSV"} ];
ss.addMenu("CSV", csvMenuEntries);
};
function saveAsCSV() {
var folder = createCSVs(); // creates a folder with CSV for each Sheet
var zipFile = zipCSVs(folder, "DI.zip"); // creates a zip of all CSVs in folder
var ui = UiApp.createApplication().setTitle("Download");
var p = ui.createVerticalPanel();
ui.add(p);
p.add(ui.createAnchor("Download", zipFile.getDownloadUrl()));
SpreadsheetApp.getActive().show(ui)
}
EDIT : read the comments below, Zig Mandel is perfectly right when he points out the limitations of the "complicated" version, it was really a simple (and fun) exercice to show other methods.
I think you'll have to use an intermediate Ui as a popup to confirm the download.
After that there are 2 possible ways that I know, one is very simple and the other is quite cumbersome, make your choice, the code below shows both of them.
note : to use the complicated one you need to deploy your app (ie save a version and deploy as webapp), for the simple one just use it "as it is". (I show the simple in the code comments).
The code :
function onOpen() {
var menuEntries = [ {name: "test download", functionName: "downloadFile"}
];
var sheet = SpreadsheetApp.getActiveSpreadsheet();
sheet.addMenu("Utils",menuEntries);
}
function downloadFile(){
var file = DriveApp.createFile('test file', 'Some content in this file to test it');
var fileID = file.getId();
var fileName = file.getName();
var ui = UiApp.createApplication().setTitle('Download');
var url = ScriptApp.getService().getUrl()+'?&ID='+fileID+'&name='+fileName;
var p = ui.createVerticalPanel();
ui.add(p);
p.add(ui.createAnchor('click to download', url));
p.add(ui.createAnchor('or use this link ',file.getDownloadUrl()));// this is the simple one, just get the file you created and use getDownloadUrl()
SpreadsheetApp.getActive().show(ui)
}
function doGet(e){
var fileId = e.parameter.ID;
var fileName = e.parameter.name;
var fileString = DocsList.getFileById(fileId).getContentAsString();
return ContentService.createTextOutput(fileString).downloadAsFile(fileName);
}
PS : I had some fun writing this, the "complicated version" is really funny imho :-)
OP's answer is deprecated (in 2021), so I made a more general purpose one based on it.
Code.gs:
// Runs when the spreadsheet starts, adds a tab at the top
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Script Menu')
.addItem('Download a file!', 'dlFile')
.addToUi();
}
// Run when you click "Download a file!"
function dlFile() {
let file = DriveApp.getRootFolder().createFile('Hi.txt', 'Hello, world!');
// Create little HTML popup with the URL of the download
let htmlTemplate = HtmlService.createTemplateFromFile('Download.html');
htmlTemplate.dataFromServerTemplate = { url: file.getDownloadUrl() };
let html = htmlTemplate
.evaluate()
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Download');
};
Download.html:
<!DOCTYPE html>
<html>
<head>
<script>
let data = <?!= JSON.stringify(dataFromServerTemplate) ?>; // Stores the data directly in the javascript code
function downloadFile() {
document.getElementById("dlBtn").innerText = "Downloading..";
window.open(data.url, '_blank');
document.getElementById("dlBtn").disabled = true;
}
</script>
</head>
<body>
<button id="dlBtn" onclick="downloadFile()">Download</button>
</body>
</html>
Just adding to #dr-bracket's answer where I made some small additions to the scripts in an attempt to stop the browser from navigating away to a new tab.
I got the idea from:
Download a created Google Doc from a deployed web app (Google Apps Script)
Where #tanaike uses the google.script.run.withSuccessHandler class and method to create a popup prompt then closes and returns to your app on download. (May not popup if your browser settings are set to not pick download location.)
Code.gs:
// Runs when the spreadsheet starts, adds a tab at the top
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Script Menu')
.addItem('Download a file!', 'dlFile')
.addToUi();
}
// Run when you click "Download a file!"
function dlFile() {
let file = DriveApp.getRootFolder().createFile('Hi.txt', 'Hello, world!');
// Create little HTML popup with the URL of the download. Added filename to object. ~~~~~~~~~~~
let htmlTemplate = HtmlService.createTemplateFromFile('Download.html');
htmlTemplate.dataFromServerTemplate = { url: file.getDownloadUrl(), name: file.getName() };
let html = htmlTemplate
.evaluate()
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Download');
};
// Added the following to satisfy the withSuccessHandler method: ~~~~~~~~~~~~~
function createDownloadUrl(data) {
return {
url: data.url,
name: data.name,
};
}
Download.html:
<!DOCTYPE html>
<html>
<head>
<script>
let data = <?!= JSON.stringify(dataFromServerTemplate) ?>; // Stores the data directly in
// the javascript code
function downloadFile() {
const dlBtn = document.getElementById("dlBtn");
dlBtn.innerText = "Downloading..";
// window.open(data.url);
// Replaced with:
// the url and name variables will be returned here from the
// code.gs function createDownloadEvent() after it runs successfully.
google.script.run
.withSuccessHandler(({ url, name }) => {
const a = document.createElement("a");
document.body.appendChild(a);
a.download = name;
a.href = url;
a.target = "_blank";
a.click();
})
.createDownloadEvent(data);
dlBtn.disabled = true;
}
</script>
</head>
<body>
<button id="dlBtn" onclick="downloadFile()">Download</button>
</body>
</html>
Resources:
https://developers.google.com/apps-script/guides/html/reference/run#withsuccesshandlerfunction
Download a created Google Doc from a deployed web app (Google Apps Script)