How to read which checkboxes have been selected (HTML form to Google Sheet)

Viewed 66

I'm using Praveen Kumar's script to send HTML form data to a Google Sheet (https://www.youtube.com/watch?v=39XZBKmcMfE&ab_channel=CodingMSTR).

It works great, but the only example code was for simple text inputs. How might this work for a checkbox input?

The form:

enter image description here

Currently if any of the checkboxes are selected, the value "on" is passed, otherwise blank but I need to see in the Google Sheet which of the checkboxes have been selected:

enter image description here

The HTML form:

<form method="post" name="google-sheet" autocomplete="off">
    <div class="mb-3"><input class="form-control" type="text" id="form-FName" name="FName" placeholder="Your First Name"></div>
    <div class="mb-3"><input class="form-control" type="text" id="form-LName" name="LName" placeholder="Your Surname (optional)"></div>
    <div class="mb-3"><input class="form-control" type="email" id="form-Email" name="Email" placeholder="Your email address"></div>
    <div class="mb-3"><input class="form-control" type="tel" id="form-Phone" name="Phone" placeholder="Your phone number (preferably mobile)"></div>
    <div>
        <fieldset>
            <legend>How can I contact you?</legend>
            <div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-1" name="HowContact"><label class="form-check-label" for="formCheck-1">Text</label></div>
            <div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-3" name="HowContact"><label class="form-check-label" for="formCheck-3">Phone - daytime</label></div>
            <div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-2" name="HowContact"><label class="form-check-label" for="formCheck-2">Phone - early evening</label></div>
        </fieldset>
    </div>
    <div></div>
    <div><button class="btn btn-primary d-block w-100" type="submit" name="submit" style="margin-top: 18px;">Send </button></div>
    <div></div>
</form>

The Javascript in the HTML page:

<script>
const scriptURL = 'https://script.google.com/macros/s/AKfycbyDwOEAmWRazqBBpSEoPxp0gj3vZQxXVwRf7BGnvep5wRD2_xdI9efKjeIXPU4XTzftnQ/exec'
const form = document.forms['google-sheet']

form.addEventListener('submit', e => {
  e.preventDefault()
  fetch(scriptURL, { method: 'POST', body: new FormData(form)})
    .then(response => alert("Thanks for Contacting us..! We Will Contact You Soon..."))
    .catch(error => console.error('Error!', error.message))
})
</script>

The Appscript doGet function:

  function doPost(e) {
  var lock = LockService.getScriptLock()
  lock.tryLock(10000)

  try {
    var doc = SpreadsheetApp.openById(scriptProp.getProperty('key'))
    var sheet = doc.getSheetByName(sheetName)

    var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]
    var nextRow = sheet.getLastRow() + 1

    var newRow = headers.map(function (header) {
      return header === 'timestamp' ? new Date() : e.parameter[header]
    })

    sheet.getRange(nextRow, 1, 1, newRow.length).setValues([newRow])

    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'success', 'row': nextRow }))
      .setMimeType(ContentService.MimeType.JSON)
  }

  catch (e) {
    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'error', 'error': e }))
      .setMimeType(ContentService.MimeType.JSON)
  }

  finally {
    lock.releaseLock()
  }
}

Any help much appreciated.

UPDATE: After Tanaike's suggestion, the sheet receives a value but when selecting more than one item, only the first one is sent to the sheet: ![enter image description here enter image description here

The updated form:

<fieldset>
     <legend>How can I contact you?</legend>
     <div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-1" name="HowContact" value="text"><label class="form-check-label" for="formCheck-1">Text</label></div>
     <div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-3" name="HowContact" value="ph-day"><label class="form-check-label" for="formCheck-3">Phone - daytime</label></div>
     <div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-2" name="HowContact" value="ph-eve"><label class="form-check-label" for="formCheck-2">Phone - early evening</label></div>
 </fieldset>

The updated Appscript:

try {
    var doc = SpreadsheetApp.openById(scriptProp.getProperty('key'))
    var sheet = doc.getSheetByName(sheetName)

    var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]
    var nextRow = sheet.getLastRow() + 1

    var newRow = headers.map(function (header) {
    return header === 'timestamp' ? new Date() : e.parameters[header].join(",");
    });

    // var newRow = headers.map(function (header) {
    //   return header === 'timestamp' ? new Date() : e.parameter[header]
    // })

    sheet.getRange(nextRow, 1, 1, newRow.length).setValues([newRow])

    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'success', 'row': nextRow }))
      .setMimeType(ContentService.MimeType.JSON)
  }
2 Answers

When I saw your HTML, it seems that value is not set in the input tag. So, as a simple modification, how about the following modification?

HTML side:

