Passing parameters from Dialog to server side script - google-apps-script

I have a Google sheet template. Each time this is opened, I would like to open a Dialog to get two parameters and pass those parameters to a script bound to the Google sheet template. So far I have managed to define and open the Dialog. But the parameters don't show up in the server side script.
It seems like readFormData is not called when I push the "Create" button. If I replace readFormData with google.script.host.close(), the dialog box will close down. But not with readFormData. The same problem goes with close(). So my take on this is the Java script does not execute.
EDIT: I have solved the problem with a workaround. I replaced onclick="readFormData" with onclick="google.script.run.withSuccessHandler(google.script.host.close).getFormData(this.parentNode)" and then everything work as expected. (required a few changes on GS side as well) However, I can't figure out why I can't call my own javascript procedure readFormData(). With help form Chrome Developer Tool I can notice readFormData is not defined, raising an exception "Uncaught ReferenceError: readFormData is not defined". It fires every time i click on the button. So I guess it must be a syntax error that fools the parser or similar.
GS:
function getFormData(obj) {
Logger.log(obj);
return "hello";
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('index');
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.showModalDialog(html, 'Create file');
}
function onOpen(e) {
openDialog();
}
HTML:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet"
href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
<form id="getSomeData">
<div class="inline form-group">
<label for="fname">Destination</label>
<input type="text" name="fname" style="width: 150px;">
</div>
<div class="inline form-group">
<label for="date">Date</label>
<input type="text" name="date" style="width: 40px;">
</div>
<div>
<input type="button" class = "action" value= "Create" onclick="readFormData()" >
</div>
</form>
<!-- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> -->
<script>
function success(msg) { // I have fixed the ((msg) error
alert(msg);
}
function readFormData(){
console.log("readFormData");
var form = document.getElementById("getSomeData").elements;
var obj ={};
for(var i = 0 ; i < form.length ; i++){
var item = form.item(i);
obj[item.name] = item.value;
}
google.script.run.withSuccessHandler(close).getFormData(obj);
}
function close(){
google.script.host.close();
}
</script>
</body>
</html>

