1

I have a Google Apps Script Webapp which serves a simple html form. My Apps Script function processData(formdata) receives the form data as a Blob as described here . After I process the data I want to return another simple html page to the user. I tried the naive approach like this:

function processData(formdata){
  // do something with the data
  process(formdata);
    return HtmlService
    .createHtmlOutputFromFile('html/final')
   }

However the page is always blank after the user submits the data. How can I give the user a proper response?

This is my second approach as suggested by @Cooper. I added it in the html and used the withSuccessHandler but the page remains white after submitting:

<!DOCTYPE html>
<html>
 <head>
<link
  rel="stylesheet"
  href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"
/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script>
function updateUrl() {
var div = document.getElementById('msg');
div.innerHTML = "<h1>Thanks for the submission</h1><p>You should be hearing from us soon</p>";
}
function handleFormSubmit(formObject) {
  google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
    }
</script>
</head>

<body>
<div id="msg"></div>
<div class="container">
  <h1>FORM insert data</h1>
  <form id="myForm" onsubmit="handleFormSubmit(this)">
    <div class="form-group">
      <label for="nameInput">enter your name</label><br />

... and so on

[Edit]

I now added the preventFormSubmit function as described in the example by Cooper. Now after submitting the message is shown. However the complete form stays on the page where I would prefere to only have the message be shown.

That said it seems that it is not easy to just show another html. Does that mean it is not possible to have a more complex webapp which require more html sides with Google Apps Script?

zlZimon
  • 2,334
  • 4
  • 21
  • 51
  • I would probably do it on the same page with another div and use google.script.run to connect to the server if necessary. – Cooper Jun 29 '20 at 17:21
  • how will this look like? I dont have any experience with html – zlZimon Jun 29 '20 at 17:24
  • What do want it look like? – Cooper Jun 29 '20 at 17:25
  • You can try some site like JS Fiddle to play around with different options if you want. – Cooper Jun 29 '20 at 17:26
  • Perhaps you put something like this in the html `
    ` along with `` and then with Javascript to load message into the div with `document.getElementById('msg').innerHTML="

    Thanks for the submission

    You should be hearing from us soon

    "` and then you can change the style with `$('#msg').css('display','block');` of course you'll have to include JQuery on your page as well. Fortunate Google host all of those types of things for people to use. But you can also do it with straight javascript.
    – Cooper Jun 29 '20 at 17:31
  • hm I am really new to html. When I put this in the html I can see for a short time the "thank you for submission" and then the page is white again.. I am not sure where to put it – zlZimon Jun 29 '20 at 17:45
  • Inside the body of your html page. Please share the html of your first page – Cooper Jun 29 '20 at 17:47
  • your html doesn't seem complete – Cooper Jun 29 '20 at 18:15
  • I did not post the complete form since it is very long, but I do not think that this is the issue here – zlZimon Jun 29 '20 at 18:20
  • What is the issue? – Cooper Jun 29 '20 at 18:25
  • Are using still running this function? `function processData(formdata){ // do something with the data process(formdata); return HtmlService .createHtmlOutputFromFile('html/final') }` – Cooper Jun 29 '20 at 18:29
  • yes, but without the `return HtmlService.createHtmlOutputFromFile('html/final')` – zlZimon Jun 29 '20 at 18:30
  • Then what is clearing the div? – Cooper Jun 29 '20 at 18:55
  • well that is my question... how can I find that out? – zlZimon Jun 29 '20 at 18:57
  • I even can not get the simples version working. Just a button which calls the server function with a callback which displays a message... – zlZimon Jun 29 '20 at 19:14
  • I can't debug it without a [mcve] – Cooper Jun 29 '20 at 19:20
  • well I am looking for the minimal reproducible example so I can try it out. – zlZimon Jun 29 '20 at 19:27
  • Do you see any content when you run `console.log(HtmlService .createHtmlOutputFromFile('html/final').getContent())` ? – Aerials Jun 30 '20 at 11:18
  • yes I do see the content. – zlZimon Jun 30 '20 at 11:44
  • Have a look at [this answer](https://stackoverflow.com/a/62447055/4243927) for some ideas. – Aerials Jun 30 '20 at 12:46

1 Answers1

2

You were right there were some problems with that example that are a result of the recent switch to the v8 engine. I didn't use the doGet(), I ran it as a dialog instead. But here's a working version.

Code.gs:

function runADialog() {
  SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutputFromFile('ah1'), 'MCVE')
}

