HTML Service: Templated HTML as sidebar - google-apps-script

I'm working on a sidebar to display items from a one-column sheet titled "bib", and so far it has not been successful, even though I only modified the code where it's necessary, as I see it.
The code I followed is from: https://developers.google.com/apps-script/guides/html/templates
// from code.gs
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Custom Menu')
.addItem('Show sidebar', 'showSidebar')
.addToUi();
}
function showSidebar() {
var html = HtmlService.createTemplateFromFile('Page') // updated code from createTemplateFromFile('Page')
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.showSidebar(html); // this line generates error with createTemplateFromFile
html.data = SpreadsheetApp
.getActive() // modified to getActive() because the sidebar is intended to be displayed while working on the active spreadsheet
.getSheetByName('bib') // the sheet name where items are stored
.getDataRange()
.getValues();
return html.evaluate();
}
// from Page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<table>
<? for (var i = 0; i < data.length; i++) { ?>
<tr>
<? for (var j = 0; j < data[i].length; j++) { ?>
<td><?= data[i][j] ?></td>
<? } ?>
</tr>
<? } ?>
</table>
</body>
</html>
I expect a one-column table dynamically output the one-column sheet "bib", but instead I get
"Object does not allow properties to be added or changed."
Update: The suggested answer from some other question, createTemplateFromFile(filename), doesn't have sidebar as an argument. It leads to error:
"Invalid argument: userInterface"

Related

Google Visualization.Sankey -- GoogleScript Dropping Node Label

So I am able to make a Sankey plot from my Google Sheet data using Google Chart. The issue is that the plot seems to drop off the label for one of the nodes.
My data looks as follow:
Source Type Count
External A 12
External B 7
External C 1
External D 0
Internal A 26
Internal B 23
Internal C 15
Internal D 0
Other A 0
Other B 1
Other C 24
Other D 0
The plot looks like
As you can see the last Node name "C" (in green is dropped)
My GoogleScript code was as follow:
code.gs
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('CustomScripts')
.addItem('SankeyDiagram', 'openDialog')
.addToUi();
}
function getSpreadsheetData() {
// ID of your Document, take from URL
var ssID = "sheetID",
sheet = SpreadsheetApp.openById(ssID).getSheets()[0],
data = sheet.getDataRange().getValues();
return data;
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('index')
.setHeight(300)
.setWidth(1000);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Sankey Diagram');
}
And the corresponding HTML was:
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://www.google.com/jsapi"></script>
</head>
<body>
<div id="main"></div>
<script type="text/javascript">
google.load('visualization', '1', {
packages: ['corechart', 'sankey']
}); google.setOnLoadCallback(initialize);
function initialize() {
google.script.run.withSuccessHandler(drawChart).getSpreadsheetData();
}
function drawChart(rows) {
console.log(rows);
var data = google.visualization.arrayToDataTable(rows);
var chart = new google.visualization.Sankey(document.getElementById('sankey_basic'));
chart.draw(data, {'title':'Detection Method by Severity',width: 900,height: 250, sankey: {}});
google.script.run.withSuccessHandler().newChart(chart);
}
</script>
</body>
</head>
<body>
<div id="sankey_basic" style="width: 900px; height: 300px;"></div>
</body>
</html>
What am I overlooking that is causing the "C" category not to appear?
Separate but not critical question -- is there a way to embed the image as an image/chart on the active sheet
Also for the code reference... special thanks to
njoerd114 for creating the framework for drawing Sankey plots... https://gist.github.com/njoerd114/839b9a5298843ea4cf9fd241e39ebbf6
Suggestion:
The reason why you are getting the cut image of the sankey graph is because of the zero values you have in "D".
I have found a similar concern like this in git hub: Node positions do not work with link value zero in Sankey
I suggest that you remove zeros first by filtering the array:
Modified code:
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('CustomScripts')
.addItem('SankeyDiagram', 'openDialog')
.addToUi();
}
function getSpreadsheetData() {
// ID of your Document, take from URL
var ssID = "1KtvAFFQV13zSwktXKtUXZLQF9hmb9VaxbC4OM3hGEVw",
// which Sheet? [0] is the first and so on...
sheet = SpreadsheetApp.openById(ssID).getSheets()[0],
data = sheet.getDataRange().getValues(),
///Filtered the data by removing zeros
header = sheet.getRange(1,1,1,3).getValues()[0],
filteredData = data.filter(r => r[2] > 0);
filteredData.unshift(header)
return filteredData
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('index')
.setHeight(300)
.setWidth(1000);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Sankey Diagram');
}
Result:

How to connect HTML & Google Sheet and then display row values based on user input in HTML

I am trying to connect HTML & Google Sheet. I have some sample codes below but I don't know how to connect them together. Could anyone point out to me how can I change below code to make it work for me?
Below codes are trying to display a HTML page for users to input ID Number and then get the latest row of related data from Google Sheet. Thank you.
index.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div class="form-row">
<div class="form-group col-md-2">
<label for="idNum">ID Number</label>
<input type="text" class="form-control" id="idNum">
<output id="rnum"></output>
</div>
<button type="button" class="btn btn-outline-info" id="searchbtn">Search Profile</button>
</div>
<script>
document.getElementById("searchbtn").addEventListener("click", searchProfile);
function searchProfile(){
var appProfile = document.getElementById("idNum").value;
if(appProfile.length === 6){
google.script.run.withSuccessHandler(updateProfile).updateIdNum(appProfile);
}
//else{
//alert("invalid Profile Number");
}
function updateProfile(returnData){
document.getElementById("rnum").value = returnData[1];
}
</script>
</body>
</html>
Apps Script inside Google Sheet:
function updateIdNum(appProfile){
// var appProfile = "101018"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ws = ss.getSheetByName("Results");
var data = ws.getRange(1, 1, ws.getLastRow(), 1).getValues();
var dJoin = data.join().split(",");
var myPnum = dJoin.indexOf(appProfile);
var dataRow = ws.getRange(myPnum + 1, 1, 1, ws.getLastColumn()).getValues();
Logger.log(dataRow[0]);
if (myPnum > -1){
return dataRow[0];
} else {
Logger.log("unknown")
//return "unavailable";
}
}
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Custom Menu')
.addItem('Show sidebar', 'showSidebar')
.addToUi();
}
function showSidebar() {
var html = HtmlService.createHtmlOutputFromFile('index')
.setTitle('My custom sidebar')
.setWidth(300);
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.showSidebar(html);
}
Google Sheet data look like:
ID Number | First Name | Last Name|
101018 | John | Doe |
101011 | Jane | Doe |
Displaying Data from table in html dialog
GS:
function getMyData() {
return [["ID Number","First Name","Last Name"],[101018,"John","Doe"],[101011,"Jane","Doe"]];
}
function launchThisDialog() {
let t = HtmlService.createTemplateFromFile("ah1").evaluate();
SpreadsheetApp.getUi().showModelessDialog(t,"Display Table");
}
html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div id="tabledata">
<? var vs = getMyData(); ?>
<table>
<? vs.forEach((r,i)=>{ ?>
<tr>
<? r.forEach((c,j)=>{ ?>
<? if(i == 0) { ?>
<th style="padding:2px 5px;font-weight:bold;border:1px solid black;"><?= c ?> </th>
<? } else { ?>
<td style="padding:2px 5px;border:1px solid black;"><?= vs[i][j] ?> </td>
<? } ?>
<? }); ?>
</tr>
<? }); ?>
</table>
</div>
</body>
</html>

How to create a sidebar list of all sheets in a Google Spreadsheet workbook?

