Вы можете комбинировать код Apps Script и HTML для создания динамических страниц с минимальными усилиями. Если вы использовали язык шаблонизации, сочетающий код и HTML, например, PHP, ASP или JSP, синтаксис должен показаться вам знакомым.
Скриптлеты
Шаблоны Apps Script могут содержать три специальных тега, называемых скриптлетами. Внутри скриптлета можно написать любой код, который будет работать в обычном файле Apps Script: скриптлеты могут вызывать функции, определённые в других файлах кода, ссылаться на глобальные переменные или использовать любые API Apps Script. Вы даже можете определять функции и переменные внутри скриптлетов, с оговоркой, что они не могут быть вызваны функциями, определёнными в файлах кода или других шаблонах.
Если вставить приведенный ниже пример в редактор скриптов, содержимое тега <?= ... ?>
( скриптлета печати ) будет выделено курсивом. Этот выделенный курсивом код выполняется на сервере до того, как страница будет показана пользователю . Поскольку код скриптлета выполняется до того, как страница будет показана пользователю, он может быть выполнен только один раз на каждой странице; в отличие от клиентских функций JavaScript или Apps Script, вызываемых через google.script.run
, скриптлеты не могут быть выполнены повторно после загрузки страницы.
Код.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Индекс.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Hello, World! The time is <?= new Date() ?>.
</body>
</html>
Обратите внимание, что функция doGet()
для шаблонного HTML отличается от примеров создания и обслуживания базового HTML . Показанная здесь функция генерирует объект HtmlTemplate
из HTML-файла, а затем вызывает его метод evaluate()
для выполнения скриптлетов и преобразования шаблона в объект HtmlOutput
, который скрипт может предоставить пользователю.
Стандартные скриптлеты
Стандартные скриптлеты, использующие синтаксис <? ... ?>
, выполняют код без явного вывода содержимого на страницу. Однако, как показывает этот пример, результат выполнения кода внутри скриптлета может по-прежнему влиять на HTML-содержимое за пределами скриптлета:
Код.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Индекс.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<? if (true) { ?>
<p>This will always be served!</p>
<? } else { ?>
<p>This will never be served.</p>
<? } ?>
</body>
</html>
Печать скриптлетов
Печатные скриптлеты, которые используют синтаксис <?= ... ?>
, выводят результаты своего кода на страницу с помощью контекстного экранирования.
Контекстное экранирование означает, что Apps Script отслеживает контекст выходных данных на странице — внутри атрибута HTML, внутри тега клиентского script
или где-либо еще — и автоматически добавляет экранированные символы для защиты от атак межсайтового скриптинга (XSS) .
В этом примере первый скриптлет печати выводит строку напрямую; за ним следует стандартный скриптлет, который создает массив и цикл, а затем еще один скриптлет печати выводит содержимое массива.
Код.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Индекс.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<?= 'My favorite Google products:' ?>
<? var data = ['Gmail', 'Docs', 'Android'];
for (var i = 0; i < data.length; i++) { ?>
<b><?= data[i] ?></b>
<? } ?>
</body>
</html>
Обратите внимание, что скриптлет печати выводит только значение своего первого оператора; все остальные операторы ведут себя так, как если бы они содержались в стандартном скриптлете. Например, скриптлет <?= 'Hello, world!'; 'abc' ?>
выводит только «Hello, world!»
Принудительная печать скриптлетов
Принудительно печатающие скриптлеты, которые используют синтаксис <?!= ... ?>
, похожи на печатающие скриптлеты, за исключением того, что они избегают контекстного экранирования.
Контекстное экранирование важно, если ваш скрипт допускает ненадёжный пользовательский ввод. Если же вывод вашего скриптлета намеренно содержит HTML или скрипты, которые вы хотите вставить именно так, как указано, вам потребуется принудительно выводить текст.
Как правило, следует использовать распечатываемые скриптлеты вместо принудительной распечатки скриптлетов, если только вы не уверены, что вам нужно распечатать HTML или JavaScript без изменений.
Код скрипта приложений в скриптлетах
Скриптлеты не ограничиваются запуском обычного JavaScript; вы также можете использовать любой из следующих трех методов, чтобы предоставить вашим шаблонам доступ к данным Apps Script.
Однако помните, что, поскольку код шаблона выполняется до того, как страница будет показана пользователю, эти методы могут только передать на страницу начальный контент. Для интерактивного доступа к данным Apps Script со страницы используйте API google.script.run
.
Вызов функций Apps Script из шаблона
Скриптлеты могут вызывать любую функцию, определённую в файле кода или библиотеке Apps Script. В этом примере показан один из способов извлечь данные из электронной таблицы в шаблон и затем создать HTML-таблицу на основе этих данных.
Код.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
function getData() {
return SpreadsheetApp
.openById('1234567890abcdefghijklmnopqrstuvwxyz')
.getActiveSheet()
.getDataRange()
.getValues();
}
Индекс.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<? var data = getData(); ?>
<table>
<? for (var i = 0; i < data.length; i++) { ?>
<tr>
<? for (var j = 0; j < data[i].length; j++) { ?>
<td><?= data[i][j] ?></td>
<? } ?>
</tr>
<? } ?>
</table>
</body>
</html>
Прямой вызов API-интерфейсов Apps Script
Вы также можете использовать код Apps Script непосредственно в скриптлетах. Этот пример достигает того же результата, что и предыдущий, загружая данные в сам шаблон, а не через отдельную функцию.
Код.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Индекс.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<? var data = SpreadsheetApp
.openById('1234567890abcdefghijklmnopqrstuvwxyz')
.getActiveSheet()
.getDataRange()
.getValues(); ?>
<table>
<? for (var i = 0; i < data.length; i++) { ?>
<tr>
<? for (var j = 0; j < data[i].length; j++) { ?>
<td><?= data[i][j] ?></td>
<? } ?>
</tr>
<? } ?>
</table>
</body>
</html>
Передача переменных в шаблоны
Наконец, вы можете добавить переменные в шаблон, назначив их свойствами объекта HtmlTemplate
. Этот пример снова даёт тот же результат, что и предыдущие.
Код.gs
function doGet() {
var t = HtmlService.createTemplateFromFile('Index');
t.data = SpreadsheetApp
.openById('1234567890abcdefghijklmnopqrstuvwxyz')
.getActiveSheet()
.getDataRange()
.getValues();
return t.evaluate();
}
Индекс.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<table>
<? for (var i = 0; i < data.length; i++) { ?>
<tr>
<? for (var j = 0; j < data[i].length; j++) { ?>
<td><?= data[i][j] ?></td>
<? } ?>
</tr>
<? } ?>
</table>
</body>
</html>
Шаблоны отладки
Отладка шаблонов может оказаться сложной задачей, поскольку написанный вами код не выполняется напрямую. Вместо этого сервер преобразует ваш шаблон в код, а затем выполняет полученный код.
Если не совсем понятно, как шаблон интерпретирует ваши скриптлеты, два метода отладки в классе HtmlTemplate
помогут вам лучше понять, что происходит.
получитьКод()
getCode()
возвращает строку, содержащую код, созданный сервером на основе шаблона. Если вы зарегистрируете код, а затем вставите его в редактор скриптов, вы сможете запустить его и отладить как обычный код Apps Script.
Вот простой шаблон, который снова отображает список продуктов Google, а затем результат getCode()
:
Код.gs
function myFunction() {
Logger.log(HtmlService
.createTemplateFromFile('Index')
.getCode());
}
Индекс.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<?= 'My favorite Google products:' ?>
<? var data = ['Gmail', 'Docs', 'Android'];
for (var i = 0; i < data.length; i++) { ?>
<b><?= data[i] ?></b>
<? } ?>
</body>
</html>
ЖУРНАЛ (ОЦЕНЕННЫЙ)
(function() { var output = HtmlService.initTemplate(); output._ = '<!DOCTYPE html>\n';
output._ = '<html>\n' +
' <head>\n' +
' <base target=\"_top\">\n' +
' </head>\n' +
' <body>\n' +
' '; output._$ = 'My favorite Google products:' ;
output._ = ' '; var data = ['Gmail', 'Docs', 'Android'];
for (var i = 0; i < data.length; i++) { ;
output._ = ' <b>'; output._$ = data[i] ; output._ = '</b>\n';
output._ = ' '; } ;
output._ = ' </body>\n';
output._ = '</html>';
/* End of user code */
return output.$out.append('');
})();
получитьCodeWithComments()
getCodeWithComments()
похож на getCode()
, но возвращает оцененный код в виде комментариев, которые отображаются рядом с исходным шаблоном.
Прохождение оцененного кода
Первое, что вы заметите в любом из примеров вычисляемого кода, — это неявный объект output
, создаваемый методом HtmlService.initTemplate()
. Этот метод недокументирован, поскольку он нужен только самим шаблонам. output
— это специальный объект HtmlOutput
с двумя необычно названными свойствами, _
и _$
, которые являются сокращённой записью для вызова append()
и appendUntrusted()
.
У output
есть ещё одно специальное свойство, $out
, которое ссылается на обычный объект HtmlOutput
, не обладающий этими особыми свойствами. Шаблон возвращает этот обычный объект в конце кода.
Теперь, когда вы поняли этот синтаксис, остальная часть кода должна быть довольно простой для понимания. HTML-содержимое за пределами скриптлетов (например, тег b
) добавляется с помощью output._ =
(без контекстного экранирования ), а скриптлеты добавляются как JavaScript (с контекстным экранированием или без него, в зависимости от типа скриптлета).
Обратите внимание, что вычисляемый код сохраняет номера строк из шаблона. Если при выполнении вычисляемого кода возникнет ошибка, строка будет соответствовать эквивалентному содержимому шаблона.
Иерархия комментариев
Поскольку оцениваемый код сохраняет номера строк, комментарии внутри скриптлетов могут комментировать другие скриптлеты и даже HTML-код. Эти примеры демонстрируют несколько неожиданных эффектов комментариев:
<? 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. */ ?>