HTML Service: Templated HTML

You can mix Apps Script code and HTML to produce dynamic pages with minimal
effort. If you've used a templating language that mixes code and HTML, such as
PHP, ASP, or JSP, the syntax should feel familiar.

Scriptlets

Apps Script templates can contain three special tags, called scriptlets. Inside
a scriptlet, you can write any code that would work in a normal Apps Script
file: scriptlets can call functions defined in other code files, reference
global variables, or use any of the Apps Script APIs. You can even define
functions and variables within scriptlets, with the caveat that they can't be
called by functions defined in code files or other templates.

If you paste the example below into the script editor, the contents of the
<?= ... ?> tag (a printing scriptlet) will appear in
italics. That italicized code runs on the server before the page is served
to the user. Because scriptlet code executes before the page is served, it
can only run once per page; unlike client-side JavaScript or Apps Script
functions that you call through
google.script.run, scriptlets can't
execute again after the page loads.

Index.html

Note that the doGet() function for templated HTML differs from the examples
for creating and serving basic HTML. The function
shown here generates an
HtmlTemplate object from the HTML
file, then calls its
evaluate() method to
execute the scriptlets and convert the template into an
HtmlOutput object that the script
can serve to the user.

Standard scriptlets

Standard scriptlets, which use the syntax <? ... ?>, execute code without
explicitly outputting content to the page. However, as this example shows, the
result of the code inside a scriptlet can still affect the HTML content
outside of the scriptlet:

In this example, the first printing scriptlet outputs a string directly; it is
followed by a standard scriptlet that sets up an array and a loop, followed by
another printing scriptlet to output the contents of the array.

Note that a printing scriptlet only outputs the value of its first statement;
any remaining statements behave as if they were contained in a standard
scriptlet. So, for example, the scriptlet <?= 'Hello, world!'; 'abc' ?> only
prints "Hello, world!"

Force-printing scriptlets

Force-printing scriptlets, which use the syntax <?!= ... ?>, are like printing
scriptlets except that they avoid contextual escaping.

Contextual escaping is important if your script allows untrusted user input. By
contrast, you’ll need to force-print if your scriptlet’s output intentionally
contains HTML or scripts that you want to insert exactly as specified.

As a general rule, use printing scriptlets rather than force-printing scriptlets
unless you know that you need to print HTML or JavaScript unchanged.

Apps Script code in scriptlets

Scriptlets aren’t restricted to running normal JavaScript; you can also use any
of the following three techniques to give your templates access to Apps Script
data.

Remember, however, that because template code executes before the page is served
to the user, these techniques can only feed initial content to a page. To access
Apps Script data from a page interactively, use the
google.script.run API instead.

Calling Apps Script functions from a template

Scriptlets can call any function defined in an Apps Script code file or library.
This example shows one way to pull data from a spreadsheet into a template, then
construct an HTML table from the data.

Calling Apps Script APIs directly

You can also use Apps Script code directly in scriptlets. This example
accomplishes the same result as the previous example by loading the data in the
template itself rather than through a separate function.

Debugging templates

Templates can be challenging to debug because the code you write is not executed
directly; instead, the server transforms your template into code, then executes
that resulting code.

If it isn’t obvious how the template is interpreting your scriptlets, two
debugging methods in the
HtmlTemplate class can help you
better understand what's going on.

getCode()

getCode() returns a
string containing the code that the server creates from the template. If you
log the
code, then paste it into the script editor, you can run it and
debug it like normal
Apps Script code.

Here's the simple template that displays a list of Google products again,
followed by the result of getCode():

getCodeWithComments()

getCodeWithComments()
is similar to getCode(), but returns the evaluated code as comments that
appear side-by-side with the original template.

Walking through evaluated code

The first thing you’ll notice in either sample of evaluated code is the implicit
output object created by the method HtmlService.initTemplate(). This method
is undocumented because only templates themselves need to use it. output is a
special HtmlOutput object with two
unusually named properties, _ and _$, which are shorthand for calling
append() and
appendUntrusted().

output has one more special property, $out, which refers to a regular
HtmlOutput object that does not possess these special properties. The template
returns that normal object at the end of the code.

Now that you understand this syntax, the rest of the code should be fairly easy
to follow. HTML content outside of scriptlets (like the b tag) is appended
using output._ = (without contextual escaping),
and scriptlets are appended as JavaScript (with or without contextual escaping,
depending on the type of scriptlet).

Note that the evaluated code preserves line numbers from the template. If you
get an error while running evaluated code, the line will correspond to the
equivalent content in the template.

Hierarchy of comments

Because evaluated code preserves line numbers, it is possible for comments
inside scriptlets to comment out other scriptlets and even HTML code. These
examples show a few surprising effects of comments:

<? var x; // a comment ?> This sentence won't print because a comment begins inside a scriptlet on the same line.
<? var y; // ?> <?= "This sentence won't print because a comment begins inside a scriptlet on the same line.";
output.append("This sentence will print because it's on the next line, even though it's in the same scriptlet.”) ?>
<? doSomething(); /* ?>
This entire block is commented out,
even if you add a */ in the HTML
or in a <script> */ </script> tag,
<? until you end the comment inside a scriptlet. */ ?>