Nicholas Saunders
Nicholas Saunders

Reputation: 764

How to count button clicks for a form in Google Apps Script with JavaScript?

Simplified the code as much as possible. How are the button clicks counted?

html:

<!DOCTYPE html>

<html>
<head>
  <base target="_top">
</head>
<body>
  <h1>Example File 3</h1>
  <form action="<?= url ?>" method="GET">
    <input type="text"  name="update" />
    <input type="submit" name="Submit" /><br>   <!--   count clicks   -->
    <span><?= update ?></span> 
    <span><?= count ?></span> 
  </form>
</body>
</html>

The Code.js file:

var clicks = 0;

function doGet(e) {

  Logger.log(JSON.stringify(e));
  var htmlOutput = HtmlService.createTemplateFromFile('ExampleFile3');
  if (!e.parameter['update']) {
    htmlOutput.update = '';
  }
  else {
    htmlOutput.update = 'updated value is: ' + e.parameter['update'];
    htmlOutput.count = 'count is: ' + clicks;
    clicks++
  }
  htmlOutput.clicks = clicks;
  htmlOutput.count = clicks;
  htmlOutput.url = getUrl();
  return htmlOutput.evaluate();
}





function getUrl() {
  var url = ScriptApp.getService().getUrl();
  return url;
}

the increment isn't increasing, that I see, and the output is squished together. Looking to count the number of form submissions.

Upvotes: 0

Views: 551

Answers (1)

Wicket
Wicket

Reputation: 38346

Here is a sample code for creating an oversimplified web app. It shows one way to count button clicks using JavaScript.

  1. The client-side code is added to the HtmlOutput object by using the HtmlService.HtmlOutput.append method.
  2. The global variable is set in the client-side instead of the server-side. Please bear in mind that reloading the web app will reset the global variable.
  3. In <form> uses the attribute onsubmit instead of the attributes action and method. This attribute calls a client-side JavaScript function that updates the client-side global variable and display it in <div>.
  4. Please note the use of event.preventDefault(). This is required when submitting html forms in Google Apps Script web apps because the form is hosted in a special Google service that cannot be modified by the Google Apps Script web app developers.
function doGet(e) {
  
  return HtmlService.createHtmlOutput()
  .append(`
  <form onsubmit="handleFormSubmit()">
  <input type="text" name="something"><br>
  <button type="submit">Submit</button>
  </form>
  <div>0</div>
  <script>
    var clicks = 0; 
    function handleFormSubmit(){ 
      event.preventDefault(); 
      document.querySelector('div').innerHTML = ++clicks;
    }
  </script>
  `);
}

Notes:

  1. The web browser console will show several warnings. You can ignore them
  2. The web browser console will show the following error:

Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'. at gas-hub.js:25:10

For the purpose of this oversimplified example, you can ignore it. To avoid this error, the HtmlOutput object should include a "full" HTML5 structure:

function doGet(e) {
  return HtmlService.createHtmlOutput()
  .append(`
  <!DOCTYPE html>
  <html>
  <head>
  </head>
  <body>
  <form onsubmit="handleFormSubmit()">
  <input type="text" name="something"><br>
  <button type="submit">Submit</button>
  </form>
  <div>0</div>
  <script>
  var clicks = 0; 
  function handleFormSubmit(){
    event.preventDefault(); 
    document.querySelector('div').innerHTML = ++clicks;
    }
  </script>
  </body>
  </html>
`)
.setTitle('Demo')
.addMetaTag('viewport', 'width=device-width, initial-scale=1');
}

To send <form> values to the server side, as well for other type of values, without reloading the web app, use google.script.run to call a server side function as is explained in https://developers.google.com/apps-script/guides/html/communication.

You also can use google.script.run to call server side functions on client-side event like clicking button. A very simple way is by using a button (<button> or <input type="button"> with an onclick attribute).

<button onclick="google.script.run.myFunction();">Run</button>

Also it's possible to use it with <form>'s onsubmit attribute and buttons of type submit but you have to include event.preventDefault() to avoid the page redirection cause the form submit action.

Please bear in mind that the use of event attributes is discouraged. Instead set the function to be called on the event by using HtmlElement.addEventListener whenever be possible.

To store a value persistently, do not use server-side global variables, instead consider to use the Properties Service among other options (see the related questions).


Here is another example, now using the "full" HTML5 structure, the Properties Service to persistently keep the number of button clicks. Instead of using the attribute onsubmit it uses the <form>attributes action and method but also use <base> to set the elements target to `"_top". This is required to properly reload the web app when the form is submitted.

A hidden <input> is used to pass a value to control when the click counter should be increased, this way we prevent that following the link (without the clicked=yes url parameter) increase the counter. Please note that no JavaScript is included on the client-side (HtmlOutput).

function doGet(e) {
  
  const props = PropertiesService.getScriptProperties();
  const name = "clicks";
  let clicks = Number(props.getProperty(name));

  if (e && e.parameter && e.parameter.clicked === 'yes') {
    props.setProperty(name, ++clicks);
  }
  
  return HtmlService.createHtmlOutput()
    .append(`
  <!DOCTYPE html>
  <html>
  <head>
  <base target="_top">
  </head>
  <body>
  <form action="${ScriptApp.getService().getUrl()}" method="GET">
  <input type="hidden" name="clicked" value="yes">
  <input type="text" name="something" value="default value"><br>
  <button type="submit">Submit</button>
  </form>
  <div>${clicks}</div>
  </body>
  </html>
`)
    .setTitle('Demo')
    .addMetaTag('viewport', 'width=device-width, initial-scale=1');
}


If you are on your way to create a production class web application, please be aware that the client-side code is "satinized" and there are some limitations about what can done compared to other platforms. Please study the references to learn the basics about this.

Related

References

Upvotes: 1

Related Questions