Вы можете использовать шаблоны для смешивания кода Google Apps Script и HTML, чтобы создавать динамические страницы с минимальными усилиями. Если вы уже использовали языки шаблонизации, которые смешивают код и HTML, такие как PHP, ASP или JSP, синтаксис должен показаться вам знакомым.
Сценарии
Шаблоны Apps Script могут содержать три специальных тега, называемых скриптлетами. Внутри скриптлета можно написать любой код, работающий в обычном файле Apps Script: скриптлеты могут вызывать функции, определенные в других файлах кода, ссылаться на глобальные переменные или использовать любой из API Apps Script. Вы даже можете определять функции и переменные внутри скриптлетов, с оговоркой, что их нельзя вызывать из функций, определенных в файлах кода или других шаблонах.
Если вы вставите следующий пример в редактор скриптов, содержимое тега <?= ... ?> ( скриптлет для печати ) отобразится курсивом. Этот код выполняется на сервере до того, как страница будет показана пользователю. Поскольку код скриптлета выполняется до показа страницы, он может быть выполнен только один раз за страницу. В отличие от функций JavaScript на стороне клиента или функций Apps Script, которые вызываются через google.script.run , скриптлеты не могут быть выполнены повторно после загрузки страницы.
Code.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Index.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-содержимое за пределами скриптлета:
Code.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<? if (true) { ?>
<p>This is always served!</p>
<? } else { ?>
<p>This is never served.</p>
<? } ?>
</body>
</html>
Распечатать сценарии
Скриптлеты для вывода на страницу, использующие синтаксис <?= ... ?> , выводят результаты своего кода с помощью контекстного экранирования.
Контекстное экранирование означает, что Apps Script отслеживает контекст выходных данных на странице — внутри HTML-атрибута, внутри тега script на стороне клиента или в любом другом месте — и автоматически добавляет экранирующие символы для защиты от атак межсайтового скриптинга (XSS) .
В этом примере первый скрипт для вывода текста выводит строку напрямую; за ним следует стандартный скрипт, который создает массив и цикл, а затем еще один скрипт для вывода содержимого массива.
Code.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Index.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 без изменений.
Код Apps Script в скриптлетах
Скриптлеты не ограничиваются выполнением обычного JavaScript; вы также можете использовать любой из следующих трех способов, чтобы предоставить вашим шаблонам доступ к данным Apps Script.
Однако помните, что поскольку код шаблона выполняется до того, как страница будет показана пользователю, эти методы могут передавать на страницу только исходное содержимое. Для интерактивного доступа к данным Apps Script со страницы используйте API google.script.run .
Вызов функций Apps Script из шаблона
Скриптлеты могут вызывать любые функции, определенные в файле кода Apps Script или библиотеке. В этом примере показан один из способов извлечения данных из электронной таблицы в шаблон, а затем построения HTML-таблицы на основе этих данных.
Code.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
function getData() {
return SpreadsheetApp
.openById('1234567890abcdefghijklmnopqrstuvwxyz')
.getActiveSheet()
.getDataRange()
.getValues();
}
Index.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 непосредственно в скриптлетах. В этом примере достигается тот же результат, что и в предыдущем, загрузка данных происходит непосредственно в шаблоне, а не через отдельную функцию.
Code.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Index.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 . И снова, этот пример дает тот же результат, что и предыдущие.
Code.gs
function doGet() {
var t = HtmlService.createTemplateFromFile('Index');
t.data = SpreadsheetApp
.openById('1234567890abcdefghijklmnopqrstuvwxyz')
.getActiveSheet()
.getDataRange()
.getValues();
return t.evaluate();
}
Index.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
Функция getCode возвращает строку, содержащую код, который сервер создает на основе шаблона. Если вы запишете этот код в лог, а затем вставите его в редактор скриптов, вы сможете запустить и отладить его как обычный код Apps Script.
Вот шаблон, который снова отображает список продуктов Google, а затем результат выполнения getCode :
Code.gs
function myFunction() {
Logger.log(HtmlService
.createTemplateFromFile('Index')
.getCode());
}
Index.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('');
})();
Функция getCodeWithComments
Функция 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 prints 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. */ ?>