Bạn có thể kết hợp mã Apps Script và HTML để tạo ra các trang động mà không tốn nhiều công sức. Nếu bạn đã sử dụng một ngôn ngữ tạo mẫu kết hợp mã và HTML, chẳng hạn như PHP, ASP hoặc JSP, thì cú pháp này sẽ quen thuộc với bạn.
Scriptlet
Mẫu Apps Script có thể chứa 3 thẻ đặc biệt, được gọi là scriptlet. Trong một tập lệnh nhỏ, bạn có thể viết bất kỳ mã nào hoạt động trong một tệp Apps Script thông thường: tập lệnh nhỏ có thể gọi các hàm được xác định trong các tệp mã khác, tham chiếu các biến chung hoặc sử dụng bất kỳ API nào của Apps Script. Bạn thậm chí có thể xác định các hàm và biến trong scriptlet, với lưu ý rằng các hàm và biến này không thể được gọi bởi các hàm được xác định trong tệp mã hoặc các mẫu khác.
Nếu bạn dán ví dụ bên dưới vào trình chỉnh sửa tập lệnh, nội dung của thẻ <?= ... ?>
(một đoạn tập lệnh in) sẽ xuất hiện ở dạng chữ in nghiêng. Đoạn mã in nghiêng đó chạy trên máy chủ trước khi trang được phân phát cho người dùng. Vì mã scriptlet thực thi trước khi trang được phân phát, nên mã này chỉ có thể chạy một lần cho mỗi trang; không giống như các hàm JavaScript phía máy khách hoặc Apps Script mà bạn gọi thông qua google.script.run
, scriptlet không thể thực thi lại sau khi trang tải.
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>
Xin lưu ý rằng hàm doGet()
cho HTML dựa trên mẫu khác với các ví dụ về tạo và phân phát HTML cơ bản. Hàm được minh hoạ ở đây tạo một đối tượng HtmlTemplate
từ tệp HTML, sau đó gọi phương thức evaluate()
của đối tượng này để thực thi các tập lệnh nhỏ và chuyển đổi mẫu thành một đối tượng HtmlOutput
mà tập lệnh có thể phân phát cho người dùng.
Standard scriptlet
Các tập lệnh nhỏ tiêu chuẩn sử dụng cú pháp <? ... ?>
, thực thi mã mà không cần xuất nội dung một cách rõ ràng ra trang. Tuy nhiên, như ví dụ này cho thấy, kết quả của mã bên trong một scriptlet vẫn có thể ảnh hưởng đến nội dung HTML bên ngoài 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>
In tập lệnh nhỏ
Các tập lệnh in sử dụng cú pháp <?= ... ?>
sẽ xuất kết quả của mã vào trang bằng cách sử dụng tính năng thoát theo ngữ cảnh.
Thoát theo bối cảnh có nghĩa là Apps Script theo dõi bối cảnh đầu ra trên trang – bên trong một thuộc tính HTML, bên trong thẻ script
phía máy khách hoặc ở bất kỳ vị trí nào khác – và tự động thêm các ký tự thoát để bảo vệ khỏi các cuộc tấn công tập lệnh trên nhiều trang web (XSS).
Trong ví dụ này, tập lệnh nhỏ in đầu tiên xuất trực tiếp một chuỗi; sau đó là một tập lệnh nhỏ tiêu chuẩn thiết lập một mảng và một vòng lặp, tiếp theo là một tập lệnh nhỏ in khác để xuất nội dung của mảng.
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>
Xin lưu ý rằng một tập lệnh nhỏ in chỉ xuất giá trị của câu lệnh đầu tiên; mọi câu lệnh còn lại sẽ hoạt động như thể chúng nằm trong một tập lệnh nhỏ tiêu chuẩn. Ví dụ: tập lệnh nhỏ <?= 'Hello, world!'; 'abc' ?>
chỉ in "Xin chào thế giới!"
Tập lệnh in bắt buộc
Các tập lệnh in bắt buộc sử dụng cú pháp <?!= ... ?>
, giống như các tập lệnh in, ngoại trừ việc chúng tránh thoát theo bối cảnh.
Thoát theo ngữ cảnh là điều quan trọng nếu tập lệnh của bạn cho phép người dùng không đáng tin cậy nhập dữ liệu. Ngược lại, bạn sẽ cần phải buộc in nếu đầu ra của tập lệnh nhỏ chứa HTML hoặc tập lệnh mà bạn muốn chèn chính xác như đã chỉ định.
Theo quy tắc chung, hãy sử dụng tập lệnh in thay vì tập lệnh in bắt buộc, trừ phi bạn biết rằng bạn cần in HTML hoặc JavaScript mà không thay đổi.
Mã Apps Script trong tập lệnh nhỏ
Scriptlet không bị hạn chế chạy JavaScript thông thường; bạn cũng có thể sử dụng bất kỳ kỹ thuật nào trong số 3 kỹ thuật sau để cấp cho các mẫu của bạn quyền truy cập vào dữ liệu Apps Script.
Tuy nhiên, hãy nhớ rằng vì mã mẫu thực thi trước khi trang được phân phát cho người dùng, nên những kỹ thuật này chỉ có thể cung cấp nội dung ban đầu cho một trang. Để truy cập vào dữ liệu Apps Script từ một trang một cách tương tác, hãy sử dụng API google.script.run
.
Gọi hàm Apps Script từ một mẫu
Scriptlet có thể gọi bất kỳ hàm nào được xác định trong tệp mã hoặc thư viện Apps Script. Ví dụ này cho thấy một cách để kéo dữ liệu từ bảng tính vào một mẫu, sau đó tạo một bảng HTML từ dữ liệu đó.
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>
Gọi trực tiếp các API Apps Script
Bạn cũng có thể sử dụng mã Apps Script ngay trong các tập lệnh nhỏ. Ví dụ này đạt được kết quả tương tự như ví dụ trước bằng cách tải dữ liệu trong chính mẫu thay vì thông qua một hàm riêng biệt.
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>
Đẩy các biến vào mẫu
Cuối cùng, bạn có thể đẩy các biến vào một mẫu bằng cách chỉ định các biến đó làm thuộc tính của đối tượng HtmlTemplate
. Một lần nữa, ví dụ này đạt được kết quả tương tự như các ví dụ trước.
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>
Gỡ lỗi mẫu
Bạn có thể gặp khó khăn khi gỡ lỗi mẫu vì mã bạn viết không được thực thi trực tiếp; thay vào đó, máy chủ sẽ chuyển đổi mẫu của bạn thành mã, sau đó thực thi mã kết quả đó.
Nếu không rõ cách mẫu diễn giải các tập lệnh nhỏ của bạn, thì 2 phương pháp gỡ lỗi trong lớp HtmlTemplate
có thể giúp bạn hiểu rõ hơn về những gì đang diễn ra.
getCode()
getCode()
trả về một chuỗi chứa mã mà máy chủ tạo từ mẫu. Nếu ghi mã, sau đó dán mã đó vào trình soạn thảo tập lệnh, bạn có thể chạy và gỡ lỗi mã đó như mã Apps Script thông thường.
Sau đây là mẫu đơn giản hiển thị lại danh sách các sản phẩm của Google, theo sau là kết quả của 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 (ĐƯỢC ĐÁNH GIÁ)
(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()
tương tự như getCode()
, nhưng trả về mã đã đánh giá dưới dạng nhận xét xuất hiện song song với mẫu ban đầu.
Xem xét mã được đánh giá
Điều đầu tiên bạn sẽ nhận thấy trong cả hai mẫu mã được đánh giá là đối tượng output
ngầm ẩn do phương thức HtmlService.initTemplate()
tạo. Phương thức này không có tài liệu vì chỉ các mẫu mới cần sử dụng phương thức này. output
là một đối tượng HtmlOutput
đặc biệt có 2 thuộc tính có tên bất thường là _
và _$
. Đây là cách viết tắt để gọi append()
và appendUntrusted()
.
output
có thêm một thuộc tính đặc biệt là $out
. Thuộc tính này đề cập đến một đối tượng HtmlOutput
thông thường không có các thuộc tính đặc biệt này. Mẫu này trả về đối tượng thông thường đó ở cuối mã.
Giờ đây, khi bạn đã hiểu cú pháp này, phần còn lại của mã sẽ khá dễ hiểu. Nội dung HTML bên ngoài các đoạn mã (chẳng hạn như thẻ b
) được thêm vào bằng output._ =
(không có thoát theo ngữ cảnh) và các đoạn mã được thêm vào dưới dạng JavaScript (có hoặc không có thoát theo ngữ cảnh, tuỳ thuộc vào loại đoạn mã).
Xin lưu ý rằng mã được đánh giá sẽ giữ lại số dòng từ mẫu. Nếu bạn gặp lỗi khi chạy mã được đánh giá, thì dòng này sẽ tương ứng với nội dung tương đương trong mẫu.
Hệ thống phân cấp bình luận
Vì mã được đánh giá giữ lại số dòng, nên các nhận xét bên trong các tập lệnh nhỏ có thể nhận xét các tập lệnh nhỏ khác và thậm chí cả mã HTML. Những ví dụ này cho thấy một số tác động đáng ngạc nhiên của bình luận:
<? 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. */ ?>