Layanan HTML: Berkomunikasi dengan Fungsi Server

google.script.run adalah model asinkron JavaScript API sisi klien yang memungkinkan halaman layanan HTML memanggil sisi server Fungsi Apps Script. Contoh berikut menunjukkan fungsi yang paling dasar dari google.script.runmemanggil fungsi di server dari JavaScript sisi klien.

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>

Jika men-deploy skrip ini sebagai aplikasi web dan membuka URL-nya, Anda tidak akan melihat apa pun, tetapi jika melihat log, Anda akan melihat bahwa fungsi server doSomething() dipanggil.

Panggilan sisi klien ke fungsi sisi server bersifat asinkron: setelah browser meminta agar server menjalankan fungsi doSomething(), browser akan melanjutkan langsung ke baris kode berikutnya tanpa menunggu respons. Artinya, panggilan fungsi server mungkin tidak dieksekusi dalam urutan yang Anda harapkan. Jika Anda membuat dua panggilan fungsi secara bersamaan, tidak ada cara untuk mengetahui fungsi mana jalankan terlebih dahulu; hasilnya mungkin berbeda setiap kali Anda memuat halaman. Dalam situasi ini, pengendali keberhasilan dan pengendali kegagalan membantu mengontrol alur kode Anda.

google.script.run API memungkinkan 10 panggilan serentak ke fungsi server. Jika Anda melakukan panggilan ke-11 saat 10 masih berjalan, fungsi server akan ditunda sampai salah satu dari 10 titik kosong. Dalam praktiknya, Anda jarang harus memikirkan batasan ini, terutama karena sebagian besar browser sudah membatasi jumlah permintaan serentak ke server yang sama dengan jumlah yang lebih rendah dari 10. Di Firefox, misalnya, batasnya adalah 6. Sebagian besar browser juga menunda permintaan server yang berlebih hingga salah satu permintaan yang ada selesai.

Parameter dan nilai yang ditampilkan

Anda dapat memanggil fungsi server dengan parameter dari klien. Demikian pula, fungsi server dapat mengembalikan nilai ke klien sebagai parameter yang diteruskan ke pengendali keberhasilan.

Parameter resmi dan nilai yang ditampilkan adalah primitif JavaScript seperti Number, Boolean, String, atau null, serta objek dan array JavaScript yang terdiri dari primitif, objek, dan array. Elemen form di dalam halaman juga legal sebagai parameter, tetapi harus menjadi satu-satunya parameter fungsi, dan itu tidak sah sebagai nilai hasil. Permintaan akan gagal jika Anda mencoba meneruskan Date, Function, elemen DOM selain form, atau jenis terlarang lainnya, termasuk jenis yang dilarang di dalam objek atau array. Objek yang membuat referensi melingkar juga akan gagal, dan kolom yang tidak ditentukan dalam array menjadi null.

Perhatikan bahwa objek yang diteruskan ke server menjadi salinan dari objek asli. Jika server menerima objek dan mengubah propertinya, properti pada klien tidak terpengaruh.

Pengendali berhasil

Karena kode sisi klien berlanjut ke baris berikutnya tanpa menunggu server untuk menyelesaikan panggilan, withSuccessHandler(function) memungkinkan Anda menentukan fungsi callback sisi klien yang akan dijalankan saat merespons. Jika fungsi server menampilkan nilai, API akan meneruskan nilai tersebut ke fungsi baru sebagai parameter.

Contoh berikut menampilkan peringatan browser saat server merespons. Catatan bahwa contoh kode ini memerlukan otorisasi karena fungsi sisi server yang mengakses akun Gmail Anda. Cara paling sederhana untuk memberikan otorisasi pada skrip adalah dengan menjalankan fungsi getUnreadEmails() secara manual dari editor skrip satu kali sebelum Anda memuat halaman. Atau, ketika Anda men-deploy aplikasi web, Anda dapat memilih untuk menjalankannya sebagai "pengguna yang mengakses aplikasi web," dalam hal ini Anda akan akan diminta untuk melakukan otorisasi saat Anda memuat aplikasi.

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>

Pengendali kegagalan

Jika server gagal merespons atau menampilkan pesan {i>error<i}, withFailureHandler(function) memungkinkan Anda menentukan pengendali kegagalan, alih-alih pengendali berhasil, dengan Error (jika ada) yang diteruskan sebagai argumen.

Secara default, jika Anda tidak menetapkan pengendali kegagalan, kegagalan akan dicatat ke Konsol JavaScript. Untuk menggantinya, panggil withFailureHandler(null) atau berikan pengendali kegagalan yang tidak melakukan apa pun.

Sintaksis untuk pengendali kegagalan hampir sama dengan pengendali keberhasilan, seperti yang ditunjukkan contoh ini.

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>

Objek pengguna