Because I have a Google spreadsheet workbook with so many sheets that they are not all visible in the pulldown on the bottom left side of the spreadsheet workbook, I am attempting to modify the code at "How to get the list of all sheet in a dropdown list in a sidebar" from a standalone script to a bound script in order to be able to see a longer list. Also, I do not want my list in a dropdown. I want it simply as a bulleted list.
I am getting a malformed HTML error that I assume is being generated from within the scriplet. (See code below.) Any ideas what is going wrong with this code.
HTML
<html>
<head>
<base target="_top">
</head>
<body>
<select name="Sheets List" onchange="listSheets()")>
<? var sheets=spreadsheet.getSheets(); ?>
<? for(var i=0;i<sheets.length;i++) { ?>
<option value=<?=sheets[i].getName()?>> <?= sheets[i].getName()?></option>
<? } ?>
</select>
<script>
function listSheets(){
var name=document.getElementsByName("Sheets List")[0].value;
google.script.run.goToSheet(name);
};
</script>
</body>
</html>
GS
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom')
.addItem('Sheets List', 'sheetsList')
.addToUi();
function sheetsList(){
var html = HtmlService.createHtmlOutputFromFile('sheets').evaluate
.setTitle('Sheets in this Workbook')
.setWidth(300);
SpreadsheetApp.getUi()
.showSidebar(html);
}
}
Error Message
Message details
Malformed HTML content: > function listSheets(){ var name=document.getElementsByName("Sheets List")[0].value; google.script.run.goToSheet(name); }; .
You have a few issues that are preventing the sidebar from showing:
You should be using HtmlService.createTemplateFromFile(), not createHtmlOutputFromFile().
To generate the HTML, you need to be sure to include the parentheses when calling .evaluate(). (Related to the first point, note that evaluate() is only available for HtmlTemplate objects.)
sheetsList() should not be nested within your onOpen() function.
You need to define spreadsheet in your HTML.
GS
function onOpen() {
SpreadsheetApp.getUi().createMenu('Custom')
.addItem('Sheets List', 'sheetsList')
.addToUi();
}
function sheetsList(){
var html = HtmlService.createTemplateFromFile('sheets').evaluate()
.setTitle('Sheets in this Workbook')
.setWidth(300);
SpreadsheetApp.getUi().showSidebar(html);
}
HTML
In the HTML below, I removed the frontend JS code for listSheets() because it wasn't part of the question's scope and was calling a backend function that was also not included in the question.
<html>
<head>
<base target="_top">
</head>
<body>
<select name="Sheets List">
<? var spreadsheet = SpreadsheetApp.getActive(); ?>
<? var sheets=spreadsheet.getSheets(); ?>
<? for(var i=0;i<sheets.length;i++) { ?>
<option value=<?=sheets[i].getName()?>> <?= sheets[i].getName()?></option>
<? } ?>
</select>
</body>
</html>
Sheet Link List in Sidebar
function allsheetslist() {
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
var html='<table>';
shts.forEach(function(sh,i){
html+=Utilities.formatString('<tr><td><input type="button" value="%s" onClick="gotoSheet(%s);" /></td></tr>',sh.getName(),sh.getIndex());
});
html+='</table>';
html+='<script>function gotoSheet(index){google.script.run.gotoSheetIndex(index);}</script>';
var userInterface=HtmlService.createHtmlOutput(html)
SpreadsheetApp.getUi().showSidebar(userInterface);
}
function gotoSheetIndex(index) {
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
shts[index-1].activate();
}
You can get a lot more buttons on the side bar this way:
function allsheetslist() {
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
var html='<style>input[type="button"]{margin:2px 5px 2px 0;border:none;background:white;}</style>';
shts.forEach(function(sh,i){
html+=Utilities.formatString('<input type="button" value="%s" onClick="gotoSheet(%s);" />',sh.getName(),sh.getIndex());
});
html+='<script>function gotoSheet(index){google.script.run.gotoSheetIndex(index);}</script>';
var userInterface=HtmlService.createHtmlOutput(html).setTitle("Sheet Link List")
SpreadsheetApp.getUi().showSidebar(userInterface);
}
Tried the #Cooper way. Its easier and more friendly for me. But the problem still pending is that the "OnClick" function is not called and nothing happens when the sheet name button is clicked on the side bar. I have even added a specific field to be displayed along with sheet name in the sidebar. That works fantastically. Now the only help needed is to activate the click. I have tried many ways still no success. The code used is as below:
function allsheetsList() {
var ss = SpreadsheetApp.getActive();
var shts = ss.getSheets();
var html = '<table>';
shts.forEach(function(sh, i) {
html += Utilities.formatString('<tr><td><input type="button" value="%s" onClick="google.script.run.gotoSheet(\'%s\');" /></td>', sh.getName(), sh.getName());
html += Utilities.formatString('<td><input type="button" value="%s" /></td></tr>', sh.getRange("C2").getValue());
});
html += '</table>';
var userInterface = HtmlService.createHtmlOutput(html).setTitle("Sheet Link List");
SpreadsheetApp.getUi().showSidebar(userInterface);
}
function gotoSheet(name) {
var ss = SpreadsheetApp.getActive();
ss.getSheetByName(name).activate();
}

fill sidebar form from sheet

