บริการ 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() เบราว์เซอร์จะทำงานต่อ ไปยังบรรทัดถัดไปของโค้ดได้ทันทีโดยไม่ต้องรอการตอบกลับ ซึ่งหมายความว่าการเรียกใช้ฟังก์ชันเซิร์ฟเวอร์อาจไม่ทํางานตามลําดับที่คุณคาดไว้ หากคุณให้ สองการเรียกใช้ฟังก์ชันพร้อมกัน จะไม่มีทางทราบว่าฟังก์ชันใดจะ แสดงก่อน ผลการค้นหาอาจแตกต่างกันในแต่ละครั้งที่คุณโหลดหน้าเว็บ ในกรณีนี้ เครื่องจัดการสำเร็จและเครื่องจัดการความล้มเหลว จะช่วยควบคุมโฟลว์ของโค้ด

google.script.run API อนุญาตให้มีการเรียกฟังก์ชันของเซิร์ฟเวอร์พร้อมกัน 10 ครั้ง ถ้า คุณเรียกครั้งที่ 11 ในขณะที่เลข 10 ยังทำงานอยู่ ฟังก์ชันของเซิร์ฟเวอร์จะ ล่าช้าจนกว่า 1 ใน 10 สปอตจะว่าง ในทางปฏิบัติ คุณแทบจะไม่มี ให้คำนึงถึงข้อจำกัดนี้ โดยเฉพาะอย่างยิ่ง เนื่องจากเบราว์เซอร์ส่วนใหญ่ได้จำกัด จำนวนคำขอหลายรายการพร้อมกันไปยังเซิร์ฟเวอร์เดียวกันที่น้อยกว่า 10 เช่น ใน Firefox ขีดจํากัดคือ 6 เบราว์เซอร์ส่วนใหญ่มีความล่าช้าในแนวทางเดียวกัน คำขอของเซิร์ฟเวอร์จนกว่าคำขอที่มีอยู่รายการหนึ่งจะเสร็จสมบูรณ์

พารามิเตอร์และค่าที่ส่งคืน

คุณสามารถเรียกใช้ฟังก์ชันเซิร์ฟเวอร์ที่มีพารามิเตอร์จากไคลเอ็นต์ได้ ในทำนองเดียวกัน ฟังก์ชันของเซิร์ฟเวอร์สามารถส่งค่าไปยังไคลเอ็นต์ โดยส่งเป็นพารามิเตอร์ที่ส่งไปยัง เครื่องจัดการสำเร็จ

พารามิเตอร์ทางกฎหมายและค่าที่แสดงผลคือ JavaScript แบบดั้งเดิม เช่น Number Boolean, String หรือ null ตลอดจนออบเจ็กต์และอาร์เรย์ JavaScript ที่ ประกอบด้วยรูปทรงพื้นฐาน ออบเจ็กต์ และอาร์เรย์ องค์ประกอบ form ภายในหน้าเว็บ ก็สามารถเป็นพารามิเตอร์ทางกฎหมายได้เช่นกัน แต่จะต้องเป็นพารามิเตอร์เดียวของฟังก์ชัน และ จะถือว่าไม่มีความผิดทางกฎหมายที่ใช้เป็นมูลค่าการแสดงผล คำขอจะดำเนินการไม่สำเร็จหากคุณพยายามส่ง Date, Function, องค์ประกอบ DOM นอกเหนือจาก form หรือประเภทอื่นๆ ที่ไม่ได้รับอนุญาต รวมถึงประเภทที่ไม่ได้รับอนุญาตภายในออบเจ็กต์หรืออาร์เรย์ ออบเจ็กต์ที่สร้างการอ้างอิงแบบวนซ้ำก็จะไม่สำเร็จเช่นกัน และฟิลด์ที่ไม่มีการกําหนดภายในอาร์เรย์จะกลายเป็นnull