function processForm(data) {
  SpreadsheetApp.getActive().toast('processForm');
  console.log('processForm');
  var file = Utilities.newBlob(data.bytes,data.mimeType,data.name)
  var driveFile = DriveApp.createFile(file);
  return driveFile.getUrl();
}

ah1.html:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function preventFormSubmit() {
        var forms=document.querySelectorAll('form');
        for (var i=0;i<forms.length;i++) {
          forms[i].addEventListener('submit', function(event) {
            event.preventDefault();
          });
        }
      }
      window.addEventListener('load', preventFormSubmit);
      
      function handleFormSubmit(formObject) {
        console.log('handleFormSubmit');
        const file = formObject.myFile.files[0];
        console.log(file);
        const fr = new FileReader();
        fr.onload = function(e) {
          const obj = {name:file.name,mimeType: file.type,bytes:[...new Int8Array(e.target.result)]};
          google.script.run.withSuccessHandler(updateUrl).processForm(obj);
        }
        fr.readAsArrayBuffer(file);
      }
      
      function updateUrl(url) {
        var div = document.getElementById('output');
        div.innerHTML = '<a href="' + url + '">Got it!</a>';
      }
    </script>
  </head>
  <body>
    <form id="myForm" onsubmit="handleFormSubmit(this)">
      <input name="myFile" type="file" />
      <input type="submit" value="Submit" />
    </form>
    <div id="output"></div>
 </body>
</html>

Most of the modifications for this example came from Tanaike

This is what the dialog looks like when it works:

enter image description here

and you can click on that link to go to the file.

And this is a version using that other div I was talking about.

Code.gs:

function runADialog() {

SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutputFromFile('ah1'), 'MCVE')
}

function processForm(data) {
  SpreadsheetApp.getActive().toast('processForm');
  console.log('processForm');
  var file = Utilities.newBlob(data.bytes,data.mimeType,data.name)
  var driveFile = DriveApp.createFile(file);
  return driveFile.getUrl();
}

function getServerInfo() {
  //getting nothing for now
  return{msg:"This is the default message."};
}

ah1.html:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <script>
      function preventFormSubmit() {
        var forms=document.querySelectorAll('form');
        for (var i=0;i<forms.length;i++) {
          forms[i].addEventListener('submit', function(event) {
            event.preventDefault();
          });
        }
      }
      window.addEventListener('load', preventFormSubmit);
      
      function handleFormSubmit(formObject) {
        console.log('handleFormSubmit');
        const file = formObject.myFile.files[0];
        console.log(file);
        const fr = new FileReader();
        fr.onload = function(e) {
          const obj = {name:file.name,mimeType: file.type,bytes:[...new Int8Array(e.target.result)]};
          google.script.run
          .withSuccessHandler(function(url){ 
             updateUrl(url);
             google.script.run
             .withSuccessHandler(function(obj){
               document.getElementById("msg").innerHTML="<h1> Thanks for Submitting that Image<h1><p>" + obj.msg + "</p>";
               
             })
             .getServerInfo();
           })
          .processForm(obj);
        }
        fr.readAsArrayBuffer(file);
      }
      
      function updateUrl(url) {
        var div = document.getElementById('output');
        div.innerHTML = '<a href="' + url + '">Got it!</a>';
      }
    </script>
  </head>
  
  <body>
    <form id="myForm" onsubmit="handleFormSubmit(this)">
      <input name="myFile" type="file" />
      <input type="submit" value="Submit" />
    </form>
    <div id="output"></div>
    <div id="msg"></div>
 </body>
</html>

And this is what the new dialog looks like:

enter image description here

Cooper
  • 59,616
  • 6
  • 23
  • 54
  • this is interesting, when I test it similar to your example it works, so the question is why doesnt it in my version – zlZimon Jun 30 '20 at 09:35
  • I did change the way the file is read when you upload it if you’ll notice there’s a file reader in there – Cooper Jun 30 '20 at 13:28
  • I am a bit confused when you said that you do not use a doGet() method. But afaik you need one right? And where does the runADialoge() gets called? – zlZimon Jul 02 '20 at 08:53