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.
Related
I am a student who learn to code Apps Script. I want to create a form with 2 number input, let say A & B, with a button. When user submit the form, the script will search column A & B in active Google Spreadsheet sheet that match with 2 input and query a result in the column C on the same row. Finally, the result C will appear below the form.
The problem is that, when the form appear, I input 2 values but the result don't work.
I wrote two file code like this in Apps Script:
The HTML file
<form onsubmit="handleFormSubmit(event)">
<label for="inputA">height:</label><br>
<input type="number" id="inputA" name="inputA"><br>
<label for="inputB">weight:</label><br>
<input type="number" id="inputB" name="inputB"><br>
<input type="submit" value="Submit">
</form><br>
<script>
function handleFormSubmit(event) {
// Prevent the form from refreshing the page
event.preventDefault();
// Get the input values from the form
var inputA = document.getElementById("inputA").value;
var inputB = document.getElementById("inputB").value;
// Search the Google Sheet for a matching row
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var result = "";
for (var i = 0; i < data.length; i++) {
if (data[i][0] == inputA && data[i][1] == inputB) {
result = data[i][2];
break;
}
}
// Display the result on the page
var resultContainer = document.getElementById("result");
resultContainer.innerHTML = result;
}
</script>
<label for="resultA">result is:</label><div id="result"></div>
<!-- Result will be added here -->
The script file:
function doGet(e) {
return HtmlService.createTemplateFromFile('searchForm.html')
.evaluate();
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
The sheet link is here: https://docs.google.com/spreadsheets/d/1DqjBbU4b0uDTtjYsBFVFDuvA9fthR1N3KUCtkvwRowc/edit?usp=sharing
I think the problem is the code in script tag but I don't quite sure.
Modification points:
In your script, it seems that you are using Google Apps Script in the Javascript on the HTML side. Google Apps Script can be used on the server side. So, in this case, google.script.run is used.
When these points are reflected in your script, how about the following modification?
Modified script: searchForm.html
HTML & Javascript:
<form>
<label for="inputA">Input A:</label><br>
<input type="number" id="inputA" name="inputA"><br>
<label for="inputB">Input B:</label><br>
<input type="number" id="inputB" name="inputB"><br>
<input type="submit" value="Submit" onclick="handleFormSubmit(event)">
</form>
<div id="result">
</div>
<script>
function handleFormSubmit(event) {
event.preventDefault();
var inputA = document.getElementById("inputA").value;
var inputB = document.getElementById("inputB").value;
google.script.run.withSuccessHandler(result => {
var resultContainer = document.getElementById("result");
resultContainer.innerHTML = result;
}).sample(inputA, inputB);
}
</script>
Google Apps Script: code.gs
function doGet(e) {
return HtmlService.createTemplateFromFile('searchForm.html').evaluate();
}
function sample(inputA, inputB) {
var sheet = SpreadsheetApp.getActiveSheet(); // or var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var data = sheet.getDataRange().getValues();
var result = "";
for (var i = 0; i < data.length; i++) {
if (data[i][0] == inputA && data[i][1] == inputB) {
result = data[i][2];
break;
}
}
return result;
}
Note:
As an important point of this modification, google.script.run is run with the asynchronous process. Please be careful about this.
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".
Thit is a sample modification. So, please modify this for your actual situation. If you want to know about google.script.run more, you can also see various sample scripts at Stackoverflow.
Reference:
Class google.script.run (Client-side API)
I am looking to simulate the behavior of Google Classroom's assignment feature that allows you to make a copy of a document for a list of students. This copy retains ownership by the creator and adds the student as an editor.
I have succeed in making this work with a script attached to a template doc. The teachers can paste a list of email addresses into a custom pull-out, and on submission a loop creates each doc with the permissions.
Attempts:
I can use copy() functionality to copy the doc, but the attached script goes along for the ride and is then accessible by the students. This is not a major security risk, but has the potential to be abused.
I can have moderate success by using the regularly mentioned method of looping through all elements and appending them to the new doc, but so far I have not been able to make everything work in this way. Some images, tables, and other elements that teachers might create do not format properly in the new doc.
Hopeful solutions:
Is there a way to remove the script from the copied doc? or
Is there a way to use permissions to only allow the script to be run by faculty? (We do have an Org Unit for faculty, but my tests with the AdminDirectory module leave me concerned about permissions once all faculty is using the tool.) or
Knowing that our student email addresses are formatted differently from our faculty email addresses, could I programmatically block the script based on email address parsing of the current user?
I've gone in circles and keep ending up at the posts explaining how to copy elements one at a time into a new doc. This does not appear to be sufficient due to formatting so I'm hoping one of the other solutions involving keeping the copy() function is possible.
Sidebar Code sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
</script>
<style>
textarea{
font-size: .9em;
width: 90%;
height: 300px;
}
</style>
</head>
<body>
<textarea id="addressList">
Paste class email list here with spaces between addresses.
</textarea>
<p>
<input id="submit" type="button" value="Distribute to Students" onclick="distributor();" />
<input id="reset" type="button" value="Reset" onclick="reset();" />
</p>
<script>
function reset(){
document.getElementById('addressList').value = "Paste class email list here with spaces between addresses.";
document.getElementById("submit").disabled = false;
}
var mainOut = "";
function cleanUp(output){
mainOut += output;
document.getElementById('addressList').value = mainOut;
}
function distributor(){
document.getElementById("submit").disabled = true;
var nameList = document.getElementById('addressList').value; // get email list
var list = nameList.split("\n"); // break it up
// loop through names
for(let i = 0; i < list.length; i++){
// create a doc for each student and return success to the UI
google.script.run.withSuccessHandler(cleanUp).distro(list[i],i+1,list.length);
}
}
</script>
</body>
</html>
Current Script Code.gs
// student email addresses end in a 2 digit class year, faculty does not
var event;
var emailParts = Session.getEffectiveUser().getEmail().split('#');
var username = emailParts[0];
var classCheck = username.substring(username.length-2);
var validUser = false;
// using school email address style, determine if student or teacher to hide the UI changes
if(isNaN(classCheck)){
validUser = true;
}
// Custom Menu
function onOpen(e){
if(validUser){
event = e;
var ui = DocumentApp.getUi();
var faMenu = ui.createMenu('FA')
.addItem('Open Distribution Tool', 'openTool').addToUi();
}
}
/**
* Creates Custom Sidebar for emailing teams from spreadsheet
*/
function openTool() {
if(validUser){
var html = HtmlService.createHtmlOutputFromFile('sidebar');
html.setTitle('Share with Students');
html.setWidth(400);
html.setContent(html.getContent());
DocumentApp.getUi().showSidebar(html);
}
}
// create copy, set permissions
function distro(studentEmail,count,total){
var output = "";
if(validUser){
var teacherEmail = 'xxx#xxx.org'; // this will be replaced by email of logged in user
var thisDoc = DocumentApp.getActiveDocument();
let studentName = studentEmail.split('#')[0];
if(studentEmail != "" && studentEmail != null){
let filename = thisDoc.getName() + "- " + studentName;
let newDoc = DriveApp.getFileById(thisDoc.getId()).makeCopy(filename);
newDoc.setOwner(studentEmail);
newDoc.addEditor(teacherEmail);
output += "Created doc " + count + "/" + total + ": " + filename + "\n";
}
}
return output;
}
Issue:
I don't think you can programmatically remove the bound script from a copied document.
In theory, this is possible if you use Apps Script API, by calling projects.updateContent and set and empty content for your Files.
Nevertheless, this requires knowing the scriptId, and you cannot programmatically retrieve the scriptId of a bound script which is not the current one (for the current one, Session.getScriptId() can be used). See this answer, for example, and this related feature request:
Retrieving Project ID of Container-Bound script
Workaround - use libraries:
As a workaround, I'd suggest putting at least some of the script code in a different, standalone script, and make your template call this library. This way, the library source code would not be available to the script bound to the copied file, which could only run the different functions defined in the library.
For example, you could move onOpen and distro to another script:
Library Code.gs:
// student email addresses end in a 2 digit class year, faculty does not
var event;
var emailParts = Session.getEffectiveUser().getEmail().split('#');
var username = emailParts[0];
var classCheck = username.substring(username.length-2);
var validUser = false;
// using school email address style, determine if student or teacher to hide the UI changes
if(isNaN(classCheck)){
validUser = true;
}
// Custom Menu
function onOpen(e){
if(validUser){
event = e;
var ui = DocumentApp.getUi();
var faMenu = ui.createMenu('FA')
.addItem('Open Distribution Tool', 'openTool').addToUi();
}
}
// create copy, set permissions
function distro(studentEmail,count,total){
var output = "";
if(validUser){
var teacherEmail = 'xxx#xxx.org'; // this will be replaced by email of logged in user
var thisDoc = DocumentApp.getActiveDocument();
let studentName = studentEmail.split('#')[0];
if(studentEmail != "" && studentEmail != null){
let filename = thisDoc.getName() + "- " + studentName;
let newDoc = DriveApp.getFileById(thisDoc.getId()).makeCopy(filename);
newDoc.setOwner(studentEmail);
newDoc.addEditor(teacherEmail);
output += "Created doc " + count + "/" + total + ": " + filename + "\n";
}
}
return output;
}
Then, share this script as a library, and use it in your template script, which could be like this (where LIBRARY is the identifier for your previously shared library):
Template Code.gs:
// student email addresses end in a 2 digit class year, faculty does not
var event;
var emailParts = Session.getEffectiveUser().getEmail().split('#');
var username = emailParts[0];
var classCheck = username.substring(username.length-2);
var validUser = false;
// using school email address style, determine if student or teacher to hide the UI changes
if(isNaN(classCheck)){
validUser = true;
}
// Custom Menu
function onOpen(e){
LIBRARY.onOpen(e);
}
/**
* Creates Custom Sidebar for emailing teams from spreadsheet
*/
function openTool() {
if(validUser){
var html = HtmlService.createHtmlOutputFromFile('sidebar');
html.setTitle('Share with Students');
html.setWidth(400);
html.setContent(html.getContent());
DocumentApp.getUi().showSidebar(html);
}
}
// create copy, set permissions
function distro(studentEmail,count,total){
LIBRARY.distro(studentEmail, count, total);
}
In this example, sidebar.html would also be contained in your template script.
Note:
This is a basic example just to show how this could be done, and could probably be improved. For example, it should be possible to also move openTool and the .html file to the library code, even though calling distro via google.script.run could become tricky: see Call Library function from html with google.script.run.
Reference:
Apps Script: Libraries
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
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.
Broken down to its most elemental, what I want to do from a web app context is:
Ask for some data from user.
Display some data to user.
Ask for some more data from user.
Use all that data in a web app.
More specifically, I am trying to build a Google Script web app that does the following:
Presents an html page where the user can input a user ID number.
Takes that number and finds the last line on a spreadsheet belonging
to that user.
Displays that last line number to the user (this is
the first point at which I am stumped—see below for what I have
tried).
Presents a 2nd html input page where the user can either
accept the last line info displayed to them, or enter an alternate
number (and some other info).
All of that info is then used to
create a Google Doc and add info about that Google Doc on a new row
in a Google spreadsheet.
I have tried:
(a) Class PromptResponse [ui.prompt]
(b) alert(Prompt)
(c) showModalDialog
(d) show ModelessDialog
All of these failed as they apparently must be triggered from a bound app.
I considered the concept of having two doGet statements in a single webApp which led me to
Linking to another HTML page in Google Apps Script, but that seems to deal with a two-page SINGLE html rather than two separate html pages (which is what I think I need).
I also considered using the Browser.msgBox in the Class CacheService but that produced the same context error as (a) thru (d) above.
Lastly, I thought about—rather than displaying the user ID number from (1) above—saving the variable and inserting it later in the script (i.e., loading it in (4) above). That led me to the CacheService. But I could not see how to make that work and in any event, it’s not really what I want to do.
GS
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
function getSongId(objArgs){
// Get User's Catalog SS URL from Master Users List
var userId = objArgs.user;
var masterSSId = "ID";//This is the ID to the master users list SS.
var userSS = SpreadsheetApp.openById(masterSSId);//Open
var userSheet = userSS.getActiveSheet();
var nameOfUserRange = "User" + userId; //this constructs the user ID, like "user101"
Logger.log("nameOfUserRange = " + nameOfUserRange);
var userNamedRange = userSS.getRangeByName(nameOfUserRange); //this returns "Range" to pass its value on to future code lines
var cell = userNamedRange.activate(); //activates range and first cell in range
var namedUrlRange = userSS.getRange('SongsSheetUrl'); //this gets the SongSheetUrl named range
var userCol = namedUrlRange.getColumn(); //this gets col # of namedUrlRange
Logger.log("userCol = " + userCol);
var userSsUrl = cell.offset(0, userCol-1, 1, 1). getValue(); //this gets the user's Catalog SS URL
Logger.log("userSsUrl = " + userSsUrl);
var ss = SpreadsheetApp.openByUrl(userSsUrl);
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
var songId = lastRow+1;
Logger.log("songId = " + songId);
//some code here that displays songID to user
HTML "Index"
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<center> Enter your User ID below.
<input id="userId" type="text" placeholder="User ID"><br><br>
<button onclick="saveUserInput()">Continue</button>
</center>
<script>
window.saveUserInput = function() {
var user = document.getElementById('userId').value;
console.log('userId: ' + userId)
google.script.run
.withSuccessHandler(openPrompt)
.getSongId({user:user})
}
function openPrompt(results){
window.open(results.url, '_blank').focus();
}
</script>
</body>
</html>
songId Code
function getSongId() {
var masterSSId = "ID";//This is the ID to the master users list SS.
var userSS = SpreadsheetApp.openById(masterSSId);//Open
var userSheet = userSS.getActiveSheet();
var nameOfUserRange = "User" + userId; //this constructs the user ID, like "user101"
var userNamedRange = userSS.getRangeByName(nameOfUserRange); //this returns "Range" to pass its value on to future code lines
var cell = userNamedRange.activate(); //activates range and first cell in range
var namedUrlRange = userSS.getRange('SongsSheetUrl'); //this gets the SongSheetUrl named range
var userCol = namedUrlRange.getColumn(); //this gets col # of namedUrlRange
var userSsUrl = cell.offset(0, userCol-1, 1, 1). getValue(); //this gets the user's Catalog SS URL
var ss = SpreadsheetApp.openByUrl(userSsUrl);
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
var songId = lastRow+1;
}
As noted, I got "context" errors with everything I tried. BTW, I also created a web app that had 2 GS pages and 2 Index pages, and that just displayed both html pages on one page, and I still couldn't figure out how to display the User ID.
Finally, I spent a lot of hours, and used a lot of search terms, both at SO and the web in general trying to find someone else that has tackled this problem—and came up goose eggs.
Note: To respect "minimal, and verifiable," I have not included the script that asks for the 2nd set of info, but it is written and works.
Update: The following SO Question/Answer showed up to the right of this question: "Web apps+ remotely using script" after I posted it
It seems to in part solve my problem. At least it does display the user's User ID input, but I need it to display info I pull from a Google sheet based on the User ID (i.e., the songId). Using the doGet(e) approach, I still don't know where to put the getSongIdcode that gets the songId. I have added that code above.
Revised Code
gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
function getSongId(uObj) {
var userId = uObj.user;
var masterSSId = "ID";//This is the ID to the master users list SS.
var userSS = SpreadsheetApp.openById(masterSSId);//Open
var userSheet = userSS.getActiveSheet();
var nameOfUserRange = "User" + userId; //this constructs the user ID, like "user101"
Logger.log("nameOfUserRange = " + nameOfUserRange);
var userNamedRange = userSS.getRangeByName(nameOfUserRange); //this returns "Range" to pass its value on to future code lines
var cell = userNamedRange.activate(); //activates range and first cell in range
var namedUrlRange = userSS.getRange('SongsSheetUrl'); //this gets the SongSheetUrl named range
var userCol = namedUrlRange.getColumn(); //this gets col # of namedUrlRange
var userSsUrl = cell.offset(0, userCol-1, 1, 1). getValue(); //this gets the user's Catalog SS URL
var ss = SpreadsheetApp.openByUrl(userSsUrl);
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
var songId = lastRow+1;
Logger.log("songId = " + songId);
return songId;
}
html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<center>
Enter your User ID below.
<input id="userId" type="text" placeholder="User ID"><br>
<input type="button" value="Continue" onclick="saveUserInput()">
<div id="results"></div>
</center>
<script>
function saveUserInput() {
var user = document.getElementById('userId').value;
google.script.run
.withSuccessHandler(function(hl){
document.getElementById('results').innerHTML=hl;
})
.getSongId({user:user})
}
</script>
</body>
</html>
Try something simple first. Just to see that you can get the client and the server communicating:
html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Enter your User ID below.
<input id="userId" type="text" placeholder="User ID"><br>
<input type="button" value="Continue" onclick="saveUserInput()">
<div id="results"></div>
<script>
function saveUserInput() {
var user = document.getElementById('userId').value;
google.script.run
.withSuccessHandler(function(hl){
document.getElementById('results').innerHTML=hl;
})
.getSongId({user:user})
}
</script>
</body>
</html>
Then use a simple getSongId() function:
function getSongId(uObj) {
return uObj.user
}
I would use this sort of doGet()
function doGet() {return HtmlService.createHtmlOutputFromFile('Index');
}
You html doesn't have any scriptlets that need to be evaluated. It's not really a template.
Then test the getSongId by itself and once that works you can return it to the div and later if you wish you can create another page.