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

google.script.run API อนุญาตให้เรียกฟังก์ชันเซิร์ฟเวอร์พร้อมกันได้ 10 รายการ หากคุณโทรครั้งที่ 11 ในขณะที่ยังมีการโทร 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) หรือระบุ ตัวแฮนเดิลข้อผิดพลาดที่ไม่ทำอะไร

ไวยากรณ์สำหรับตัวแฮนเดิลข้อผิดพลาดแทบจะเหมือนกับตัวแฮนเดิลความสำเร็จ ดังที่ตัวอย่างนี้แสดงให้เห็น

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

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

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

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 service Document.setCursor(position) และ Document.setSelection(range)