Please modify your HTML as follows.

From:

<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-1" name="HowContact"><label class="form-check-label" for="formCheck-1">Text</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-3" name="HowContact"><label class="form-check-label" for="formCheck-3">Phone - daytime</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-2" name="HowContact"><label class="form-check-label" for="formCheck-2">Phone - early evening</label></div>

To:

<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-1" name="HowContact" value="Text"><label class="form-check-label" for="formCheck-1">Text</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-3" name="HowContact" value="Phone - daytime"><label class="form-check-label" for="formCheck-3">Phone - daytime</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-2" name="HowContact" value="Phone - early evening"><label class="form-check-label" for="formCheck-2">Phone - early evening</label></div>

Google Apps Script side:

In order to use the multiple responses, please modify Google Apps Script as follows.

From:

var newRow = headers.map(function (header) {
   return header === 'timestamp' ? new Date() : e.parameter[header]
 })

To:

var newRow = headers.map(function (header) {
  return header === 'timestamp' ? new Date() : e.parameters[header].join(",");
});

Note:

Added:

If your HTML and Javascript are put in the same Google Apps Script project of doPost, I thought that the following modification might be able to be used.

HTML & Javascript: index.html

<form method="post" name="google-sheet" autocomplete="off" id="form">
    <div class="mb-3"><input class="form-control" type="text" id="form-FName" name="FName" placeholder="Your First Name"></div>
    <div class="mb-3"><input class="form-control" type="text" id="form-LName" name="LName" placeholder="Your Surname (optional)"></div>
    <div class="mb-3"><input class="form-control" type="email" id="form-Email" name="Email" placeholder="Your email address"></div>
    <div class="mb-3"><input class="form-control" type="tel" id="form-Phone" name="Phone" placeholder="Your phone number (preferably mobile)"></div>
    <div>
        <fieldset>
            <legend>How can I contact you?</legend>
<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-1" name="HowContact" value="Text"><label class="form-check-label" for="formCheck-1">Text</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-3" name="HowContact" value="Phone - daytime"><label class="form-check-label" for="formCheck-3">Phone - daytime</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" id="formCheck-2" name="HowContact" value="Phone - early evening"><label class="form-check-label" for="formCheck-2">Phone - early evening</label></div>
        </fieldset>
    </div>
    <div></div>
    <div><button class="btn btn-primary d-block w-100" type="submit" name="submit" style="margin-top: 18px;">Send </button></div>
    <div></div>
</form>

<script>
const form = document.forms['google-sheet'];
form.addEventListener('submit', e => {
  e.preventDefault()
  google.script.run.withSuccessHandler(res => {
    console.log(res);
    alert("Thanks for Contacting us..! We Will Contact You Soon...");
  }).saveData(form);
})
</script>

Google Apps Script: Code.gs

// If you are using a dialog and sidebar, please use it. If you are using Web Apps, you can use this.
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index"); // Please set the html filename.
}

function saveData(e) {
  var sheetName = "###"; // Please set sheet name.
  var scriptProp = PropertiesService.getScriptProperties();

  var lock = LockService.getScriptLock();
  lock.tryLock(10000);
  try {
    var doc = SpreadsheetApp.openById(scriptProp.getProperty('key'));
    var sheet = doc.getSheetByName(sheetName);
    var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    var nextRow = sheet.getLastRow() + 1
    var newRow = headers.map(function (header) {
      return header === 'timestamp' ? new Date() : Array.isArray(e[header]) ? e[header].join(",") : e[header];
    })
    sheet.getRange(nextRow, 1, 1, newRow.length).setValues([newRow])
    return { 'result': 'success', 'row': nextRow };
  } catch (e) {
    return { 'result': 'error', 'error': e };
  } finally {
    lock.releaseLock()
  }
}
  • In this modification, even when the Web Apps is deployed as a new version and the URL of Web Apps is changed, this script works because the Web Apps URL is not used for fetch API.

  • When you use this script, please copy and paste the above scripts to a Google Apps Script project, and please deploy Web Apps. When you access the created Web Apps URL, the HTML is opened. And, when you submit the HTML form, the values are put on the Spreadsheet.

Here's an example that create checkboxes on the fly and also a couple static ones and then reads them. It's actually a complete solution for backing up your script functions into ascii files.

HTML:

<!DOCTYPE html>
<html>

<head>
    <base target="_top">
    <style>
        input {margin: 2px 5px 2px 0;}
    #btn3,#btn4{display:none}
    </style>
</head>