It seems to me there is a misprint on "success" function which could lead to an interpretation problem :
success( ( msg).
Did you try without this "(" ?

Related

Sending input from HTML using callback

I have the code for the input from a user, and I have the callback, I am just confused on how to have the data sent using the callback
my code:
HTML
<!DOCTYPE html>
<html>
<head>
<base target="\_top">
</head>
<body>
<label>User info: </label>
<div align="justify">
<input type= "text" id = "email">
</div>
<br>
<button id="searchData"> Search data sheet </button>
<script>
//Above creates a box for user input [//document.getElementById](//document.getElementById)("searchData").addEventListener("click",addHeaders); document.getElementById("searchData").addEventListener("click",google.script.run.withSuccessHandler(onSuccess).testCase());
//gets the button1 element and listens for a click then runs the function addName.
function onSuccess(numUnread) {
var div = document.getElementById('output');
div.innerHTML = numUnread
} ​
google.script.run.withSuccessHandler(onSuccess).testCase()
</script>
</body>
</html>
Apps Script
function doGet() {
return HtmlService.createHtmlOutputFromFile('frontEnd');
}
function testCase(input)
{
Logger.log((input + ' hello'))
return (input + ' hello')
}
My expected would be the input + " hello", instead I got "undefined hello"
Let's say that in your html you have a button like this with a div above it:
<body>
<div id="message"></div>
<input type="button" value="Hello" onClick="sayHello();" />
<script>
//When you click the button this function gets called
function sayHello() {
google.script.run
.withSuccessHandler(function(msg){
document.getElementById("message").innerHTML=msg;//message will appear in the div
})
.sayHelloToServer();//This function is on the server
console.log('My Code');
</script>
</body>
Then in Code.gs:
function sayHelloToServer() {
return "Hello. We're very happy that you came to visit us.";//this is returned to withSuccessHandler(function(msg){}) or you can also use a standalone function in which you simply put its name in like this .withSuccessHandler(funcname)
}
This is explained in Client to Server Communication

Refresh the deployed HTML web page using Google Apps Script

I have created a small website using HTML service of Google apps script.
Here is GAS Code
function doGet() {
var t = HtmlService.createTemplateFromFile('form');
t.email=Session.getActiveUser().getEmail();
return t.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
and this is HTML Code
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<base target="_top">
</head>
<body onLoad="addEventListeners()">
<div class="container-fluid">
<form id="form1">
<label for="comp_indiv_name" id="company_individual_name" style="display: block" >2. COMPANY NAME</label>
<input type="text" class="form-control" name="cName" required>
<br>
<input type="submit" class="btn btn-primary" value="Create Contract Now">
</form>
</div>
<script>
function addEventListeners() {
var condition=true;
document.getElementById('form1').addEventListener('submit', function(e){
e.preventDefault();
if(condition==true){condition=false;google.script.run.addData(this);}});
}
</script>
</body>
</html>
HTML page has one form with just one question, and when that form is submitted, the value get written in spreadsheet. Code for that is
function addData(form)
{
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheet=ss.getSheetByName('test');
sheet.getRange(5,5).setValue(form.cName)
htmlPage();
}
All i want is once the value gets written on the sheet, this HTML page gets refreshed automatically. Right now it just stays as it is. Link to the HTML page is https://script.google.com/macros/s/AKfycbzavm6TPFOkIXj_V0uD8XIqMN-9w6jAgp_QgRkJXawFJF59rPU/exec
If you truly want to refresh the page, and not just clear the input field, then I would add a hidden link that has your web app URL, and then programatically "click" it when the server code has completed.
HTML
</form>
<a id="linkToThisWebApp" href="https://script.google.com/macros/s/webAppID/exec" style="display:none">Hidden</a>
<!-- <button onclick="reloadPg()">Test</button> -->
</div>
Script tag - Code with success handler
<script>
function addEventListeners() {
var condition=true;
document.getElementById('form1')
.addEventListener('submit',
function(e){
e.preventDefault();
if(condition==true){
condition=false;
google.script.run
.withSuccessHander(reloadPg)
.addData(this);
}
});
}
window.reloadPg = function() {//Runs when server code has completed
console.log('reloadPg ran');
document.getElementById('linkToThisWebApp').click();//Click the link
}
</script>

Control Flow Between Server and Client and Back to Server for Google Apps Script

I have a question about the control flow between some JavaScript code running as bound functions within a google spreadsheet - so server side - and a dialog (that happens to be Modal, but Modeless is the same) that is client side.
While the code examples below work fine in that the dialog successfully calls the server side function as per the line below, and the withSuccessHandler works too.
google.script.run.withSuccessHandler(success_callback).getCredentials(this.parentNode)
But what I actually want to achieve is for some server side code to carry on executing once the dialog has gone; ideally from the point the .showModalDialog() function was called, but I'd be happy just passing control back to any server-side function.
Some example software is below; don't forget this works, just not how I want it too! Essentially the event handler for a menu item created by the the OnOpen() function calls a modal dialog to prompt the user for security credentials.
var html = HtmlService.createHtmlOutputFromFile('authorization_dialog');
SpreadsheetApp.getUi()
.showModalDialog(html, 'Authorization Dialog');
The HTML file:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form>
Authorization Code:
<input type="text" name="authorization_code"><br><br>
Account ID:
<input type="text" name="account_id"><br><br>
Enter account details...
<br>
<br><br>
<input type="button" value="OK"
onclick="google.script.run.withSuccessHandler(success_callback).getCredentials(this.parentNode)" />
<input type="button" value="Close"
onclick="google.script.host.close()" />
</form>
<script>
// Using this call back prevents the need to hit the Close Button after OK.
function success_callback() {
google.script.host.close(); // Close the dialog.
}
</script>
</body>
</html>
If you don't need a response from the server-side function, simply omit 'withSuccessHandler';
function func_client(){
google.script.run.func_server();
google.script.host.close();
}
In this case, the server-side code will continue executing without locking your client's UI - you can call any other functions inside 'func_server'.
If you'd like to process a response from the first function call, call the second function from 'success_callback'. The dialog will be closed without waiting for the google.script.run to complete, but the server code will continue executing.
In the example below, the 1st server function passes form data back to the client where 'success_callback' immediately invokes another server function that takes a while to complete (it logs each file in my Google Drive);
Client:
<form id="form">
.....
<input type="submit" id="ok" value="OK" />
<input type="button" value="Close" onclick="google.script.host.close()" />
.....
</form>
<script>
window.onload = function(){
document.getElementById("form")
.addEventListener("submit", function(e){
e.preventDefault();
google.script.run
.withSuccessHandler(success_callback)
.logFormData(this);
});
}
function success_callback(response) {
console.log(response);
google.script.run.scanFiles();
google.script.host.close(); // Close the dialog.
}
</script>
Server:
function showDialog(){
var ui = SpreadsheetApp.getUi();
//IMPORTANT: client-side scripts won't be executed
//without calling evaluate() on the HtmlTemplate object before passing it to UI;
var template = HtmlService.createTemplateFromFile("dialog");
var html = template.evaluate();
ui.showModalDialog(html, "dialog");
}
function logFormData(formData){
return formData;
}
function scanFiles() {
var files = DriveApp.getFiles();
var file;
while (files.hasNext()) {
file = files.next();
Logger.log(file.getName() + ": " + file.getMimeType());
}
}

Google apps script get user input with no length limit

I want to take user input (HTML specifically) using either:
var ui = SpreadsheetApp.getUi();
var response = ui.prompt('Paste HTML below');
or
var input = Browser.inputBox('Paste HTML below', Browser.Buttons.OK_CANCEL);
These work fine for small inputs, however when copying over the entire HTML for a page of interest an error occurs (in each case). This error cannot be caught, it simply crashes the script.
Do you know why this is happening? I can't find anything in the docs that mention limits on input size.
Any experience doing this a different way?
Edit: as per a suggestion in the comments, I have tried another method (below). This also fails (with no error message) when passed large input.
First I set up Page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Paste Sitemap Content Below
<textarea id="user-input-box" rows="4" cols="50"></textarea>
<script>
function logToConsole() {
var userInput = document.getElementById("user-input-box").value;
google.script.run.doSomething(userInput);
}
</script>
<input type="button" value="Close" onclick="logToConsole();google.script.host.close();" />
</body>
</html>
Then in file Code.gs
function testDialog() {
var html = HtmlService.createHtmlOutputFromFile('Page')
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi()
.showModalDialog(html, 'My custom dialog');
}
function doSomething(userInput){
Logger.log(userInput);
}
I just ran into the same problem and couldn't log the error. In my case as is yours, you're calling your logToConsole() function and then directly after you're closing the dialog by using google.script.host.close();
google.script.host.close() is the problem. For some reason it can cancel the script execution - this typically happens when you're sending a lot of data back. The trick is to use a successHandler when you call your script which then calls google.script.host.close(). This way, the data transfer from the dialog finishes correctly and when you call withSuccessHandler(), that callback closes the dialog. Try this amendment to your code:
<script>
function logToConsole() {
var userInput = document.getElementById("user-input-box").value;
google.script.run.withSuccessHandler(closeDialog).doSomething(userInput);
}
function closeDialog() {
google.script.host.close();
}
</script>
<input type="button" value="Close" onclick="logToConsole()" />

Create dropdown menu within a popup window

I have a list of records in multiple sheets (same workbook).
I currently have a dropdown menu within my googlesheet where if you select one of the records it will delete the row with that record.
However, I would like to give the option to either move it to another sheet or delete it. I was trying to use UiApp but then found out alot of the options are deprecated and that now I have to use HTMLService.
So what I'm looking to do is, once I select a record, have a popup that has two options.
Option 1 : a Move option (button) with a dropdown of the names of the other sheets within the workbook that will then move that record to that sheet
Option 2 : Delete the record
Option 3 : Cancel.
Is this possible? and if so, would someone be able to guide me to the right direction or a similar example so I can try and figure out how to get that going?
You can try creating a Custom dialogs
A custom dialog can display an HTML service user interface inside a Google Docs, Sheets, or Forms editor.
Custom dialogs do not suspend the server-side script while the dialog is open. The client-side component can make asynchronous calls to the server-side script using either the google.script API for HTML-service interfaces or server handlers for UI-service interfaces.
Code.gs
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Custom Menu')
.addItem('Show dialog', 'showDialog')
.addToUi();
}
function showDialog() {
var html = HtmlService.createHtmlOutputFromFile('Page')
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'My custom dialog');
}
Page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<select>
<option>Delete</option>
<option>Move</option>
</select>
</body>
</html>
With that, try reading about HTML Service: Communicate with Server Functions
google.script.run is an asynchronous client-side JavaScript API that allows HTML-service pages to call server-side Apps Script functions. The following example shows the most basic functionality of google.script.run — calling a function on the server from client-side JavaScript.
Here is a sample code for form:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
// Prevent forms from submitting.
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener('load', preventFormSubmit);
function handleFormSubmit(formObject) {
google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
}
function updateUrl(url) {
var div = document.getElementById('output');
div.innerHTML = 'Got it!';
}
</script>
</head>
<body>
<form id="myForm" onsubmit="handleFormSubmit(this)">
<input name="myFile" type="file" />
<input type="submit" value="Submit" />
</form>
<div id="output"></div>
</body>
</html>
Hope this helps!