Anda bisa menggunakan kembali pengendali yang berhasil atau gagal yang sama untuk beberapa panggilan ke server dengan memanggil withUserObject(object) untuk menentukan objek yang akan diteruskan ke pengendali sebagai parameter kedua. “Objek pengguna” ini — jangan tertukar dengan User — memungkinkan Anda merespons di mana klien menghubungi server. Karena objek pengguna tidak dikirim ke server, mereka bisa apa saja, termasuk fungsi, DOM elemen, dan seterusnya, tanpa batasan parameter dan nilai yang ditampilkan untuk panggilan server. Namun, objek pengguna tidak boleh berupa objek yang dibuat dengan Operator new.

Dalam contoh ini, mengklik salah satu dari dua tombol akan memperbarui tombol tersebut dengan nilai dari server tanpa mengubah tombol lainnya, meskipun tombol tersebut memiliki satu pengendali yang berhasil. Di dalam pengendali onclick, kata kunci this merujuk pada button itu sendiri.

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>

Formulir

Jika Anda memanggil fungsi server dengan elemen form sebagai parameter, formulir menjadi objek tunggal dengan nama kolom sebagai kunci dan nilai kolom sebagai nilai. Tujuan semua nilai dikonversi menjadi string, kecuali untuk isi input kolom, yang menjadi objek Blob.

Contoh ini memproses formulir, termasuk kolom input file, tanpa memuat ulang halaman; file diupload ke Google Drive dan kemudian mencetak URL untuk di halaman sisi klien. Di dalam pengendali onsubmit, kata kunci this mengacu pada formulir itu sendiri. Perhatikan bahwa setelah memuat, semua formulir di halaman memiliki tindakan kirim default dinonaktifkan oleh preventFormSubmit. Hal ini mencegah halaman agar tidak mengarah ke URL yang tidak akurat jika terjadi pengecualian.

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>

Runner skrip

Anda dapat menganggap google.script.run sebagai builder untuk "pelari skrip". Jika Anda menambahkan pengendali berhasil, pengendali kegagalan, atau objek pengguna ke runner skrip, tidak mengubah runner yang ada; sebagai gantinya, Anda mendapatkan kembali runner skrip baru dengan perilaku baru.

Anda dapat menggunakan kombinasi apa pun dan berapa pun urutan withSuccessHandler(), withFailureHandler(), dan withUserObject(). Anda juga dapat memanggil memodifikasi fungsi pada runner skrip yang sudah memiliki nilai yang ditetapkan. Yang baru mengganti nilai sebelumnya.

Contoh ini menetapkan pengendali kegagalan umum untuk ketiga panggilan server, tetapi dua pengendali keberhasilan terpisah:

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

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

Fungsi pribadi

Fungsi server yang namanya diakhiri dengan garis bawah dianggap pribadi. Fungsi ini tidak dapat dipanggil oleh google.script dan namanya tidak pernah dikirim ke klien. Dengan demikian, Anda dapat menggunakannya untuk menyembunyikan detail implementasi yang perlu dirahasiakan di server. google.script juga tidak dapat melihat fungsi dalam library dan fungsi yang tidak yang dideklarasikan di tingkat atas skrip.

Dalam contoh ini, fungsi getBankBalance() tersedia di klien kode; pengguna yang memeriksa kode program Anda dapat menemukan namanya bahkan jika Anda jangan menyebutnya. Namun, fungsi deepSecret_() dan obj.objectMethod() sama sekali tidak terlihat oleh klien.

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>

Mengubah ukuran dialog di Google Workspace aplikasi

Kotak dialog kustom di Google Dokumen, Spreadsheet, atau Formulir dapat diubah ukurannya dengan memanggil Metode google.script.host setWidth(width) atau setHeight(height) dalam kode sisi klien. (Untuk menyetel ukuran awal dialog, gunakan HtmlOutput metode setWidth(width) dan setHeight(height).) Perhatikan, dialog tidak dipusatkan lagi di jendela induk saat diubah ukurannya, dan tidak mungkin mengubah ukuran sidebar.

Menutup dialog dan sidebar di Google Workspace

Jika Anda menggunakan layanan HTML untuk menampilkan kotak dialog atau sidebar di Google Dokumen, Spreadsheet, atau Formulir, Anda tidak dapat menutup antarmuka dengan memanggil window.close(). Sebaliknya, Anda dapat harus memanggil google.script.host.close() Untuk contohnya, lihat bagian tentang menayangkan HTML sebagai Google Workspace antarmuka pengguna.

Memindahkan fokus browser di Google Workspace

Untuk mengalihkan fokus di browser pengguna dari dialog atau sidebar kembali ke Editor Google Dokumen, Spreadsheet, atau Formulir, cukup panggil metode google.script.host.editor.focus() Metode ini sangat berguna jika dikombinasikan dengan Metode Layanan dokumen Document.setCursor(position) dan Document.setSelection(range)