In google sheets I have an input field for names that pops up when you press a button. I want all the names to appear in column B starting at cell B44. The names will continue to move down the column as you use the input field to add more names (B44, B45, B46...).
First I tried using appendRow, however I realized you can't specify a starting cell. I'm now using setValue and getRange('B44'). The problem with this is that each time you enter a new name into the input field it just replaces the last name and stays in cell B44.
code.gs
function addminitask() {
var html = HtmlService.createTemplateFromFile('minitask').evaluate()
var result = SpreadsheetApp.getUi().showModalDialog(html, ' ');
SpreadsheetApp.getActive().toast(result);
}
function AddRecord(name) {
var ss= SpreadsheetApp.getActiveSpreadsheet();
var mainSheet = ss.getSheetByName("DAILY HUB");
mainSheet.getRange('B44').setValue([name]);
}
function startForm()
{
var form = HtmlService.createHtmlOutputFromFile('minitask');
SpreadsheetApp.getUi().showModalDialog(form, 'Add Record');
}
function onOpen(e)
{
addMenu();
}
minitask.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
function AddRow()
{
var name = document.getElementById("name").value;
google.script.run.AddRecord(name);
}
</script>
</head>
<body>
Name: <input type="text" id="name" />
<input type="button" value="Add" onclick="AddRow()" />
</body>
</html>
In your script, how about the following modification?
From:
mainSheet.getRange('B44').setValue([name]);
To:
var values = mainSheet.getRange('B44:B').getDisplayValues();
var idx = values.findIndex(([b]) => !b);
var lastRow = (idx == -1 ? values.length : idx) + 44;
mainSheet.getRange(`B${lastRow}`).setValue(name);
By this modification, the value is put in the 1st empty cell below cell "B44".
Related
I work in widget where the user can add datas in specific row. In the Modal Dialog, he can type the row where i want add data and select data to add.
The data is linked to an other data with a macro and i want to add too this value.
This is my server side's script :
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function widget() {
const classeur = SpreadsheetApp.getActiveSpreadsheet();
const feuille = classeur.getActiveSheet();
const ui = SpreadsheetApp.getUi();
var widget;
widget = HtmlService.createTemplateFromFile("widget.html").evaluate();
ui.showModalDialog(widget, "Add new Row");
widget.setWidth(600);
widget.setHeight(600);
}
function getData(){
const classeur = SpreadsheetApp.getActiveSpreadsheet();
const feuille = classeur.getSheetByName("BDD");
var services = feuille.getRange("A2:A").getValues().filter(d =>d[0] !== "");
return services.map(d => "<option>" + d[0] + "</option>").join("")
}
function ajoutLigne(row,data) {
const classeur = SpreadsheetApp.getActiveSpreadsheet();
const feuille = classeur.getActiveSheet();
index = feuille.getActiveRange().getRow();
feuille.insertRowBefore(row);
feuille.getRange("A"+row).setValue(data);
feuille.getRange("B"+row).setValue("=RECHERCHEV(A"+row+";BDD!A:B;2;FAUX)");
}
This is my widget's HTML page :
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('JavaScript'); ?>
</head>
<body>
<p>Row : <input type="text" id="row"></p>
<br>
<p><i>Select data :</i>
<select id="data" name="data" class="form-control" required>
<option disabled selected>Choose ...</option>
<?!=getData()?>
</select>
</p>
<input type="button" class="button" value="SUBMIT" onclick="addRow();">
<input type="button" class="button" value="CLOSE" onclick="google.script.host.close();">
</body>
</html>
And this is my JavaScript's page :
<script>
function addRow(){
var row = document.getElementById("row").value;
var data = document.getElementById("data").value;
google.script.run.ajoutLigne(row,data);
}
</script>
When i launch my widget and sumbit datas, it's correctly reported in my Sheet's tab. But the macro generate an error:
formula parse error
and I don't understand why. The syntax is the same to the previous row. And if i modify 2 times the macro (first time to change one thing and second time to write the initial macro), it's work....
If anyone know the answer to this problem, I would be grateful. Thank you for advance for your help (this is the link of my Sheet).
When I saw your question, the formula is put using the following script.
feuille.getRange("B"+row).setValue("=RECHERCHEV(A"+row+";BDD!A:B;2;FAUX)");
But, when I saw your script included in your sample Spreadsheet, the formula is put using the following script.
feuille.getRange("B"+row).setValue("=VLOOKUP(A"+row+";BDD!A:B;2;FAUX)");
And, from your sample Spreadsheet and your comments, in the current stage, I thought that you might try to use VLOOKUP.
If my understanding is correct, how about the following modification?
From:
feuille.getRange("B"+row).setValue("=VLOOKUP(A"+row+";BDD!A:B;2;FAUX)");
To:
feuille.getRange("B"+row).setFormula("=VLOOKUP(A"+row+";BDD!A:B;2;FALSE)");
Reference:
setFormula(formula)
I am trying to create a role description generator which reads pre-written text from a Google Sheet and assembles it in blocks in a web app through selections (team, role, seniority level, etc.) in dropdown menus.
This is an example of what the data in the sheet looks like:
Team name
Team description
A-team
Description
B-team
Description
...
...
So far, for the team selection, I have created the dropdown menu which reads the data from the sheet, and pulls the names of each team into a dropdown list. But my problem is loading the corresponding team description text into the HTML page. I just can't seem to get it to work.
When pressing the generate button, what should happen is that the description for A-team is loaded, but instead I get [object MouseEvent].
Any suggestions? Thanks in advance! :)
Here's my code:
Code.gs
var url = "*spreadsheet URL*";
function doGet(e) {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
//get the data for the dropdown list
function valuesForList(list) {
//define the data
var ss = SpreadsheetApp.openByUrl(url)
var teamsSheet = ss.getSheetByName('Data');
var lastRow = teamsSheet.getLastRow();
var teamsRange = teamsSheet.getRange(1, 3, lastRow, 1);
//create a named range
ss.setNamedRange('teamsList', teamsRange);
//get the values from the range
var listValues = ss.getRangeByName(list).getValues();
return listValues;
}
//the function to show the data on the index.html
function PostInfo (userInfo){
//load the data
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues();
var teamList = data.map(function(r){ return r[0]});
var teamDesc = data.map(function(r){ return r[1]});
var position = teamList.indexOf(userInfo.teams);
if(position > -1){
return teamDesc[position];
} else {
return "Unavailable";
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
<script>
function onListSuccess(list) {
var listLength = list.length;
for (i=0; i<listLength;i++) {
var dropdown = document.getElementById("teams");
var opt = document.createElement("option");
dropdown.options.add(opt);
opt.text = list[i][0];
opt.value = list[i][0];
}
}
function onListSelect(teamDesc){
var text = teamDesc.toString().split(",");
document.getElementById('est').innerHTML = text;
}
</script>
</head>
<body>
<div id="main">
<h1>Role Description Generator</h1>
<p>
<label for="teams">Team:</label>
</p>
<p>
<select name="teams" id="teams" tabindex="2"></select>
</p>
<button id="btn">Generate</button>
<div>
<label for="est">Team description:</label>
<p id="est" name="est"></p>
</div>
</div>
</body>
<script>
function populateList(){
google.script.run.withSuccessHandler(onListSuccess).valuesForList('teamsList');
}
document.getElementById("teams").addEventListener("change", doStuff);
document.getElementById("btn").addEventListener("click", onListSelect);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.PostInfo(userInfo);
}
window.addEventListener('load', populateList);
</script>
</html>
Modification points:
In your script, when the dropdown list is changed, doStuff() is run. But in this case, google.script.run.PostInfo(userInfo) runs only the function of PostInfo at Google Apps Script. By this, the returned value is not used.
And, when the button is clicked, onListSelect is run. But in this case, teamDesc of onListSelect(teamDesc) is the event object. By this, such value of [object MouseEvent] is shown. I thought that this might be the reason of your issue.
By the way, when I saw your Google Apps Script, I noticed that PostInfo has a modification point. When var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues(); is run, I think that an error occurs. Because ws2 is not declared. In your case, is that ws? I thought that this might be due to your miscopy.
When you want to show the value from PostInfo when the button is clicked, how about the following modification?
Modified script:
HTML&Javascript side:
From:
document.getElementById("teams").addEventListener("change", doStuff);
document.getElementById("btn").addEventListener("click", onListSelect);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.PostInfo(userInfo);
}
To:
document.getElementById("btn").addEventListener("click", doStuff);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.withSuccessHandler(onListSelect).PostInfo(userInfo);
}
Google Apps Script side:
From:
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues();
To:
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws.getLastRow(),2).getValues();
Note:
In this modidication, it supposes that the Google Apps Script works fine and returns the correct values. Please be careful this.
Reference:
Class google.script.run
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.
I have created an HTML UI from a Google Sheet file I am working with. The UI pulls a list of vendors from my Vendor Database tab, allowing a user to select which vendor they'd like to place an order with from the HTML UI. Upon click of the Save button in the HTML UI, though, I'd like the file to populate cell B12 of the POTemplate tab with the user's selection, but am unsure how to do this. Right now, I have taken the following steps to make this happen, but with limited success:
APPS SCRIPT
This populates the HTML drop down list with vendor names from our Vendor Database tab in the active file:
function getVendors() {
var active = SpreadsheetApp.getActive();
var sheet = active.getSheetByName("Vendor Database");
var lastRow = sheet.getLastRow();
var myRange = sheet.getRange("A2:A" + lastRow);
var data = myRange.getValues();
var optionsHTML = "";
for (var i = 0; i < data.length; i+=1) {
optionsHTML += '<option>' + data[i][0] + '</option>';
};
return optionsHTML;
This attempts to grab the vendor selected in the HTML UI and populate the preferred cell in our POTemplate tab, B12:
function save(formObj) {
var vendor = formObj.vendor;
var app = SpreadsheetApp;
var orderSheet = app.getActiveSpreadsheet().getSheetByName("POTemplate");
orderSheet.getRange(B12).setValue(vendor);
}
HTML Code
<html>
<head>
</head>
<body>
<form>
<div>
<select id="vendor">
<?!= getVendors(); ?>
</select>
<select>
<?!= getATTN(); ?>
</select>
</div>
</form>
<input type="button" value="Save PO" class="button button2"
onClick="google.script.run.save(this.parentNode)" />
</body>
</html>
You can also do it like this:
code.gs:
function getSelectOptions() {
var data = SpreadsheetApp.getActive().getSheetByName('Vender Database').getDataRange().getValues();
var options = [];
//the loop skips the first line
for (var i = 1; i < data.length; i++) {
options.push(data[i][0]);
}
return options;
}
//for the webapp
function doGet()
{
var output=HtmlService.createHtmlOutputFromFile('test');
return output.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
//for a dialog
function showDialog(){
var ui=HtmlService.createHtmlOutputFromFile('test');
SpreadsheetApp.getUi().showModelessDialog(ui, 'Get Options');
}
test.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function() {
google.script.run
.withSuccessHandler(updateSelect)
.getSelectOptions();//this updates the select options everytime you load the page.
});
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]);
}
}
</script>
</head>
<body>
<select id="sel1"></select>
</body>
</html>
Saving the Selected Value in a Sheet
additional gs:
function savSelected(selected){
SpreadsheetApp.getActive().getSheetByName("POTemplate").getRange('B12').setValue(selected);
}
additional script function:
function savSelect(){
var selected=$('#sel1').val();
google.script.run.savSelected(selected);
}
additional html:
<select id="sel1" onChange="savSelect();"></select>
In your save() function, you need to surround "B12" in quotes.
function save(formObj) {
var vendor = formObj.vendor;
var app = SpreadsheetApp;
var orderSheet = app.getActiveSpreadsheet().getSheetByName("POTemplate");
orderSheet.getRange("B12").setValue(vendor);
}
In your HTML, you should not surround the google.script.run in quotes. Moreover, create a new function to make your life easier. You can modify the submitFormData() function in the HTML to include any other data that you might need to return.
<input type="button" value="Save PO" class="button button2"
onClick=submitFormData() />
<script>
function submitFormData() {
var vendor = document.getElementById("vendor").value;
var formObj = {};
formObj["vendor"] = vendor;
google.script.run.save(formObj);
}
</script>
(The getATTN() function wasn't provided, so I removed that from the HTML when testing this.)
I'm looking for a function that can get inputs like - "project", "locale", "date" and the function will go to the relevant sheet in the spreadsheet based on the "project" value and will show the relevant rows based on the locale and date.
For example -
Spreadsheet X includes 3 sheets - Project 1, project 2 & summary.
In the summary sheet, I have 3 variables - project, locale & date. I also have a button of "show".
Once I'll click on the "show" the function will show me the data according the variables.
Here's a simple example that I think may be close to what you want. It includes a search button and a clear button and expects you to select a cell on the row of the summary table that you desire to see the data of. After selecting a row on the summary table you click the search button and the data from that project page for that location and date will be display on the side bar. If you wish to clear the data then press the clear button.
Code.gs:
function searchForDataSideBar()
{
var ui=HtmlService.createHtmlOutputFromFile('searchinfordata');
SpreadsheetApp.getUi().showSidebar(ui);
}
function searchForData()
{
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Summary');
var cell=sh.getActiveCell();
var row=cell.getRow();
var cols=sh.getLastColumn();
var rg=sh.getRange(row, 1, 1, cols);
var vA=rg.getValues();
var sh1=ss.getSheetByName(vA[0][0]);
var rg1=sh1.getDataRange();
var vA1=rg1.getValues();
var s='';
for(var i=1;i<vA1.length;i++)
{
var data=vA1[i];
var a=vA[0][1];
var b=data[0];
var c=vA[0][2].valueOf();
var d=data[1].valueOf();
if(a==b && c==d)
{
s+=Utilities.formatString('Project Name:%s<br />Location:%s<br />Date:%s<br />Data1:%s<br />Data2:%s</br>Data3:%s</br>Data4:%s</br>Data5:%s</br><hr>',vA[0][0],data[0],Utilities.formatDate(new Date(data[1]), Session.getScriptTimeZone(), "E MMM dd,yy"),data[2],data[3],data[4],data[5],data[6])
}
}
return s;
}
I used a sidebar to put the buttons on and to display your data. The name of my file was searchinfordata.html:
<!DOCTYPE html>
<html>
<head>
<title>Searching for Data</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
function search()
{
google.script.run
.withSuccessHandler(found)
.searchForData();
}
function found(hl)
{
document.getElementById('found').innerHTML=hl;
}
function clearDiv()
{
document.getElementById('found').innerHTML='';
}
console.log('MyCode');
</script>
</head>
<body>
<div id="instr">Please Select a cell on the row in the summary tab that you wish to see the data for.</div>
<input type="button" value="Search" onClick="search();" />
<input type="button" value="Clear" onClick="clearDiv();" />
<div id="found"></div>
</body>
</html>
And here's the onOpen() function where you can build your menu or launch your sidebar.
function onOpen()
{
SpreadsheetApp.getUi().createMenu('My Tools')
.addItem('Show Sidebar','searchForDataSideBar')
.addToUi();
searchForDataSideBar();//launches your sidebar whenever you open your spreadsheet.
}
This is what the Summary Tab looks like:
P1 Tab:
P2 Tab:
P3 Tab:
And Finally the Sidebar: