É possível combinar código e HTML do Apps Script para produzir páginas dinâmicas com o mínimo de esforço. Se você usou uma linguagem de modelo que mistura código e HTML, como PHP, ASP ou VAST, a sintaxe deve parecer familiar.
Scriptlets
Os modelos do Apps Script podem conter três tags especiais, chamadas "scriptlets". Dentro de um scriptlet, é possível escrever qualquer código que funcione em um arquivo do Apps Script normal: ele pode chamar funções definidas em outros arquivos de código, referenciar variáveis globais ou usar qualquer uma das APIs do Apps Script. Você pode até definir funções e variáveis dentro de scriptlets, com a ressalva de que elas não podem ser chamadas por funções definidas em arquivos de código ou outros modelos.
Se você colar o exemplo abaixo no editor de script, o conteúdo da tag <?= ... ?>
(um scriptlet de impressão) aparecerá em itálico. Esse código em itálico é executado no servidor antes da exibição da página para o usuário. Como o código do scriptlet é executado antes da exibição da página, ele só pode ser executado uma vez por página. Ao contrário das funções do JavaScript ou do Apps Script do lado do cliente que você chama usando google.script.run
, os scriptlets não podem ser executados novamente após o carregamento da página.
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>
A função doGet()
para HTML modelo é diferente dos exemplos
de criação e exibição de HTML básico. A função
mostrada aqui gera um objeto
HtmlTemplate
do arquivo HTML
e, em seguida, chama o método
evaluate()
para
executar os scriptlets e converter o modelo em um objeto
HtmlOutput
que o script
pode veicular ao usuário.
Scriptlets padrão
Os scriptlets padrão, que usam a sintaxe <? ... ?>
, executam o código sem
enviar conteúdo explicitamente para a página. No entanto, como mostra o exemplo, o resultado do código dentro de um scriptlet ainda pode afetar o conteúdo HTML fora do scriptlet:
Code.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
Index.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>
Como imprimir scripts
Os scriptlets de impressão, que usam a sintaxe <?= ... ?>
, geram os resultados do
código na página usando escape contextual.
O escape contextual significa que o Apps Script monitora o contexto da saída
na página (dentro de um atributo HTML, em uma tag script
do lado do cliente ou
em qualquer outro lugar) e adiciona automaticamente caracteres de escape
para proteger contra ataques de scripting em vários locais (XSS).
Neste exemplo, o primeiro scriptlet de impressão gera uma string diretamente. Ele é seguido por um scriptlet padrão que configura uma matriz e um loop, seguidos por outro scriptlet de impressão para mostrar o conteúdo da matriz.
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>
Um scriptlet de impressão gera apenas o valor da primeira instrução. As instruções restantes se comportam como se estivessem contidas em um scriptlet padrão. Por exemplo, o scriptlet <?= 'Hello, world!'; 'abc' ?>
imprime apenas "Hello, world!"
Como forçar a impressão de scriptlets
Os scriptlets de impressão forçada, que usam a sintaxe <?!= ... ?>
, são como a impressão
de scriptlets, mas evitam escape contextual.
O escape contextual é importante se o script permite entradas não confiáveis do usuário. Em contrapartida, será necessário forçar a impressão se a saída do scriptlet contiver intencionalmente HTML ou scripts que você queira inserir exatamente como especificado.
Como regra geral, use a impressão de scriptlets em vez de forçar a impressão, a menos que você saiba que precisa imprimir HTML ou JavaScript sem alterações.
Código do Apps Script em scriptlets
Os scripts não são restritos à execução de JavaScript normal. Também é possível usar qualquer uma das três técnicas a seguir para dar aos seus modelos acesso aos dados do Apps Script.
No entanto, lembre-se de que, como o código do modelo é executado antes da exibição da página ao usuário, essas técnicas só podem alimentar o conteúdo inicial de uma página. Para acessar os dados do Apps Script em uma página de maneira interativa, use a API google.script.run
.
Como chamar funções do Apps Script usando um modelo
Os scripts podem chamar qualquer função definida em um arquivo ou biblioteca de código do Apps Script. Este exemplo mostra uma maneira de extrair dados de uma planilha para um modelo e, em seguida, criar uma tabela HTML a partir dos dados.
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>
Chamar APIs do Apps Script diretamente
Também é possível usar o código do Apps Script diretamente nos scriptlets. Este exemplo atinge o mesmo resultado do exemplo anterior, carregando os dados no próprio modelo, e não por meio de uma função separada.
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>
Como enviar variáveis para modelos
Por fim, é possível enviar variáveis para um modelo atribuindo-as como propriedades
do objeto HtmlTemplate
. Novamente, este exemplo alcança o mesmo resultado dos exemplos anteriores.
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>
Modelos de depuração
Pode ser difícil depurar modelos porque o código que você escreve não é executado diretamente. Em vez disso, o servidor transforma seu modelo em código e, em seguida, executa esse código resultante.
Se não for óbvio como o modelo está interpretando os scriptlets, dois
métodos de depuração na classe
HtmlTemplate
podem ajudar você
a entender melhor o que está acontecendo.
getCode()
getCode()
retorna uma
string que contém o código que o servidor cria com base no modelo. Se você
registrar o
código e colá-lo no editor de script, execute-o e
depure como o código normal
do Apps Script.
Este é o modelo simples que exibe novamente uma lista de produtos do Google,
seguido pelo resultado de 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>
REGISTRO (AVALIADO)
(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()
é semelhante a getCode()
, mas retorna o código avaliado como comentários que
aparecem lado a lado com o modelo original.
Orientações sobre o código avaliado
A primeira coisa que você notará em qualquer amostra de código avaliado é o objeto
output
implícito criado pelo método HtmlService.initTemplate()
. Esse método
não é documentado porque apenas os modelos em si precisam usá-lo. output
é um objeto HtmlOutput
especial com duas propriedades incomuns, _
e _$
, que são uma abreviação para chamar append()
e appendUntrusted()
.
output
tem mais uma propriedade especial, $out
, que se refere a um objeto HtmlOutput
normal que não tem essas propriedades especiais. O modelo retorna esse objeto normal no final do código.
Agora que você entende essa sintaxe, o restante do código será bastante fácil de seguir. O conteúdo HTML fora dos scriptlets (como a tag b
) é anexado
usando output._ =
(sem escape contextual),
e os scriptlets são anexados como JavaScript (com ou sem escape contextual,
dependendo do tipo de scriptlet).
O código avaliado preserva os números de linha do modelo. Se você receber um erro ao executar o código avaliado, a linha corresponderá ao conteúdo equivalente no modelo.
Hierarquia de comentários
Como o código avaliado preserva os números de linha, é possível que comentários dentro de scriptlets deixem outros scripts e até mesmo código HTML. Estes exemplos mostram alguns efeitos surpreendentes dos comentários:
<? 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. */ ?>