אפשר להשתמש בתבניות כדי לשלב קוד של Google Apps Script ו-HTML, וכך ליצור דפים דינמיים במינימום מאמץ. אם השתמשתם בשפות תבניות שמשלבות קוד ו-HTML, כמו PHP, ASP או JSP, התחביר אמור להיות מוכר לכם.
סקריפטלטים
תבניות של Apps Script יכולות להכיל שלושה תגים מיוחדים שנקראים scriptlets. בתוך סקריפטלט, אפשר לכתוב כל קוד שפועל בקובץ 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 (cross-site scripting).
בדוגמה הזו, סקריפטלט ההדפסה הראשון מוציא מחרוזת ישירות. אחריו מופיע סקריפטלט רגיל שמגדיר מערך ולולאה, ואחריו מופיע סקריפטלט הדפסה נוסף שמוציא את התוכן של המערך.
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>
קריאה ישירה ל-Apps Script APIs
אפשר גם להשתמש בקוד Apps Script ישירות ב-scriptlets. בדוגמה הזו מתקבלת אותה תוצאה כמו בדוגמה הקודמת, אבל הנתונים נטענים בתבנית עצמה ולא באמצעות פונקציה נפרדת.
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>
LOG (EVALUATED)
(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. */ ?>