# Web App Development with Google Apps Script/Printable version

The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/Web_App_Development_with_Google_Apps_Script

# Why GAS
## Javascript everywhere

Using Google Apps Script is nice because it's javascript on both the front and backend. Normally we expect javascript and html on the front end (meaning how our users will interact with our tool) but often you have to learn and use a different language on the backend, where you interact with your data and figure out what you need to show your users. Backend software examples include PHP, Ruby, and interesting flavors of javascript like node.js. With Google Apps Script you do the same sort of programming everywhere.

## Web editing

With Google Apps Script can do all your editing in a browser. Your scripts are either tied to a spreadsheet or they're stand alone scripts in your Google Drive. Either way you launch the editor through drive and you get a decent, if not fantastic, development environment that you can use on a Chromebook.
Most other web development approaches encourage you to code locally on your own machine and then push your code to the server that hosts the web page. Many people use GitHub to help facilitate the pushing, but getting a nice coding environment on your local computer can be a hassle so it's nice with Google Apps Script that you can just grab any web-connected computer to do your work.

## Spreadsheets as database

What I'll be talking about mostly in this book is using simple Google Sheets to store all your data. I've done a lot of web app development using a mysql-type backend database, and I really appreciate the speed and utilities those provide. But they're a pain to set up and they're a pain to interact with separately from your web application. Google Sheets, on the other hand, are quite easy to set up and have a great user interface already for you to go look at and mess with your data.

## User authentication

Assuming you're in a situation where you're programming in a Google environment (like a Google school like mine where all emails are just rebranded gmail accounts) there's a very simple command to determine the user's email address and from there you can decide what you want them to have access to.

## References

* ↑ test footnote

# Hello world
## Create a new script

You can create a new Google Apps Script the same way you'd create a new Google Doc or Google Sheet: Using the "New" button scroll down to "Google Apps Script" under "more". That will open a new script window with a simple test function in it:
If you write some code in that function you can run the function with the buttons at the top. Typically to see any results you'd have to use Logger.log(some variable here).
If you put javascript code outside of a function it will also execute whenever you run any of the functions. Keep that in mind as we develop web apps. Anything outside of a function will run every time the page is accessed or you run a server function with an ajax call.

## Set up doGet

To actually have a web app you have to do two things:
* Make sure you have a doGet function in your script, and
* Publish your site
Almost always you're going to want to write your html in a separate file (still housed in the overall script google entity) and then have the doGet function call it and return it. Assuming you've created an html file called "main.html" you would call it like this:
You would then access the page by going under to the "Publish->Deploy As Web App..." menu item. Once it's published you can access the url from that same menu item.

# Templated html
## Putting variables into html pages

As noted in the Hello World chapter, you can have a basis of an html page by creating one and using it as a template. Normally you'd want to tailor that page with information from the server (ie usually one of your Google Sheets). You can use the various templating tools to do that.
You send a variable to the template with t.coolvariable="whatever" and you put it in the html document with <?= coolvariable ?>.

### doGet code

from "code.gs":

### html code

From "main.html":

### Web result

hi there

## More complex variables

If you want to send something more complex than a string, there are a few options:
* <?!= somevariable ?> will print things with special characters in it. This is useful, for example, for strings that contain html tags in them.
* var pagevariable = <?!= JSON.stringify(somevariable) ?>; inside a <script> tag on the html page will then let the local javascript deal with it as a global variable.
* If you have a bunch of variables that you want to have as global variables in the html document:
** t.globals={"var1":var1, "var2":var2}; in the code.gs doc
** var globals = <?!= JSON.stringify(globals) ?>; Object.keys(globals).forEach(key=>window[key]=globals[key]);
** this works because the window object is the global so-called "namespace" for the html document. That just means it's where global variables sit.

## Passing functions

The Google Apps Script editor doesn't work very well when checking for syntax errors in javascript code that's embedded in an html file. Here's a trick to let you write javascript functions in the server code spaces (which lets them be syntax checked) and then pass them to the client. The trick is to use function expressions.
In the server side you'd do this:
and in the client html file you'd do this:

# Data from spreadsheets
## Structure overview

Google Apps Script lets you have access to Google Sheets. You do it by making a connection first to the Google Drive Sheet. There are two ways to do this:
* ss = SpreadsheetApp.openById(id) where "id" is the typical google drive id (the long string of characters usually right after "https://docs.google.com/spreadsheets/d/" in the url of a sheet).
* ss = SpreadsheetApp.getActive if your script is tied to a Spreadsheet (see below)
Once you have the spreadsheet variable (ss in those examples), you can access the various sheets (some people call them tabs) in that doc and any data inside them.

## Script tied to spreadsheet

If you want you can tie your script to a spreadsheet. Then the script doesn't live as a stand-alone google drive file but rather is stored with the Google Sheet. You can access the script by going to Tools->Script editor. It'll then look just like any other script. Oddly it still needs a name, but it'll prompt you to name it when you try to run something.
If you have it connected to a spreadsheet you definitely want to use the second approach mentioned above. Interestingly, the first method always works. There are some subtleties to access control when you tie it to a spreadsheet. Personally I do this a lot because if I want to edit the script I usually want access to the data at the same time. This way they're all stored together.

## Read the data from a spreadsheet

If you have the spreadsheet variable, let's say you wanted to get all the data in a sheet called "Sheet1", which is the default name for the first sheet in a brand new Google Sheet document. You'd do that like this:
This usually takes a second or two, even for large (500+ row) spreadsheets. Once you have the data you can do all sorts of things. Let's say you have a spreadsheet that looks like this:
| Name | email | age |
| Andy | andy@myschool.edu | 48 |
| Ash | ash@myschool.edu | 20 |
| Char | char@myschool.edu | 18 |
Assuming it's in "Sheet1", you could augment the above code to create an html list to be displayed on the main.html page like this:

# Responding to users
## JavaScript for user interfacing

This book is all about leveraging your google account to make data-driven web applications. It will not cover how to create your user interfaces very much. In this section I'll just give some simple examples, some of which don't even follow best practices but are quick and dirty approaches you can try for some proof-of-principle approaches.

### User clicked something

Nearly any element in html can have an onClick function added to it. This includes:
* buttons
* text (most easily done with span tags)
* List items
onClick is not usually considered the proper way to do things, but if you're sure it's the only thing you want that element to do when the user is interacting it can work for you.
Here's some simple html/javascript code that would open an alert saying "you clicked me" when the user presses a button:

### Update some text on the page

Alerts are annoying, don't use them. Instead have the user's actions change something on the page. Here's an update to the last example where now pressing the button puts some text on the page just below the button:
If you plan to make lots of changes to the text inside that div, you might instead want to make a global variable like this:

## Footnotes

* ↑ The proper method is to do is element.addEventListener('click', function { /* do stuff here*/ }, false);
* ↑ Note that you can't do var emptyatfirst=document.getElementById('emptyatfirst').innerHTML; even if you thought that would save you some typing later.

# Data to spreadsheets
## Basic gist

If you collect some data from a user (maybe from an input tag or something) that you want to save to your spreadsheet, you have to send it to the server-side, even though you're using the client-side for all the user interface.
The steps are:
* Grab data from the html element that the user adjusts
* Possibly massage the data a little (trim any trailing or leading spaces, for example)
* use the magic sauce: google.script.run!
* Deal with any response from the server

## Get data from input

Here's a simply way to get the data from a textarea element when the user hits "send":

## Sending to the server

Once you have some information to send to the server, you have to somehow get it there. That's where the magical google.script.run comes in!
Basically you can send some data to the server by calling a function **on the server** that can do stuff there and then return some response.
Here's an example that builds on the one from above. We grab whatever the user enters and we add it to a sheet on the (attached) spreadsheet:
Meanwhile on the server (so in code.gs instead of javascript added to main.html):

## Hearing back from the server

Often you'll want to hear something back from the server. That's what withSuccessHandler is all about. Basically you tell javascript both the server function you want to run **and** the client-side function that should run when something comes back. The syntax is weird, but it works.
Here's an example that augments the last one letting the user know the data has been saved in the spreadsheet:
Meanwhile on the server (so in code.gs instead of javascript added to main.html):

# authentication
## Why authenticate?

Quite often you'll want to protect your web app to ensure only certain people can use it. When you publish your page you have a few options in two major categories:
* Who has access (who can get to the page)?
** Just you
** Anyone in your domain (hamline.edu for me)
** Anyone
* Which account is being used to run the script?
** Yours (even when other people access)
** Theirs (won't work with "anyone" above)
Those are useful but they're a little coarse grained. Sometimes you'll want only certain people to either have access and/or be able to do certain things.

## Who is accessing?

For this section we'll assume you've set "who has access" to "anyone in your domain." If you do then you can determine who is accessing like this:

## Are they allowed?

Once you have the user's email, you can check it against a sheet in your spreadsheet that has allowed users and perhaps other information about them, like what things they're allowed to do. Let's assume you have a sheet that looks like this:
"my users" sheet :
| email | name | role |
| arundquist@hamline.edu | Andy Rundquist | admin |
| test1@hamline.edu | Testy McTesterson | student |
| test2@hamline.edu | Tess Tesserson | faculty |
Then we could authenticate the user like this:

# syntax
## ajax

Ajax stands for "Asynchronous JavaScript and XML" and it's the ability for a web page to communicate with a server without reloading the page.
To do it in Google Apps Script you use the google.script.run tool. This command, which has several varieties, runs JavaScript code on Google's servers and potentially returns information back to your web page.
For example, if you wanted to send a name to a server, then check if it's in your spreadsheet and return true if it is and false if it's not that you can set to a variable, you'd do this:
First the server side code:
Now the portion of "main.html" with the JavaScript you'd be doing
The flow is: main javascript -> function on server -> returns data to your SuccessHandler -> your successHandler does something.

## Array numbering

A simple (and quite forgivable) mistake is to be off by a row or column when moving data back and forth between google sheets and javascript is the array numbering issuer. Javascript starts numbering at zero while google sheets starts numbering at 1. Here's an example:

## filter

If you need to filter an array, use, um, filter:
As with find, forEach, and map you can also use the index if you want:

## find

If you need to find an element in an array, find is super helpful:

## forEach

If you need to loop through an array and do something with every element, you could of course do this:
But you could also do this, which I think reads better:
Note that you never have to initialize the "i" variable this way.
If you need to access the item (fruit) and the location (index) that it's in the array:
Note the parentheses around fruit, i that makes that magic work. Also note that you still don't have to declare the i variable!

## includes

If you're looking to see if an array includes a particular element, try includes:

## map

Similar to forEach, map is a great way to create a new array based on an existing array. Let's say you wanted to add a tag around some text in an array:
You can also have easy access to the index of the item in the array:

## sort

Sorting arrays is quite valuable and sometimes a pain. Assume you have a 2D array from a spreadsheet with columns of "name", "age", "email". If you want to sort them by age, do this:
really sort needs to return a true or false when determining who should go first, but by subtracting two numbers, if you get a zero or a negative number it'll be treated as false whereas a positive number will be treated as true.
If you wanted to sort by the age and the name (alphabetically) you could:

# templates
## Templates

* Google Apps Script