שירות HTML: תקשורת עם פונקציות שרת

הכתובת google.script.run היא אסינכרונית JavaScript API בצד הלקוח שמאפשר לדפי שירות HTML לקרוא בצד השרת פונקציות של Apps Script. בדוגמה הבאה מוצגת הפונקציונליות הבסיסית ביותר מתוך google.script.runשליחת קריאה לפונקציה בשרת מ-JavaScript בצד הלקוח.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function doSomething() {
  Logger.log('I was called!');
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      google.script.run.doSomething();
    </script>
  </head>
</html>

אם תפרסו את הסקריפט הזה כאפליקציית אינטרנט ותבקרו בכתובת ה-URL שלו, לא תראו כל דבר, אבל אם תצפו ביומנים, תוכלו לראות שפונקציית השרת בוצעה שיחה אל doSomething().

קריאות בצד הלקוח לפונקציות בצד השרת הן אסינכרוניות: אחרי הדפדפן בקשות לשרת להפעיל את הפונקציה doSomething(), הדפדפן ממשיך ישירות לשורת הקוד הבאה, בלי לחכות לתגובה. כלומר ייתכן שהקריאות לפונקציות שרת לא יופעלו בסדר שציפיתם לו. אם שתי קריאות לפונקציות בו-זמנית, אין דרך לדעת איזה פונקציה הפעלה ראשונה; התוצאה עשויה להשתנות בכל פעם שטוענים את הדף. במקרה כזה, רכיבי handler של הצלחה ומטפלי בכשלים לשלוט בזרימת הקוד.

ה-API של google.script.run מאפשר 10 קריאות בו-זמנית לפונקציות שרת. אם המיקום תבצעו קריאה 11 בזמן שהשמעה 10 עדיין פועלת, פונקציית השרת מתעכבת עד שאחד מ-10 המקומות יתפנה. בפועל, לעתים נדירות חשוב על ההגבלה הזו, במיוחד לאור העובדה שרוב הדפדפנים כבר מגבילים מספר הבקשות בו-זמניות לאותו שרת במספר נמוך מ-10. למשל, ב-Firefox, המגבלה היא 6. רוב הדפדפנים משהים חריגה באופן דומה עד להשלמת אחת מהבקשות הקיימות.

פרמטרים וערכים מוחזרים

אפשר להפעיל פונקציית שרת עם פרמטרים מהלקוח. באופן דומה, פונקציית השרת יכולה להחזיר ערך ללקוח כפרמטר שמועבר מטפל בהצלחה.

פרמטרים משפטיים וערכי החזרה הם רכיבי JavaScript, כמו Number, Boolean, String או null, וגם באובייקטים ובמערכים של JavaScript מורכבים מפרימיטיביים, אובייקטים ומערכים. רכיב form בתוך הדף חוקי גם בתור פרמטר, אבל הוא חייב להיות הפרמטר היחיד של הפונקציה, הוא לא חוקי כערך החזרה. הבקשות ייכשלו אם תנסו להעביר Date, Function, רכיב DOM מלבד form או סוג אסור אחר, כולל סוגים אסורים בתוך אובייקטים או מערכים. אובייקטים שיוצרים הפניות מעגליות ייכשלו, ושדות לא מוגדרים בתוך מערכים null

שימו לב שאובייקט שמועבר לשרת הופך לעותק של האובייקט המקורי. אם פונקציית השרת מקבלת אובייקט ומשנה את המאפיינים שלו, הלקוח לא מושפע.

רכיבי handler של הצלחות

מפני שהקוד בצד הלקוח ממשיך לשורה הבאה בלי לחכות לשרת הקריאה הושלמה, withSuccessHandler(function) מאפשרת לציין פונקציית קריאה חוזרת בצד הלקוח שתרוץ כשהשרת מגיב. אם פונקציית השרת מחזירה ערך, ה-API מעביר את הערך אל את הפונקציה החדשה כפרמטר.

בדוגמה הבאה מוצגת התראה של הדפדפן כשהשרת מגיב. הערה שדוגמת הקוד הזו דורשת הרשאה, כי הפונקציה בצד השרת ניגשים לחשבון Gmail. הדרך הפשוטה ביותר לאשר את הסקריפט היא להריץ את את הפונקציה getUnreadEmails() באופן ידני מעורך הסקריפטים פעם אחת לפני לטעון את הדף. לחלופין, כאשר לפרוס את אפליקציית האינטרנט, אתם יכולים לבחור להפעיל אותו בתור "המשתמש שנכנס לאפליקציית האינטרנט", ובמקרה כזה כשטוענים את האפליקציה, צריך לתת לה הרשאה.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  return GmailApp.getInboxUnreadCount();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(numUnread) {
        var div = document.getElementById('output');
        div.innerHTML = 'You have ' + numUnread
            + ' unread messages in your Gmail inbox.';
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

מטפלים בכשלים

אם השרת לא מגיב או מטיח שגיאה, withFailureHandler(function) מאפשר לציין handler של כשל במקום handler של הצלחה, באמצעות Error (אם יש כזה) שמועבר כארגומנט.

כברירת מחדל, אם לא מציינים handler של כשלים, הכשלים נרשמים ביומן לוח JavaScript. כדי לשנות את ההגדרה הזו, צריך להתקשר אל withFailureHandler(null) או לספק את השירות מטפל בכשל שלא עושה דבר.

התחביר של מטפלים בכשלים כמעט זהה לתחביר של מטפלי הצלחה, לדוגמה.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  // 'got' instead of 'get' will throw an error.
  return GmailApp.gotInboxUnreadCount();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onFailure(error) {
        var div = document.getElementById('output');
        div.innerHTML = "ERROR: " + error.message;
      }

      google.script.run.withFailureHandler(onFailure)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

אובייקטים של משתמש

אפשר להשתמש שוב באותו handler של הצלחה או כישלון בקריאות מרובות שרת באמצעות קריאה withUserObject(object) כדי לציין אובייקט שיועבר ל-handler כפרמטר שני. אין להתבלבל, בין "אובייקט המשתמש" הזה לבין User – מאפשרת להשיב שבו הלקוח יצר קשר עם השרת. כי האובייקטים של המשתמשים הם לא יישלחו לשרת, הן יכולות להיות כמעט כל דבר, כולל פונקציות, DOM וכן הלאה, ללא הגבלות על פרמטרים והחזרת ערכים לקריאות שרת. עם זאת, אי אפשר ליצור אובייקטים של משתמש באמצעות האופרטור new.

בדוגמה זו, לחיצה על אחד משני לחצנים תעדכן את הלחצן מהשרת, ובמקביל משאירים את הלחצן השני ללא שינוי, למרות לשתף handler אחד של הצלחה. בתוך ה-handler של onclick, מילת המפתח this מתייחס אל button עצמו.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getEmail() {
  return Session.getActiveUser().getEmail();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function updateButton(email, button) {
        button.value = 'Clicked by ' + email;
      }
    </script>
  </head>
  <body>
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
  </body>
</html>

טפסים

אם מפעילים פונקציית שרת עם רכיב form כפרמטר, הטופס הופך לאובייקט יחיד עם שמות של שדות כמפתחות, וערכי השדות כערכים. כל הערכים מומרים למחרוזות, מלבד התוכן של קלט קבצים שהופכים לאובייקטים Blob.

בדוגמה הזו המערכת מעבדת טופס, כולל שדה להזנת קלט של קובץ, בלי לטעון מחדש הדף; הוא מעלה את הקובץ ל-Google Drive ואז מדפיסה את כתובת ה-URL של בדף בצד הלקוח. בתוך ה-handler של onsubmit, מילת המפתח this מתייחס אל הטופס עצמו. לתשומת ליבכם: אחרי שטוענים את כל הטפסים בדף, פעולת השליחה שמוגדרת כברירת מחדל הושבתה על ידי preventFormSubmit. הפעולה הזו מונעת מהפניה מחדש לכתובת אתר לא מדויקת במקרה של חריגה.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function processForm(formObject) {
  var formBlob = formObject.myFile;
  var driveFile = DriveApp.createFile(formBlob);
  return driveFile.getUrl();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      // Prevent forms from submitting.
      function preventFormSubmit() {
        var forms = document.querySelectorAll('form');
        for (var i = 0; i < forms.length; i++) {
          forms[i].addEventListener('submit', function(event) {
            event.preventDefault();
          });
        }
      }
      window.addEventListener('load', preventFormSubmit);

      function handleFormSubmit(formObject) {
        google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
      }
      function updateUrl(url) {
        var div = document.getElementById('output');
        div.innerHTML = '<a href="' + url + '">Got it!</a>';
      }
    </script>
  </head>
  <body>
    <form id="myForm" onsubmit="handleFormSubmit(this)">
      <input name="myFile" type="file" />
      <input type="submit" value="Submit" />
    </form>
    <div id="output"></div>
 </body>
</html>

הרצת סקריפטים

אפשר לחשוב על google.script.run ככלי ליצירת 'הרצת סקריפטים'. אם הוספה של מטפל הצלחה, מטפל בכשלים או אובייקט משתמש להרצת סקריפט, לא משנים את ההרצה הקיימת, במקום זאת, מקבלים חזרה קובץ מפעיל סקריפטים חדש עם התנהגות חדשה.

אפשר להשתמש בכל שילוב ובכל סדר של withSuccessHandler(), withFailureHandler() ו-withUserObject(). אפשר גם להתקשר לכל אחד לשנות פונקציות בהפעלה של סקריפט שכבר הוגדר לו ערך. הגרסה החדשה ערך פשוט מבטל את הערך הקודם.

בדוגמה הזו מוגדר handler משותף לכשלים לכל שלוש קריאות השרת, אבל רכיבי handler נפרדים של הצלחה:

var myRunner = google.script.run.withFailureHandler(onFailure);
var myRunner1 = myRunner.withSuccessHandler(onSuccess);
var myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);

myRunner1.doSomething();
myRunner1.doSomethingElse();
myRunner2.doSomething();

פונקציות פרטיות

פונקציות שרת שהשמות שלהן מסתיימים בקו תחתון נחשבות כפרטיות. לא ניתן לקרוא לפונקציות האלה עד google.script, והשמות שלהן אף פעם לא שנשלחו ללקוח. אתם יכולים להשתמש בהם כדי להסתיר פרטי הטמעה, צריך לשמור בסוד בשרת. גם לgoogle.script אין אפשרות לראות פונקציות בתוך ספריות ופונקציות שלא שהוצהר ברמה העליונה של הסקריפט.

בדוגמה הזו, הפונקציה getBankBalance() זמינה באפליקציית הלקוח קוד משתמש שבודק את קוד המקור יכול לגלות את השם שלו גם לא צריך לקרוא לזה. עם זאת, הפונקציות deepSecret_() ו-obj.objectMethod() לא גלויות לגמרי עם הלקוח.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getBankBalance() {
  var email = Session.getActiveUser().getEmail()
  return deepSecret_(email);
}

function deepSecret_(email) {
 // Do some secret calculations
 return email + ' has $1,000,000 in the bank.';
}

var obj = {
  objectMethod: function() {
    // More secret calculations
  }
};

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(balance) {
        var div = document.getElementById('output');
        div.innerHTML = balance;
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getBankBalance();
    </script>
  </head>
  <body>
    <div id="output">No result yet...</div>
  </body>
</html>

שינוי הגודל של תיבות דו-שיח Google Workspace באפליקציות

תיבות דו-שיח מותאמות אישית ב-Google Docs, ב-Sheets או כדי לשנות גודל של טפסים, אפשר לקרוא לפונקציה google.script.host אמצעי תשלום setWidth(width) או setHeight(height) אינץ' בקוד בצד הלקוח. (כדי להגדיר את הגודל ההתחלתי של תיבת דו-שיח, משתמשים ברכיב HtmlOutput אמצעי תשלום setWidth(width) וגם setHeight(height)). שימו לב שתיבות דו-שיח לא יתמרכזו מחדש בחלון ההורה לאחר שינוי הגודל, שאין אפשרות לשנות את הגודל של סרגל הצד.

סגירה של תיבות דו-שיח וסרגלי צד ב- Google Workspace

אם אתם משתמשים בשירות ה-HTML כדי להציג תיבת דו-שיח או סרגל הצד ב-Google Docs, ב-Sheets או טפסים, לא ניתן לסגור את הממשק על ידי שליחת קריאה אל window.close(). במקום זאת, חייב להתקשר google.script.host.close() לדוגמה, אפשר לעיין בקטע הצגת HTML כ Google Workspace ממשק משתמש.

העברת המיקוד של הדפדפן אל Google Workspace

כדי להעביר את המיקוד בדפדפן של המשתמש מתיבת דו-שיח או סרגל צד, חזרה אל עורכים ב-Google Docs , Sheets או Forms, פשוט קוראים לשיטה google.script.host.editor.focus() השיטה הזאת שימושית במיוחד בשילוב עם שיטות של שירות מסמכים Document.setCursor(position) וגם Document.setSelection(range).