How can we pass objects/arrays to html-nested script to loop in in GAS?

Viewed 61

Tl;Dr: How can a html-nested script loop if I am not able to give it dynamic boundaries?

I can't manage to gather multiple 'selects' values because the number of selects and its IDs are dynamically generated.

'selects'

More precisely I don't manage to pass the info the html-nested js script needs to perform correctly its loop as it is on html side (if i am not wrong).

I have been looking for answers or clues but i'm starting to think i am wrong from the beginning. I am a newbie sorry.

So here's my situation:

  • I have a web-app (GAS) page rendered thanks to DoGet() on app loading. Before it is evaluated as html, many javascript scripts are involved as all the web app is based on database data.
  • So then I have some dynamically built forms composed of 3 selects each, the number of forms is not static and has to be calculated during loading.
  • At the end of the forms i have a button. The goal of the button is to save all the selects values into a spreadsheet, having enough information (like IDs in div) to know where to save each select value.

So for now i have this:

  1. The page loads and is rendered
  2. User chooses selects' values
  3. When user clicks on 'save' button, a document.getElementById("button-id").addEventListener("click", doSomething) triggers the html-nested function which uses the value thanks to document.getElementById("divID").value;

So far so good. My problem is: I need to be able to gather several selects values and not only one, and I don't know the div IDs since they are generated dynamically during the doGet(). I have the information I need on JavaScript side (.gs sheets, I don't know what it is called technically), but i can't seem to pass objects/arrays from the javascript side to the html nested scripts.

My first idea was to use something like that in html-nested script (the code is just to give the idea):

var selectsValues = {};
for (let form in ojectfromJavaSide){
selectValues[form][Select1] = x
selectValues[form][Select2] = y
selectValues[form][Select3] = z
}

But even if i declare "ojectfromJavaSide" in the html object that is then evaluated during doGet, it seems like it is "lost in rendering".

I think I am just not in the right direction to solve this.

EDIT

OK sorry my post was ill-structured, it wasn't intentional. OK i just tried the document.querySelector('#winner').value; method.

Here are the bits of code involved. I didn't paste them at the beginning since it was confusing.

The button that is listened to:

<button type="button" class="btn btn-primary btn-lg" id="savepicks">SAVE PICKS</button>

by:

document.getElementById("savepicks").addEventListener("click", StartSaving);

which runs this function (this is the JS function nested in html as i called it:

 function StartSaving(){

    const select = document.querySelector('#winner').value;
    google.script.run.SavePicks(select);
  }

it should get the value of following:

<option value="1" id="winner">Player X</option>

which calls this - For the moment i just want to check the values it grabs:

function SavePicks(select){
  Logger.log(select);
}

It is working with querySelector() but not with querySelectorAll which returns null (with or without .value at the end). My JS code is included at the end of the html and also these functions run on click when all the html is loaded so i don't get why it returns null. Also, querySelector() returns 'winner' and not the value 'Player X'. I read the links you provided and followed it.

EDIT #2

The DoGet() creates a template:

var tmp = HtmlService.createTemplateFromFile("page-bootstrap");

then i'll put all my variables in tmp.something and then use:

return tmp.evaluate();

So far i had no problem since i used it to built the html. But in this case i would need to pass the array/object with the list of selects to loop through (since it is known on the sheets the data comes from). But it doesn't seem to work with the html-side script.

Concretely if i pass:

tmp.listOfSelectstoLoopThrough 

then i just can't use it with the function invoked with the evenListener. That is why i then tried the QueryselectorAll that just doesn't return data correctly..I am a bit lost.

2 Answers

Besides getElementById there are many other methods to get HTML elements. I suggest you to start by learning about CSS selectors, Document.querySelector and Document.querySelectorAll. I.E. you might use document.querySelectorAll('select') to get all the HTMLElement.select's. This is very similar to document.getElementsByTagName('select').


Apparently you are stuck on how web apps might be developed in Google Apps Script.

  • The JavaScript on .gs is usually called "server-side" code
  • It's possible to have JavaScript on the "client-side", just include it in the HtmlOutput to be returned by the doGet function.
  • There are several ways to create a HtmlOutput. The most common way might be by using something as follows:

Code.gs

function doGet(e){
  return HtmlService.createHtmlOutputFromFile('index')
    .setTitle('Put here the title to be shown in the web browser tab')
    .addMetaTag('viewport', 'width=device-width, initial-scale=1');
}

index.html

<!DOCTYPE html>
<html>
  <head>
   <base target="_top">
   <style>
     // Put here the CSS
   </style>
  <head>
  <body>
   <!-- Put here the content: p, table, img, a, form, div etc. -->
   <script>
     /** Put here the client-side JavaScript code */
   </script>
  </body>

In the client-side JavaScript there are many options to get HTMLElements, i.e. Document.getElementsByTagName, Document.getElementsByClassName, Document.querySelector, Document.querySelectorAll. I prefer the last two.

Please review thoroughly HTML Service: Create and Serve HTML and Web Apps

Related

A simple dialog with template html

GS:

function getMyData() {
  const ss = SpreadsheetApp.getActive();
  const sh = ss.getSheetByName("Sheet0");
  return sh.getDataRange().getValues();
}

function launchmydialog() {
  SpreadsheetApp.getUi().showModelessDialog(HtmlService.createTemplateFromFile("ah1").evaluate(),"Templated HTML");
}

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>

Data:

COL1 COL2 COL3 COL4 COL5 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9 6 7 8 9 10 7 8 9 10 11 8 9 10 11 12 9 10 11 12 13 10 11 12 13 14

Dialog:

enter image description here

Related