HTML Hizmeti: Sunucu İşlevleri ile İletişim

google.script.run, HTML hizmeti sayfalarının sunucu tarafı Apps Komut Dosyası işlevlerini çağırmasına olanak tanıyan eşzamansız bir istemci taraflı JavaScript API'dir. Aşağıdaki örnekte google.script.run öğesinin en temel işlevi gösterilmektedir: istemci taraflı JavaScript'ten sunucuda işlev çağırma.

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>

Bu komut dosyasını bir web uygulaması olarak dağıtır ve URL'sini ziyaret ederseniz hiçbir şey görmezsiniz, ancak günlükleri görüntülerseniz doSomething() sunucu işlevinin çağrıldığını görürsünüz.

Sunucu tarafı işlevlere yönelik istemci tarafı çağrılar eşzamansızdır: Tarayıcı, sunucunun doSomething() işlevini çalıştırmasını istedikten sonra, tarayıcı yanıt beklemeden derhal sonraki kod satırına devam eder. Bu, sunucu işlevi çağrılarının beklediğiniz sırayla yürütülemeyeceği anlamına gelir. Aynı anda iki işlev çağrısı yaparsanız önce hangi işlevin çalıştırılacağını bilmek mümkün olmaz; sayfayı her yüklediğinizde sonuç farklılık gösterebilir. Bu durumda, başarı işleyiciler ve hata işleyiciler kodunuzun akışını kontrol etmenize yardımcı olur.

google.script.run API, sunucu işlevlerine 10 eşzamanlı çağrıya izin verir. 10. çağrı hâlâ çalışırken 11. çağrıyı yaparsanız sunucu işlevi, 10 noktadan biri serbest bırakılana kadar geciktirilir. Pratikte, özellikle de çoğu tarayıcı aynı sunucuya eşzamanlı istek sayısını 10'dan düşük bir sayıyla sınırlandırdığından, bu kısıtlamayı çok nadir düşünmeniz gerekir. Örneğin, Firefox'ta bu sınır 6'dır. Benzer şekilde çoğu tarayıcı, aşırı sunucu isteklerini mevcut isteklerden biri tamamlanana kadar geciktirir.

Parametreler ve döndürülen değerler

İstemciden gelen parametrelerle bir sunucu işlevini çağırabilirsiniz. Benzer şekilde, bir sunucu işlevi de başarı işleyiciye iletilen bir parametre olarak istemciye değer döndürebilir.

Yasal parametreler ve döndürülen değerler Number, Boolean, String veya null gibi temel JavaScript öğelerinin yanı sıra temel öğeler, nesneler ve dizilerden oluşan JavaScript nesneleri ve dizileridir. Sayfa içindeki form öğesi de parametre olarak yasaldır ancak işlevin tek parametresi olmalıdır ve döndürülen değer olarak yasal değildir. form dışında bir Date, Function, DOM öğesi veya nesne ya da dizi içindeki yasaklı türler de dahil olmak üzere başka bir yasaklanmış türü geçirmeye çalışırsanız istekler başarısız olur. Döngüsel referanslar oluşturan nesneler de başarısız olur ve dizilerdeki tanımlanmamış alanlar null haline gelir.

Sunucuya iletilen bir nesnenin, orijinalin bir kopyası haline geleceğini unutmayın. Bir sunucu işlevi bir nesne alır ve onun özelliklerini değiştirirse istemcideki özellikler etkilenmez.

Başarı işleyiciler

İstemci tarafı kodu, sunucu çağrısının tamamlanmasını beklemeden sonraki satıra devam ettiğinden withSuccessHandler(function), sunucu yanıt verdiğinde çalışacak bir istemci tarafı geri çağırma işlevi belirtmenize olanak tanır. Sunucu işlevi bir değer döndürürse API, bu değeri yeni işleve parametre olarak iletir.

Aşağıdaki örnekte, sunucu yanıt verdiğinde bir tarayıcı uyarısı gösterilmektedir. Sunucu tarafı işlevi Gmail hesabınıza eriştiğinden bu kod örneğinin yetkilendirme gerektirdiğini unutmayın. Komut dosyasını yetkilendirmenin en basit yolu, sayfayı yüklemeden önce getUnreadEmails() işlevini komut dosyası düzenleyicisinden manuel olarak bir kez çalıştırmaktır. Alternatif olarak, web uygulamasını dağıtırken bunu "web uygulamasına erişen kullanıcı" olarak yürütmeyi de seçebilirsiniz. Bu durumda, uygulamayı yüklerken yetkilendirme istenir.

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>

Hata işleyiciler

Sunucunun yanıt verilmemesi veya hata vermesi durumunda withFailureHandler(function), Error nesnesi (varsa) bağımsız değişken olarak iletilmiş bir başarı işleyici yerine bir hata işleyici belirtmenize olanak tanır.

Varsayılan olarak, bir hata işleyici belirtmezseniz hatalar JavaScript konsoluna kaydedilir. Bunu geçersiz kılmak için withFailureHandler(null) yöntemini çağırın veya hiçbir şey yapmayan bir hata işleyici sağlayın.

Bu örnekte gösterildiği gibi, hata işleyicilerin söz dizimi, başarı işleyiciler ile neredeyse aynıdır.

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>

Kullanıcı nesneleri

İşleyiciye ikinci bir parametre olarak iletilecek bir nesneyi belirtmek için withUserObject(object) çağrısı yaparak sunucuya yapılan birden fazla çağrı için aynı başarı veya hata işleyiciyi yeniden kullanabilirsiniz. User sınıfıyla karıştırılmaması gereken bu "kullanıcı nesnesi", istemcinin sunucuyla iletişim kurduğu bağlama yanıt verebilmenizi sağlar. Kullanıcı nesneleri sunucuya gönderilmediğinden işlevler, DOM öğeleri vb. dahil olmak üzere, parametre kısıtlamaları ve sunucu çağrıları için değer döndürme olmadan neredeyse her şey olabilirler. Ancak kullanıcı nesneleri new operatörüyle oluşturulan nesneler olamaz.

Bu örnekte, iki düğmeden biri tıklandığında o düğme sunucudaki bir değerle güncellenir, tek bir başarılı işleyici paylaşsalar bile diğer düğme değiştirilmeden kalır. onclick işleyicisinde this anahtar kelimesi button öğesinin kendisini ifade etmektedir.

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>

Formlar

Parametre olarak form öğesine sahip bir sunucu işlevini çağırırsanız form, alan adlarını anahtar ve değer olarak alan değerlerini içeren tek bir nesne haline gelir. Blob nesneleri haline gelen dosya girişi alanlarının içerikleri hariç, tüm değerler dizelere dönüştürülür.

Bu örnekte, sayfa yeniden yüklenmeden, dosya girişi alanı da dahil olmak üzere bir form işlenir. Form, dosyayı Google Drive'a yükler ve ardından istemci taraflı sayfada dosyanın URL'sini yazdırır. onsubmit işleyicisinde this anahtar kelimesi, formun kendisini ifade etmektedir. Sayfadaki tüm formlar yüklendiğinde varsayılan gönderme işleminin preventFormSubmit tarafından devre dışı bırakılacağını unutmayın. Bu, bir istisna durumunda sayfanın yanlış bir URL'ye yönlendirmesini önler.

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>

Komut dosyası çalıştırıcıları

google.script.run'yi bir "komut dosyası çalıştırıcısı" oluşturucu olarak düşünebilirsiniz. Bir komut dosyası çalıştırıcıya başarı işleyici, hata işleyici veya kullanıcı nesnesi eklerseniz mevcut çalıştırıcıyı değiştirmiş olmazsınız. Bunun yerine, yeni davranışa sahip yeni bir komut dosyası çalıştırıcısı alırsınız.

withSuccessHandler(), withFailureHandler() ve withUserObject() seçeneklerinden oluşan herhangi bir kombinasyonu veya herhangi bir siparişi kullanabilirsiniz. Önceden belirlenmiş değere sahip bir komut dosyası çalıştırıcısında, değiştirme işlevlerinden herhangi birini de çağırabilirsiniz. Yeni değer, önceki değeri geçersiz kılar.

Bu örnekte, üç sunucu çağrısının tümü için ortak bir hata işleyici, ancak iki ayrı başarı işleyici ayarlanmaktadır:

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

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

Özel işlevler

Adları alt çizgiyle biten sunucu işlevleri gizli olarak kabul edilir. Bu işlevler google.script tarafından çağrılamaz ve adları hiçbir zaman istemciye gönderilmez. Dolayısıyla bunları, sunucuda gizli tutulması gereken uygulama ayrıntılarını gizlemek için kullanabilirsiniz. google.script, kitaplıklar içindeki işlevleri ve komut dosyasının üst düzeyinde bildirilmeyen işlevleri de göremez.

Bu örnekte, getBankBalance() işlevi istemci kodunda kullanılabilmektedir. Kaynak kodunuzu inceleyen bir kullanıcı, siz çağırmasanız bile adını bulabilir. Ancak deepSecret_() ve obj.objectMethod() işlevleri istemci tarafından tamamen görünmez.

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>

Uygulamalardaki iletişim kutuları Google Workspace yeniden boyutlandırılıyor

Google Dokümanlar, E-Tablolar veya Formlar'daki özel iletişim kutuları, istemci taraflı kodda google.script.host yöntemleri setWidth(width) veya setHeight(height) çağrılarak yeniden boyutlandırılabilir. (İletişim kutusunun ilk boyutunu ayarlamak için HtmlOutputsetWidth(width) ve setHeight(height) yöntemlerini kullanın.) İletişim kutuları yeniden boyutlandırıldığında üst pencerede yeniden ortalanmadığını ve kenar çubuklarını yeniden boyutlandırmanın mümkün olmadığını unutmayın.

Google Workspaceiçindeki iletişim kutuları ve kenar çubukları kapatılıyor

Google Dokümanlar, E-Tablolar veya Formlar'da bir iletişim kutusu veya kenar çubuğu görüntülemek için HTML hizmetini kullanıyorsanız window.close() yöntemini çağırarak arayüzü kapatamazsınız. Bunun yerine, google.script.host.close() numaralı telefonu aramanız gerekir. Örneğin, HTML'yi kullanıcı arayüzü Google Workspace olarak sunma ile ilgili bölüme bakın.

Google Workspaceiçinde tarayıcı odağı taşınıyor

Kullanıcının tarayıcısında odağı bir iletişim kutusundan veya kenar çubuğundan Google Dokümanlar, E-Tablolar veya Formlar düzenleyicisine döndürmek için google.script.host.editor.focus() yöntemini çağırmanız yeterlidir. Bu yöntem özellikle Belge hizmeti Document.setCursor(position) ve Document.setSelection(range) yöntemleriyle birlikte kullanıldığında kullanışlıdır.