get the form attached to a spreasheet - google-apps-script

I'm working with the "new" version of google SS.
I'd like to get the form attached to the spreadsheet I'm in, like this:
function findFormURL() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
return ss.getFormUrl();
}
However, this function does not work yet in the new version.
Anyway,it gives the URL of the form, which is interesting, but I'd like to have the form ID or object so I can then work with it, change some stuff etc. Is that possible ?

This is indeed annoying but there is a possible way to get around this missing feature using the drive search capabilities... I tested it with the code below and it worked.
I agree that this is far from ideal and requires to have a form that has the same unique name as your spreadsheet but it's better than nothing.
function getFormTest() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var nameToSearch = ss.getName();
Logger.log('title contains "'+nameToSearch+'"');
var forms = DriveApp.searchFiles('title contains "'+nameToSearch+'"');
while (forms.hasNext()){
var formDoc = forms.next();
Logger.log(formDoc.getMimeType());
if(formDoc.getMimeType()=='application/vnd.google-apps.form'){
break;
}
}
Logger.log('formDoc = '+formDoc);
var form = FormApp.openById(formDoc.getId());
var items = form.getItems();
for(var i in items){
Logger.log(items[i].getTitle()+' '+items[i].getType());
}
}

Related

How to update HTML file when selecting a cell in Google Sheets using Google App Script

I tried to Google a solution for a while but couldn't find an answer.
I have a Google spreadsheet with an HTML file and a script, I want to be able to select any cell in the Google sheet and for its value to be populated directly in a textarea HTML element in my HTML file.
Once I am able to implement this functionality, I will be expanding on this be modifying the data before it's displayed in the HTML file, but this is outside the scope of this question.
The setup looks like this:
enter image description here
I managed to get this done by adding the button on the top "Get Data" and using onclick with google.script.run.withSuccessHandler (please see the code below). However, I would like to be able to do the same without having to click the button every time.
<button id="Get Data" onclick="getData()"><b>Get Data</b></button>
<script>
function getData() {
google.script.run.withSuccessHandler(updateElement).activeCell();
}
function updateElement(values) {
var column1Value = values[0];
var column2Value = values[1];
var column3Value = values[2];
var column1 = document.getElementById('column1');
column1.value = column1Value
var column2 = document.getElementById('column2');
column2.value = column2Value
var column3 = document.getElementById('column3');
column3.value = column3Value
var column4 = document.getElementById('column4');
column4.value = column3Value
var doneMessage = document.getElementById('doneMessage');
doneMessage.innerHTML = ""
}
</script>
function activeCell(){
var sheet = SpreadsheetApp.getActiveSheet();
var rowIndex = sheet.getActiveCell().getRow();
var column1Value = sheet.getRange(rowIndex, 2).getValue();
var column2Value = sheet.getRange(rowIndex, 3).getValue();
var column3Value = sheet.getRange(rowIndex, 4).getValue();
return [column1Value, column2Value, column3Value]
}
I tried using onmousedown, but it only works when having the mouse on the HTML element rather than the spreadsheet, I assume because the HTML element is an iframe.
I tried also onSelectionChange(e) which works really well until I couldn't figure out how to send the info of the selected cell to the HTML file.
Thanks for the help!
If I understand what you want to do correctly, I suggest a work-around to refresh the data with a timer instead of click
Wait for the page to load with addEventListener("DOMContentLoaded") in order to execute getData()
window.setTimeout() in the successHandler() function (you can modify duration)
<script>
window.addEventListener("DOMContentLoaded", (event) => {
console.log("DOM loaded");
getData();
});
function getData() {
google.script.run.withSuccessHandler(updateElement).getData();
}
function updateElement(e) {
//do stuff
window.setTimeout(getData, 5*1000); //repeat getData() in 5sec
}
</script>

Fill Google form using Google Apps Script

I just tried my coding below...but the problem is, my google apps script just submit the form with blank value for both "short answer" field. Can someone show me somelight how i can solve my problem. TQ.
function myFunction() {
var dapatkanForm = FormApp.openByUrl('https://docs.google.com/forms/d/1S1F3cKMwkXm3gVt00RegQ-GCzRfELOq74IH9WEk8SlU/edit?usp=sharing');
var nama = dapatkanForm.getTitle();
var listIsiText = dapatkanForm.getItems(FormApp.ItemType.TEXT);
var panjangArray = listIsiText.length;
for(var i=0; i<panjangArray; i++)
{
var textTemp = listIsiText[i];
var nakSet = textTemp.asTextItem();
var response = nakSet.createResponse('MOHD ANIS BIN AZINAN');
}
var formResponse = dapatkanForm.createResponse();
formResponse.submit();
Logger.log(nama);
}
Your code is very close to the solution. You just need one additional method. The following line…:
var formResponse = dapatkanForm.createResponse();
…should end up looking like:
var formResponse = dapatkanForm.createResponse().withItemResponse(response);
By using .withItemResponse() the form submit will upload your responses instead of being void. Please, ask me for more clarifications if you still need help.

Uploading Google Spreadsheet to a Google Site