โปรดทราบว่าออบเจ็กต์ที่ส่งผ่านไปยังเซิร์ฟเวอร์จะกลายเป็นสำเนาของต้นฉบับ หากมี ฟังก์ชันของเซิร์ฟเวอร์ได้รับออบเจ็กต์และเปลี่ยนคุณสมบัติ รวมถึงพร็อพเพอร์ตี้ใน จะไม่ได้รับผลกระทบ

ตัวจัดการความสำเร็จ

เนื่องจากโค้ดฝั่งไคลเอ็นต์จะไปยังบรรทัดถัดไปโดยไม่ต้องรอการเรียกใช้เซิร์ฟเวอร์ให้เสร็จสมบูรณ์ withSuccessHandler(function) จึงให้คุณระบุฟังก์ชันการเรียกกลับฝั่งไคลเอ็นต์ให้ทำงานเมื่อเซิร์ฟเวอร์ตอบกลับ หากฟังก์ชันของเซิร์ฟเวอร์แสดงผลค่า API จะส่งค่าไปยัง ฟังก์ชันใหม่เป็นพารามิเตอร์

ตัวอย่างต่อไปนี้จะแสดงการแจ้งเตือนของเบราว์เซอร์เมื่อเซิร์ฟเวอร์ตอบสนอง โปรดทราบว่าตัวอย่างโค้ดนี้ต้องมีการให้สิทธิ์เนื่องจากฟังก์ชันฝั่งเซิร์ฟเวอร์จะเข้าถึงบัญชี Gmail ของคุณ วิธีที่ง่ายที่สุดในการให้สิทธิ์สคริปต์คือการเรียกใช้ฟังก์ชัน getUnreadEmails() ด้วยตนเองจากเครื่องมือแก้ไขสคริปต์ 1 ครั้งก่อนโหลดหน้าเว็บ หรือเมื่อคุณ ทำให้เว็บแอปใช้งานได้ คุณสามารถเลือก เพื่อเรียกใช้เป็น "ผู้ใช้ที่เข้าถึงเว็บแอป" ซึ่งในกรณีนี้คุณจะต้อง เพื่อรับการให้สิทธิ์เมื่อโหลดแอป

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) ช่วยให้คุณระบุเครื่องจัดการความล้มเหลวแทนเครื่องจัดการสำเร็จด้วย Error ออบเจ็กต์ (หากมี) ที่ส่งผ่านเป็นอาร์กิวเมนต์

โดยค่าเริ่มต้น ถ้าคุณไม่ได้ระบุเครื่องจัดการความล้มเหลว ความล้มเหลวจะถูกบันทึกลงใน คอนโซล JavaScript หากต้องการลบล้างค่านี้ ให้เรียกใช้ withFailureHandler(null) หรือSupply เครื่องจัดการความล้มเหลวที่ไม่ดำเนินการใดๆ

รูปแบบคำสั่งสำหรับตัวแฮนเดิลการทำงานที่ไม่สําเร็จเกือบจะเหมือนกับตัวแฮนเดิลการทำงานสําเร็จ ดังที่ตัวอย่างนี้แสดง

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>

ออบเจ็กต์ผู้ใช้

คุณใช้ตัวแฮนเดิลสําหรับความสําเร็จหรือความล้มเหลวเดียวกันซ้ำได้สําหรับการเรียกใช้เซิร์ฟเวอร์หลายครั้งโดยเรียกใช้ withUserObject(object) เพื่อระบุออบเจ็กต์ที่จะส่งไปยังตัวแฮนเดิลเป็นพารามิเตอร์ที่ 2 "ออบเจ็กต์ผู้ใช้" นี้ โปรดอย่าสับสนกับ ชั้นเรียน User — ช่วยให้คุณตอบกลับ บริบทที่ไคลเอ็นต์ติดต่อกับเซิร์ฟเวอร์ เนื่องจากออบเจ็กต์ผู้ใช้จะไม่ ไปยังเซิร์ฟเวอร์ ซึ่งสามารถเป็นอะไรก็ได้ รวมถึงฟังก์ชัน, DOM และอื่นๆ โดยไม่มีข้อจำกัดเกี่ยวกับพารามิเตอร์และค่าที่ส่งกลับ สำหรับการเรียกเซิร์ฟเวอร์ อย่างไรก็ตาม ออบเจ็กต์ผู้ใช้ต้องไม่ใช่ออบเจ็กต์ที่สร้างด้วยโอเปอเรเตอร์ new

