Anda dapat menggabungkan kode Apps Script dan HTML untuk menghasilkan halaman dinamis dengan sedikit upaya. Jika Anda telah menggunakan bahasa template yang menggabungkan kode dan HTML, seperti PHP, ASP, atau JSP, sintaksisnya akan terasa familier.
Scriptlet
Template Apps Script dapat berisi tiga tag khusus, yang disebut scriptlet. Di dalam scriptlet, Anda dapat menulis kode apa pun yang akan berfungsi dalam file Apps Script normal: scriptlet dapat memanggil fungsi yang ditentukan dalam file kode lain, mereferensikan variabel global, atau menggunakan salah satu Apps Script API. Anda bahkan dapat menentukan fungsi dan variabel dalam skriplet, dengan pengecualian bahwa fungsi tersebut tidak dapat dipanggil oleh fungsi yang ditentukan dalam file kode atau template lainnya.
Jika Anda menempelkan contoh di bawah ke editor skrip, konten tag
<?= ... ?>
(scriptlet pencetakan) akan muncul dalam
huruf miring. Kode yang dicetak miring tersebut berjalan di server sebelum halaman ditayangkan
kepada pengguna. Karena kode skriplet dieksekusi sebelum halaman ditayangkan, kode tersebut hanya dapat dijalankan satu kali per halaman; tidak seperti fungsi JavaScript sisi klien atau Apps Script yang Anda panggil melalui google.script.run
, skriplet tidak dapat dieksekusi lagi setelah halaman dimuat.
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>
Perhatikan bahwa fungsi doGet()
untuk HTML dengan template berbeda dengan contoh
untuk membuat dan menayangkan HTML dasar. Fungsi yang ditampilkan di sini menghasilkan objek HtmlTemplate
dari file HTML, lalu memanggil metode evaluate()
untuk mengeksekusi skriplet dan mengonversi template menjadi objek HtmlOutput
yang dapat ditayangkan skrip kepada pengguna.
Scriptlet standar
Scriptlet standar, yang menggunakan sintaksis <? ... ?>
, mengeksekusi kode tanpa
mengekspor konten secara eksplisit ke halaman. Namun, seperti yang ditunjukkan contoh ini, hasil kode di dalam skriplet masih dapat memengaruhi konten HTML
di luar skriplet:
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>
Mencetak scriptlet
Mencetak scriptlet, yang menggunakan sintaksis <?= ... ?>
, menghasilkan output
kodenya ke halaman menggunakan escape kontekstual.
Escaping kontekstual berarti bahwa Apps Script melacak konteks output
di halaman — di dalam atribut HTML, di dalam tag script
sisi klien, atau
di tempat lain — dan otomatis menambahkan karakter escape
untuk melindungi dari serangan pembuatan skrip lintas situs (XSS).
Dalam contoh ini, skrip pencetakan pertama menghasilkan string secara langsung; diikuti dengan skrip standar yang menyiapkan array dan loop, diikuti dengan skrip pencetakan lain untuk menghasilkan konten array.
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>
Perhatikan bahwa scriptlet pencetakan hanya menghasilkan output nilai pernyataan pertamanya;
pernyataan yang tersisa berperilaku seolah-olah terdapat dalam scriptlet
standar. Jadi, misalnya, scriptlet <?= 'Hello, world!'; 'abc' ?>
hanya
mencetak "Halo, dunia!"
Scriptlet pencetakan paksa
Scriptlet pencetakan paksa, yang menggunakan sintaksis <?!= ... ?>
, mirip dengan scriptlet
pencetakan, kecuali bahwa scriptlet ini menghindari escape kontekstual.
Escaping kontekstual penting jika skrip Anda mengizinkan input pengguna yang tidak tepercaya. Sebaliknya, Anda harus memaksa pencetakan jika output skriplet sengaja berisi HTML atau skrip yang ingin disisipkan persis seperti yang ditentukan.
Sebagai aturan umum, gunakan scriptlet pencetakan, bukan scriptlet pencetakan paksa, kecuali jika Anda tahu bahwa Anda perlu mencetak HTML atau JavaScript tanpa perubahan.
Kode Apps Script dalam scriptlet
Scriptlet tidak dibatasi untuk menjalankan JavaScript normal; Anda juga dapat menggunakan salah satu dari tiga teknik berikut untuk memberi template Anda akses ke data Apps Script.
Namun, perlu diingat bahwa karena kode template dijalankan sebelum halaman ditayangkan
kepada pengguna, teknik ini hanya dapat memasukkan konten awal ke halaman. Untuk mengakses data Apps Script dari halaman secara interaktif, gunakan google.script.run
API.
Memanggil fungsi Apps Script dari template
Scriptlet dapat memanggil fungsi apa pun yang ditentukan dalam file kode atau library Apps Script. Contoh ini menunjukkan salah satu cara untuk mengambil data dari spreadsheet ke dalam template, lalu membuat tabel HTML dari data tersebut.
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>
Memanggil Apps Script API secara langsung
Anda juga dapat menggunakan kode Apps Script secara langsung dalam skriplet. Contoh ini mencapai hasil yang sama seperti contoh sebelumnya dengan memuat data dalam template itu sendiri, bukan melalui fungsi terpisah.
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>
Mengirim variabel ke template
Terakhir, Anda dapat memasukkan variabel ke dalam template dengan menetapkannya sebagai properti
objek HtmlTemplate
. Sekali lagi, contoh ini menghasilkan hasil yang sama dengan contoh sebelumnya.
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>
Men-debug template
Template dapat sulit di-debug karena kode yang Anda tulis tidak dijalankan secara langsung; sebagai gantinya, server mengubah template Anda menjadi kode, lalu menjalankan kode yang dihasilkan.
Jika tidak jelas bagaimana template menafsirkan scriptlet Anda, dua
metode proses debug di
class HtmlTemplate
dapat membantu Anda
lebih memahami apa yang terjadi.
getCode()
getCode()
menampilkan string yang berisi kode yang dibuat server dari template. Jika Anda
mencatat ke dalam log
kode, lalu menempelkannya ke editor skrip, Anda dapat menjalankannya dan
men-debug-nya seperti kode Apps Script
normal.
Berikut adalah template sederhana yang menampilkan daftar produk Google lagi, diikuti dengan hasil 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>
LOG (DIEVALUASI)
(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()
mirip dengan getCode()
, tetapi menampilkan kode yang dievaluasi sebagai komentar yang
muncul berdampingan dengan template asli.
Mempelajari kode yang dievaluasi
Hal pertama yang akan Anda perhatikan dalam contoh kode yang dievaluasi adalah objek
output
implisit yang dibuat oleh metode HtmlService.initTemplate()
. Metode ini
tidak didokumentasikan karena hanya template itu sendiri yang perlu menggunakannya. output
adalah
objek HtmlOutput
khusus dengan dua
properti yang dinamai tidak biasa, _
dan _$
, yang merupakan singkatan untuk memanggil
append()
dan
appendUntrusted()
.
output
memiliki satu properti khusus lagi, $out
, yang merujuk ke objek
HtmlOutput
reguler yang tidak memiliki properti khusus ini. Template
akan menampilkan objek normal tersebut di akhir kode.
Setelah Anda memahami sintaksis ini, kode lainnya akan cukup mudah diikuti. Konten HTML di luar skriplet (seperti tag b
) ditambahkan
menggunakan output._ =
(tanpa escaping kontekstual),
dan skriplet ditambahkan sebagai JavaScript (dengan atau tanpa escaping kontekstual,
bergantung pada jenis skriplet).
Perhatikan bahwa kode yang dievaluasi mempertahankan nomor baris dari template. Jika Anda mengalami error saat menjalankan kode yang dievaluasi, baris akan sesuai dengan konten yang setara dalam template.
Hierarki komentar
Karena kode yang dievaluasi mempertahankan nomor baris, komentar di dalam skriplet dapat mengomentari skriplet lain dan bahkan kode HTML. Contoh ini menunjukkan beberapa efek komentar yang mengejutkan:
<? 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. */ ?>