אפשר לשלב קוד 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 will always be served!</p>
<? } else { ?>
<p>This will never be 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>
קריאה ישירה לממשקי Apps Script API
אפשר גם להשתמש בקוד 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>
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 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. */ ?>