In google sheet, I want to fill a form in a sidebar with values from the active sheet. Since I can't use a click on cell trigger, i want to use a function that takes the value of the active cell when a button in the sidebar is clicked.
I wrote this code but it didn't work unfortunately,
I am new to Apps script so excuse my low level code :)
Thank you !
test_sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
function tSubmit(form){
google.script.run.withSuccessHandler(updateT).getT(form);
}
function updateT(cellValue){
var div=document.getEelementById('T');
div.innerHTML='<input name="T" size = 10 value=' + cellValue + '>K'
}
</script>
</head>
<body>
<form>
<font color="red">Select conditions of interest:</font><p>
<ul><input value=T type="Button" onClick="tSubmit(this)"><div id="T"> = <input name="T" size = 10>K</div></ul>
<ul><i>P</i> = <input name="P" size = 10>bar</ul>
</form>
</body>
</html>
code.gs :
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Custom Menu')
.addItem('Show sidebar', 'showSidebar')
.addToUi();
}
function showSidebar() {
var html = HtmlService.createHtmlOutputFromFile('test_sidebar')
.setTitle('My custom sidebar')
.setWidth(300);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showSidebar(html);
}
function getT(form){
var cellValue= SpreadsheetApp.getActiveSheet().getActiveCell().getValue();
return cellValue;
}
You have a typo in the html file. Remove the extra e in getElementByID to make it:
var div=document.getElementById('T');
Also, there is no need to try to pass the form, which is also causing a problem anyway, so just remove those portions. You final codes will be:
test_sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
function tSubmit(){
google.script.run.withSuccessHandler(updateT).getT();
}
function updateT(cellValue){
var div=document.getElementById('T');
div.innerHTML='<input name="T" size = 10 value=' + cellValue + '>K'
}
</script>
</head>
<body>
<form>
<font color="red">Select conditions of interest:</font><p>
<ul><input value=T type="Button" onClick="tSubmit()"><div id="T"> = <input name="T" size = 10>K</div></ul>
<ul><i>P</i> = <input name="P" size = 10>bar</ul>
</form>
</body>
</html>
code.gs :
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Custom Menu')
.addItem('Show sidebar', 'showSidebar')
.addToUi();
}
function showSidebar() {
var html = HtmlService.createHtmlOutputFromFile('test_sidebar')
.setTitle('My custom sidebar')
.setWidth(300);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showSidebar(html);
}
function getT(){
var cellValue= SpreadsheetApp.getActiveSheet().getActiveCell().getValue();
return cellValue;
}
Btw you can trigger a function with the use of an Image, and linking that image to a function.. A "trick" to trigger something from the cell

In Google Script how can I get feedback from the user during runtime?

I'm attempting to get user feedback during runtime of a Google Script. I want the user to be able to select one or more students from a list of names and also select one or more standards from a list of standards.
I attempted to do this by creating an html popup screen with boxes. The popup works fine, but I can't gain access to the results of the popup.
Sample
Here are my current script files:
code.gs
// Use this code for Google Docs, Forms, or new Sheets.
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Dialog')
.addItem('Open', 'doGet')
.addToUi();
}
function doGet() {
html = HtmlService
.createTemplateFromFile('Index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Dialog title');
}
function getStandards(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var dataRange = sheet.getRange(2,1,15,1);
var data = dataRange.getValues();
return data;
}
function getStudents(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var dataRange = sheet.getRange(2,2,15,1);
var data = dataRange.getValues();
return data;
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Standards? Students?
<br>
<select size="15" name="standards" multiple>
<? var data = getStandards();
for (var i = 0; i < data.length; i++) { ?>
<option><?= data[i] ?>
<? } ?>
</select>
<select size="15" name="students" multiple>
<? var data = getStudents();
for (var i = 0; i < data.length; i++) { ?>
<option><?= data[i] ?>
<? } ?>
</select>
<input type="button" value="Close"
onclick="google.script.host.close()" />
</body>
</html>
This is the form that is displayed, but I need to get the list of Standards and the list of Students when I hit the close button so that I can Create a bunch of new rows in my spreadsheet.
The target result would be to have 9 new rows added. One for each student/standard pair. Then I can add scores for each one.
Thanks!
I don't mean to sound weird but why not just use Google form? it automatically puts all the entries into the Google Sheet.
At some point before the .close() happens you'll want to add in some thing like:
<input type="button" value="Close"
onclick="
google.script.run.storeInput
(
[
document.getElementById("#standards").value,
document.getElementById("#students").value
]
)
google.script.host.close()" />
After adding an ID attribute to both selects and creating a function storeInput(arr) in .gs