I'd like to begin by stating that the end goal is to display our company directory (a list of our employees names/job title/extension#/office location/email), which is in a Google Sheet, on a page in one of our Google Sites.
I tried to use Google's embed function, and it works... but it is very clunky, does not have a "Sort" function, and it just looks weird.
I pulled a Google Apps Script from somewhere online like 3 months ago and it actually did pull in a way that made me happy:
(This is as it appears currently on the Google Sites page. So in this screenshot, the embedded Sheet is at the top. The Sheet when, imported via the script, is below. Yes, they are both on the same page. I'm in testing!)
This is the code I used (I THINK - I don't remember how I implemented it):
function myFunction() {
}
function onOpen(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(ScriptProperties.getProperty("page url") == null){
ss.addMenu("List page", [{name: "Create list", functionName: "create_list"},null,
{name: "Fetch list items", functionName: "fetch_items"}]);
}
else{
ss.addMenu("List page", [{name: "Push Items", functionName: "push_items"}]);
}
}
function create_list() {
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var parent_page = Browser.inputBox("URL of the parent page:");
var title = Browser.inputBox("Choose a name for your list page:");
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var list = SitesApp.getPageByUrl(parent_page).createListPage(title, title.split(' ').join(''), '', data[0]);
ScriptProperties.setProperty("page url", list.getUrl());
onOpen();
push_items();
}
function push_items(){
var done = false;
while(!done){
try{
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var list = SitesApp.getPageByUrl(ScriptProperties.getProperty("page url"));
var list_items = list.getListItems();
for(i in list_items){
list_items[i].deleteListItem();
}
for(var i = 1; i < data.length; i++){
var item = list.addListItem(data[i]);
}
done = true;
}
catch(e){
}
}
SpreadsheetApp.getActiveSpreadsheet().toast(ScriptProperties.getProperty("page url"), "List page updated", 10);
}
function fetch_items(){
var url = Browser.inputBox("URL of your list page:");
var col_number = Browser.inputBox("Number of columns in the list:");
var data = new Array();
var list_items = SitesApp.getPageByUrl(url).getListItems();
for(i in list_items){
var row = new Array();
for(j = 0; j < col_number; j++){
row.push(list_items[i].getValueByIndex(j));
}
data.push(row);
}
SpreadsheetApp.getActiveSheet().getRange(1, 1, data.length, data[0].length).setValues(data);
}
[I do not take credit for writing this!]
So I would like to ask (since this ceases to make much sense to me) is if this is viable code for a Google Apps Script, and if so, how do I implement it to output Sheet data similarly in the same type of format as in the screenshot?
Alternatively, is there a better way to display this Sheet data in Google Sheets?
A totally different alternative would be to use Romain Vialard's "awesome tables" gadget. It works... awesome, and it is really easy to use. Besides, it admits filters, ...

An alternative way to pass data (eg, array values) through handler without using ScriptProperties

I built an app in which I use ScriptProperties to store data from a handler to its but(e) function. This was working nice, until other people started using the same spreadsheet at the same time. So often happens that one person is taking a time thinking about what item choose from a checkbox menu and another person uses the same function, changing the data stored at scriptProperties and affecting the use of the function by the first person.
What is the best way to fix it, using an alternative way to pass information through the handler?
Here one sample of one of theese functions (in which I'm using ScriptProperties to pass the values ofletterSpreadsheetId and recipientArray):
function letter(letterSpreadsheetId){
ScriptProperties.setProperty('letterSpreadsheetId', letterSpreadsheetId); // different people may have different letterSpreadsheetId;
ScriptProperties.setProperty('letter', 1); // to be used in another function
var activeSheet = ss.getActiveSheet();
var app = UiApp.createApplication().setHeight(400).setWidth(600);
var panel = app.createVerticalPanel(); // you can embed that in a form panel
var label = app.createLabel("Choose a receiver").setStyleAttribute("fontSize", 18);
app.add(label);
var sheet = SpreadsheetApp.openById(letterSpreadsheetId).getSheetByName("receivers");
var recipientArray = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
var item3Panel = app.createHorizontalPanel();
item3Panel.add(app.createLabel("receiver"));
var listBox = app.createListBox().setName('item3');
for(var i = 0; i < (recipientArray.length); i++){
listBox.addItem(recipientArray[i][1]);
}
item3Panel.add(listBox);
var recipientArrayStr = JSON.stringify(recipientArray);
ScriptProperties.setProperty('recipientArr', recipientArrayStr);
var handlerBut = app.createServerHandler("butAnswerLetter").addCallbackElement(panel);
var but = app.createButton("submit").setId("submitButton2").addClickHandler(handlerBut);
panel.add(item1Panel)
.add(item2Panel)
.add(item3Panel)
.add(but)
.add(app.createLabel().setId("answer"));
var scroll = app.createScrollPanel().setPixelSize(600, 400).setTitle("My title 1");
scroll.add(panel);
app.add(scroll);
ss.show(app);
}
function butAnswerLetter(e){
var letterSpreadsheetId = ScriptProperties.getProperty('letterSpreadsheetId');
var recipient = e.parameter.item3;
ScriptProperties.setProperty('recipient', recipient);
var recipientArrayRecovery = ScriptProperties.getProperty('recipientArr');
var recipientArray = JSON.parse(recipientArrayRecovery);
for(var i=0;i<recipientArray.length;i++){
if(recipient == recipientArray[i][1]){
var usedRecipientArray = recipientArray[i];
}
}
You have 2 possibilities (that I know), either use userProperties instead of script-Properties as these are associated with the user but it will require the user to login and authorize, or - and this will work in every case even if the app is accessed anonymously, use the tags that you can write on almost any widget.
the syntax is quite simple, here is a small code example :
function doGet(){
var app = UiApp.createApplication().setTitle('test_TAG');
var list = app.createListBox(true).setVisibleItemCount(5).setPixelSize(30,450).setName('list');
var handler = app.createServerHandler('show').addCallbackElement(list);
list.addChangeHandler(handler);
var data = [];
for(var n = 0;n<20;n++){
list.addItem(n+' ');
data.push('available value = '+Number(n+1));
}
list.setTag(data.toString());
app.add(list);
return app
}
function show(e){
var app = UiApp.getActiveApplication();
var data = e.parameter.list_tag.split()
var selected = e.parameter.list;
app.add(app.createTextBox().setText(selected).setPixelSize(200,20));
app.add(app.createTextArea().setText(data.join()).setPixelSize(200,300));
return app;
}
testable here
Edit
following Zig's pertinent comment :
I forgot to mention the hidden widget (or a textBox / area set to invisible, useful for debugging when you want to check what it contains ! ) that is also useable of course...
The comment about a user having multiple windows showing the same app is also worth mentioning !
All in all you have 3 possibilities after all !
(thanks to Zig Mandel)

Listbox not opening handler

I am trying to create a menu in a listbox format where user chooses an option and then another uiapp is shown with the info they selected. I am having an issue here that when I opened google gives me an error that says Error encountered. An expected error occurred. I think it has to do with the setId part, if I remove one of the setId's the error doesnt happen. is this even possible?
function doGet(e) {
var app = UiApp.createApplication().setTitle("Services");
var dropDownList = app.createListBox().setName('list').setId('list');
var infoLabel = app.createLabel('Scroll around to select the service desired').setId('infoLabel');
var panel = app.createVerticalPanel();
//addItem fills the list
dropDownList.addItem("Option 1").setId("add");
dropDownList.addItem("Option 2");
panel.add(dropDownList);
panel.add(infoLabel);
app.add(panel);
var info = app.getElementById("add");
var handler2 = app.createServerHandler('display2');
info.addClickHandler(handler2);
app.add(dropDownList);
app.add(infoLabel);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
function display2(e) {
var app = UiApp.createApplication();
var html = app.add(app.createHTML("<p><p><b>You have selected this option</b> </p>")).setHeight(220).setWidth(220);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
return app;
}
Are you deploying this as a web app or a script within a Spreadsheet?
If you are deploying this as a web app, then SpreadsheetApp.getActiveSpreadsheet() will not work - replace it with SpreadsheetApp.openById(id) where id is your spreadsheet ID which you will find in the URL when you open the file in the browser.
If you want to deploy this w/in a spreadsheet through a menu item or a simple button, then that works as is.
I was able to just copy paste your code and get the listbox part working fine -
https://docs.google.com/spreadsheet/ccc?key=0AkJNj_IM2wiPdHRYQThlaGVVSk04R052ZGNqclhEZWc#gid=0
Update -
I now understand what you are trying to do. Couple of things - you want to make sure you are adding a callback element via handler.addCallbackElement(myWidget) otherwise, you will not be able to read the value of the element. Second thing is that you don't need a server handler on each option in a dropdown list. Just having one handler will fire it for every change and you'll be able to get the option you selected.
I've cleaned up the code here below and also updated the spreadsheet to use this code.
function showUI() {
var app = UiApp.createApplication().setTitle("GeekSquad Services");
var infoLabel = app.createLabel('Scroll around to select the service desired');
var dropDownList = app.createListBox().setName('list').setId('list');
dropDownList.addItem("Option 1");
dropDownList.addItem("Option 2");
//you can add as many options here manually or dynamically
var handler = app.createServerHandler('dropDownCallback')
handler.addCallbackElement(dropDownList);
dropDownList.addClickHandler(handler);
var panel = app.createVerticalPanel();
panel.add(dropDownList);
panel.add(infoLabel);
app.add(panel);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
function dropDownCallback(e) {
var app = UiApp.createApplication();
var html = app.add(app.createHTML("<b>You have selected this option</b> " + e.parameter.list));
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
Update #2 -
If you want to fork off and create different app instances thats easy (though its unclear why wouldn't just change panels).
function dropDownCallback(e) {
if(e.parameter.list === 'Option 1'){
var app = UiApp.createApplication();
var html = app.add(app.createHTML("Here for option!"));
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
else if (e.parameter.list ==== 'Option 2'){
//create and show other App here or whatever else
}
//refactor this better to not repeat code.
}