Search code examples
htmlgoogle-apps-scriptgoogle-sheetsweb-applicationsform-submit

Uploading An Image With Google Apps Script To A GSheet - Passing Values To And From HTML Service Modal Dialog


I'm trying to trigger a file upload off of a Google Sheet, taking the uploaded file, add it to a Google Drive folder, and then return the URL of the uploaded file and place it in a cell on the Sheet. I'm currently triggering the file upload by using a checkbox. Once you set the checkbox to TRUE, it'll pop up a dialog box with a file upload input field. This is triggered by an installed onEdit function. Also, info on the row in the sheet will be used to name the newly uploaded file. This info will be input manually on the sheet.

I get to the showModalDialog line, and the dialog box comes up just fine, but I can't figure out how to pass variables from the original function to the HTML service and then back again (with the file) to upload to Drive, set the name, and put the URL back on the sheet.

Here's the first function in Code.gs, receiving values from the onEdit function:

function addFile(ss,ui,row,total) { \\Triggered if edited cell is in column 25 & value is TRUE
  Logger.log('add file function');
  var name = ss.getRange(row,1).getDisplayValue();
  var date = ss.getRange(row,3).getDisplayValue();
  var filename = 'Row ' + row + ' - ' + name + ' - ' + date + ' - ' + total;
  var htmlTemp = HtmlService.createTemplateFromFile('Index');
  htmlTemp.fName = filename;
  htmlTemp.position = row;
  var html = htmlTemp.evaluate().setHeight(76).setWidth(415);
  ui.showModalDialog(html, 'Upload');
  Logger.log('end of add file function');
}

And here's what's in Index.html:

<!DOCTYPE html>
<html>
  <head>
    <base target="_center">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
  </head>
  <body>
  <form>
    Please upload image below.<br /><br />
    <input type="file" name="upload" id="file" accept="image/*,.pdf" />
    <input type="button" value="Submit" class="action" onclick="formData(this.parentNode)" />
    <input type="button" value="Close" onclick="google.script.host.close()" />
  </form>
  <script>
    function formData(obj){
      var newFileName = <? fName ?>;
      var rowNum = <? position ?>;

  google.script.run.withSuccessHandler(closeIt).upload(obj,newFileName,rowNum);
    }
    function closeIt(e){
      console.log(e);
      google.script.host.close();
    };
  </script>
</body>
</html>

And here's the return function on Code.gs:

function upload(obj,newFileName,rowNum) {
  Logger.log('upload function');
  var upFile = DriveApp.getFolderById('[folderid]').createFile(obj).setName(newFileName);
  var fileUrl = upFile.getUrl();
  Logger.log(fileUrl);
  var urlCell = SpreadsheetApp.getSheetByName('sheet name').getRange(rowNum,26);
  urlCell.setValue('=HYPERLINK("' + fileUrl + '","View image")');
}

Running this code, the dialog box comes up just fine, and I'm able to select a file for upload. However, clicking the Submit button does nothing, and the box stays up until I X it out or hit the Cancel button. The logs only get so far as 'end of add file function' and never gets to the upload function. Should the google.script.run.withSuccessHandler line close the dialog box, or is something else needed to confirm / get the file and close the box?

I've been searching online and have found a number of posts relating to this, but none seem to address this specific issue. This is also pretty much a frankenstein of code I've cobbled together from those posts, so it's possible there's just something that doesn't belong in there and if that is the case I do apologize. Any help would be appreciated; thanks!

[Edit: the submit button wasn't opening a separate tab because I was using <input type="button"> instead of <button>.]


Solution

  • According to the documentation [1] in the “Parameters and return values” part, if you’re going to send the form object as a parameter “it must be the function’s only parameter”. So you should send the parameters inside the form, using inputs of types “hidden” or “text”, then, from code.gs you can retrieve the input data of the Form object. Another thing stated in the documentation [1] in the “form” section, is that you need to disable the default submit action with preventFormSubmit function.

    Another problem is that the correct way of printing the variables passed to the template are using <?= ?> instead of <? ?> which works to execute code but not to print variables. [2]

    Your “addFile” function is all right. Below is the code i've tested on my environment and I was able to upload an image successfully and print the url in the sheet.

    Index.html:

    <!DOCTYPE html>
    <html>
      <head>
        <base target="_center">
        <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
            <script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
      </head>
      <body>
      <form id="myForm">
          Please upload image below.<br /><br />
        <input type="hidden" name="fname" id="fname" value="<?= fName ?>"/>
        <input type="hidden" name="position" id="position" value="<?= position ?>"/>
        <input type="file" name="file" id="file" accept="image/jpeg,.pdf" />
        <input type="button" value="Submit" class="action" onclick="formData(this.parentNode)" />
        <input type="button" value="Close" onclick="google.script.host.close()" />
      </form>
      <script>
      //Disable the default submit action  using “func1”
       window.onload=func1;
       function func1() {
          document.getElementById('myForm').addEventListener('submit', function(event) {
                event.preventDefault();
              });  
       }
    
       function formData(obj){
           google.script.run.withSuccessHandler(closeIt).upload(obj);
       }   
    
      function closeIt(e){
          console.log(e);
          google.script.host.close();
      };   
    
        </script>
    </body>
    </html>
    

    Code.gs (upload function):

    function upload(obj) {
      //Retrieve the input data of the Form object.
      var newFileName = obj.fname;
      var rowNum = obj.position;
      var blob = obj.file;
    
      var upFile = DriveApp.getFolderById('[folderid]').createFile(blob).setName(newFileName);
      var fileUrl = upFile.getUrl();
    
      var urlCell = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getRange(rowNum,5);
      urlCell.setValue('=HYPERLINK("' + fileUrl + '","View image")');
    
    }
    

    [1] https://developers.google.com/apps-script/guides/html/communication

    [2] https://developers.google.com/apps-script/guides/html/templates