<body>
    <form>
        <input type="text" id="scr" name="script" size="60" placeholder="Enter Apps Script Id" onchange="getFileNames();" />
        <br /><input type="text" id="fldr" name="folder" size="60" placeholder="Enter Backup Folder Id" />
        <div id="shts"></div>
    <br /><input type="button" value="0" onClick="unCheckAll();" size="15" id="btn3" />
    <input type="button" value="1" onClick="checkAll();"size="15" id="btn4"/>
        <br /><input type="checkbox" id="files" name="saveFiles" checked /><label for="files">Save Files</label>
        <br /><input type="checkbox" id="json" name="saveJson" checked /><label for="json">Save JSON</label>
        <br /><input type="button" value="Submit" onClick="backupFiles();" />
    </form>
        <script>
      function getFileNames() {
        const scriptid = document.getElementById("scr").value;
        google.script.run
        .withSuccessHandler((names) => {
          document.getElementById('btn3').style.display = "inline";
          document.getElementById('btn4').style.display = "inline";
          names.forEach((name,i) => {
           let br = document.createElement("br"); 
           let cb = document.createElement("input");
           cb.type = "checkbox";
           cb.id = `cb${i}`;
           cb.name = `cb${i}`;
           cb.className = "cbx";
           cb.value = `${name}`;
           cb.checked = true;
           let lbl = document.createElement("label");
           lbl.htmlFor = `cb${i}`;
           lbl.appendChild(document.createTextNode(`${name}`));
           document.getElementById("shts").appendChild(cb);
           document.getElementById("shts").appendChild(lbl);
           document.getElementById("shts").appendChild(br);
          });
        })
        .getAllFileNames({scriptId:scriptid}); 
      }
      function unCheckAll() {
        let btns = document.getElementsByClassName("cbx");
        console.log(btns.length);
        for(let i =0;i<btns.length;i++) {
          btns[i].checked = false;
        }
      }
      function checkAll() {
        let btns = document.getElementsByClassName("cbx");
        console.log(btns.length)
        for(let i = 0;i<btns.length;i++) {
          btns[i].checked = true; 
        }
      }
      function backupFiles() {
        console.log('backupFiles');
        sObj = {};
        sObj.script = document.getElementById('scr').value;
        sObj.folder = document.getElementById('fldr').value;
        sObj.saveJson = document.getElementById('json').checked?'on':'';
        sObj.saveFiles = document.getElementById('files').checked?'on':'';
        sObj.selected = [];
        console.log("1");
        const cbs = document.getElementsByClassName("cbx");
        let selected = [];
        for(let i = 0;i<cbs.length; i++) {
          let cb = cbs[i];
          if(cb.checked) {
            sObj.selected.push(cb.value)
          } 
        }
        console.log("2");
        google.script.run
        .withSuccessHandler(function(obj){google.script.host.close();})
        .scriptFilesBackup(sObj);
        console.log(JSON.stringify(sObj));
      }
        </script>
</body>

</html>

GS:

function saveScriptBackupsDialog() {
  SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutputFromFile('backupscripts1'), 'Script Files Backup Dialog');
}

function scriptFilesBackup(obj) {
  console.log(JSON.stringify(obj));
  const scriptId = obj.script.trim();
  const folderId = obj.folder.trim();
  const saveJson = obj.saveJson;
  const saveFiles = obj.saveFiles;
  const fA = obj.selected;
 
  if (scriptId && folderId) {
    const base = "https://script.googleapis.com/v1/projects/"
    const url1 = base + scriptId + "/content";
    const url2 = base + scriptId;
    const options = { "method": "get", "muteHttpExceptions": true, "headers": { "Authorization": "Bearer " + ScriptApp.getOAuthToken() } };
    const res1 = UrlFetchApp.fetch(url1, options);
    const data1 = JSON.parse(res1.getContentText());
    const files = data1.files;
    const folder = DriveApp.getFolderById(folderId);

    const res2 = UrlFetchApp.fetch(url2, options);
    const data2 = JSON.parse(res2.getContentText());
    let dts = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyyMMdd-HH:mm:ss");
    let subFolderName = Utilities.formatString('%s-%s', data2.title, dts);
    let subFolder = folder.createFolder(subFolderName);
    if (saveFiles) {
      files.forEach(file => {
        if (file.source && file.name) {
          let ext = (file.type == "HTML") ? ".html" : ".gs";
          if (~fA.indexOf(file.name)) {
            subFolder.createFile(file.name + ext, file.source, MimeType.PLAIN_TEXT)
          }
        }
      });
    }
    if (saveJson) {
      subFolder.createFile(subFolderName + '_JSON', res1, MimeType.PLAIN_TEXT)
    }
  }
  return { "message": "Process Complete" };
}

And here's what the Dialog Looks like I use this code to save my functions in ascii files.

enter image description here

Related