ในตัวอย่างนี้ การคลิกปุ่มใดปุ่มหนึ่งจะอัปเดตปุ่มนั้นด้วยค่าจากเซิร์ฟเวอร์ โดยไม่ทําให้ปุ่มอื่นเปลี่ยนแปลง แม้ว่าจะใช้ตัวแฮนเดิลสําเร็จเดียวกันก็ตาม ภายในเครื่องจัดการ 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 ไดรฟ์ แล้วพิมพ์ URL สำหรับ ในหน้าฝั่งไคลเอ็นต์ ภายในเครื่องจัดการ onsubmit คีย์เวิร์ด this จะหมายถึงตัวแบบฟอร์มนั้นๆ โปรดทราบว่าเมื่อโหลดฟอร์มทั้งหมดในหน้าเว็บ การส่งโดยค่าเริ่มต้นถูกปิดใช้งานโดย preventFormSubmit ซึ่งจะป้องกันไม่ให้หน้าเว็บเปลี่ยนเส้นทางไปยัง URL ที่ไม่ถูกต้องในกรณีที่มีข้อยกเว้น

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() คุณยังเรียกใช้บริการ การแก้ไขฟังก์ชันในโปรแกรมเรียกใช้สคริปต์ที่มีการตั้งค่าไว้แล้ว ฟิลด์ จะลบล้างค่าก่อนหน้าเท่านั้น

ตัวอย่างนี้ตั้งค่าเครื่องจัดการความล้มเหลวที่พบบ่อยสำหรับการเรียกเซิร์ฟเวอร์ทั้ง 3 ครั้ง แต่ เครื่องจัดการความสำเร็จแยกต่างหาก

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 เอกสาร, ชีต หรือฟอร์มได้โดยเรียกใช้เมธอด google.script.host setWidth(width) หรือ setHeight(height) ในโค้ดฝั่งไคลเอ็นต์ (หากต้องการตั้งค่าขนาดเริ่มต้นของกล่องโต้ตอบ ให้ใช้ปุ่ม HtmlOutput วิธีการ setWidth(width) และ setHeight(height)) โปรดทราบว่า กล่องโต้ตอบจะไม่จัดกึ่งกลางใหม่ในหน้าต่างระดับบนสุดเมื่อปรับขนาด และ ไม่สามารถปรับขนาดแถบด้านข้าง

การปิดกล่องโต้ตอบและแถบด้านข้างใน Google Workspace

หากคุณใช้บริการ HTML เพื่อแสดง กล่องโต้ตอบหรือแถบด้านข้างใน Google เอกสาร, ชีต หรือ ฟอร์ม คุณไม่สามารถปิดอินเทอร์เฟซด้วยการเรียกใช้ window.close() แต่คุณ ต้องเรียก google.script.host.close() ดูตัวอย่างได้ที่ส่วน การแสดง HTML เป็น Google Workspace อินเทอร์เฟซผู้ใช้

การย้ายโฟกัสของเบราว์เซอร์ใน Google Workspace

หากต้องการสลับโฟกัสในเบราว์เซอร์ของผู้ใช้จากกล่องโต้ตอบหรือแถบด้านข้างกลับไปยัง เครื่องมือแก้ไขของ Google เอกสาร, ชีต หรือฟอร์ม เพียงแค่เรียกเมธอด google.script.host.editor.focus() วิธีการนี้มีประโยชน์อย่างยิ่งเมื่อใช้ร่วมกับ วิธีการในบริการเอกสาร Document.setCursor(position) และ Document.setSelection(range)