Usługa HTML: komunikacja z funkcjami serwera

google.script.run to asynchroniczny interfejs API JavaScript po stronie klienta, który umożliwia wywoływanie funkcji Apps Script po stronie serwera przez strony usługi HTML. Poniższy przykład pokazuje najprostszą funkcję google.script.run – wywoływanie funkcji na serwerze z JavaScriptu po stronie klienta.

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>

Jeśli wdrożysz ten skrypt jako aplikację internetową i otworzysz jej adres URL, nic się nie wyświetli. Jednak w logach zobaczysz, że została wywołana funkcja serwera doSomething().

Wywołania funkcji po stronie serwera po stronie klienta są asynchroniczne: gdy przeglądarka poprosi serwer o wykonanie funkcji doSomething(), natychmiast przechodzi do następnego wiersza kodu bez oczekiwania na odpowiedź. Oznacza to, że wywołania funkcji serwera mogą nie być wykonywane w oczekiwanej kolejności. Jeśli wywołasz 2 funkcje w tym samym czasie, nie możesz wiedzieć, która z nich zostanie wykonana jako pierwsza. Wynik może się różnić przy każdym wczytaniu strony. W takiej sytuacji obsługa sukcesuobsługa niepowodzenia pomagają kontrolować przepływ kodu.

Interfejs API google.script.run umożliwia 10 jednoczesnych wywołań funkcji serwera. Jeśli wykonasz 11. wywołanie, gdy 10 jest nadal uruchomionych, funkcja serwera zostanie opóźniona do czasu, aż jedno z 10 miejsc zostanie zwolnione. W praktyce rzadko trzeba się martwić o to ograniczenie, zwłaszcza że większość przeglądarek ogranicza już liczbę jednoczesnych żądań do tego samego serwera do liczby mniejszej niż 10. Na przykład w Firefoksie limit wynosi 6. Większość przeglądarek opóźnia również żądania wysyłane do serwera, dopóki nie zostanie zakończone jedno z dotychczasowych żądań.

Parametry i wartości zwracane

Możesz wywołać funkcję serwera z parametrami z klienta. Podobnie funkcja serwera może zwrócić wartość klientowi jako parametr przekazany do obsługi sukcesu.

Prawidłowe parametry i zwracane wartości to typy proste JavaScriptu, takie jak Number, Boolean, String lub null, a także obiekty i tablice JavaScriptu, które składają się z typów prostych, obiektów i tablic. Element form na stronie może też być parametrem, ale musi być jedynym parametrem funkcji. Nie może być wartością zwracaną. Jeśli spróbujesz przekazać element Date, Function, DOM inny niż form lub inny typ zabroniony, w tym typy zabronione w obiektach lub tablicach, żądanie zakończy się niepowodzeniem. Nie uda się też utworzyć obiektów, które tworzą pętle odwołań, a niezdefiniowane pola w tablicach staną się null.

Pamiętaj, że obiekt przekazany na serwer staje się kopią oryginału. Jeśli funkcja serwera otrzyma obiekt i zmieni jego właściwości, nie wpłynie to na właściwości na kliencie.

Obsługa zdarzeń powodzenia

Ponieważ kod po stronie klienta przechodzi do następnego wiersza bez oczekiwania na zakończenie wywołania po stronie serwera, withSuccessHandler(function) pozwala określić funkcję wywołania zwrotnego po stronie klienta, która zostanie wykonana po otrzymaniu odpowiedzi od serwera. Jeśli funkcja serwera zwraca wartość, interfejs API przekazuje ją nowej funkcji jako parametr.

W tym przykładzie po odpowiedzi serwera wyświetla się alert w przeglądarce. Pamiętaj, że ten przykład kodu wymaga autoryzacji, ponieważ funkcja po stronie serwera uzyskuje dostęp do Twojego konta Gmail. Najprostszym sposobem autoryzacji skryptu jest ręczne uruchomienie funkcji getUnreadEmails() w edytorze skryptów przed załadowaniem strony. Możesz też wdrażać aplikację internetową jako „użytkownik uzyskujący dostęp do aplikacji internetowej”. W tym przypadku podczas wczytywania aplikacji zostanie wyświetlony monit o autoryzację.

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>

Moduły obsługi błędów

Jeśli serwer nie odpowiada lub zgłasza błąd, funkcja withFailureHandler(function) umożliwia określenie modułu obsługi błędów zamiast modułu obsługi powodzenia. Argument Error (jeśli występuje) jest przekazywany jako argument.

Jeśli nie określisz modułu obsługi błędów, domyślnie błędy są rejestrowane w konsoli JavaScript. Aby zastąpić to zachowanie, wywołaj funkcję withFailureHandler(null) lub podaj element obsługi niepowodzenia, który nic nie robi.

Składnia metod obsługi błędów jest prawie identyczna jak w przypadku metod obsługi powodzeń, jak widać w tym przykładzie.

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>

Obiekty użytkownika

Możesz używać tego samego modułu obsługi sukcesu lub błędu w przypadku wielu wywołań serwera. Aby to zrobić, wywołaj funkcję withUserObject(object), aby określić obiekt, który zostanie przekazany do modułu obsługi jako drugi parametr. Ten „obiekt użytkownika” (nie mylić z klasą User) umożliwia reagowanie na kontekst, w którym klient nawiązał połączenie z serwerem. Obiekty użytkownika nie są wysyłane na serwer, więc mogą być dowolne, np. funkcje, elementy DOM itp., bez ograniczeń dotyczących parametrów i wartości zwracanych przez wywołania serwera. Obiekty użytkownika nie mogą jednak być obiektami utworzonymi za pomocą operatora new.

W tym przykładzie kliknięcie jednego z 2 przycisków spowoduje zaktualizowanie tego przycisku za pomocą wartości z serwera, a drugi pozostanie bez zmian, mimo że oba mają ten sam kod obsługi sukcesu. W obiekcie onclick słowo kluczowe this odnosi się do obiektu 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>

Formularze

Jeśli wywołasz funkcję serwera z elementem form jako parametrem, formularz stanie się pojedynczym obiektem z nazwami pól jako kluczami i wartościami pól jako wartościami. Wszystkie wartości są konwertowane na ciągi znaków, z wyjątkiem zawartości pól danych wejściowych pliku, które stają się obiektami Blob.

W tym przykładzie formularz, w tym pole do wprowadzania plików, jest przetwarzany bez ponownego wczytywania strony. Plik jest przesyłany na Dysk Google, a następnie na stronie po stronie klienta jest drukowany adres URL pliku. W obiekcie onsubmit słowo kluczowe this odnosi się do formularza. Pamiętaj, że po załadowaniu strony wszystkie formularze mają domyślne działanie przesyłania wyłączone przez preventFormSubmit. Zapobiega to przekierowywaniu strony na nieprawidłowy adres URL w przypadku wyjątku.

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>

Uruchamiający skrypt

Funkcję google.script.run można traktować jako konstruktor „uruchamiacza skryptu”. Jeśli do uruchamiacza skryptu dodasz obiekt użytkownika, obiekt obsługi sukcesu lub obiekt obsługi błędu, nie zmienisz istniejącego uruchamiacza, tylko uzyskasz nowy uruchamiacz skryptu z nowymi zachowaniami.

Możesz użyć dowolnej kombinacji atrybutów withSuccessHandler(), withFailureHandler()withUserObject() w dowolnej kolejności. Możesz też wywołać dowolną z funkcji modyfikujących w ramach skryptu, który ma już ustawioną wartość. Nowa wartość po prostu zastąpi poprzednią.

W tym przykładzie ustawiono wspólny moduł obsługi błędów dla wszystkich 3 wywołań na serwer, ale 2 oddzielne moduły obsługi powodzenia:

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

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

Funkcje prywatne

Funkcje serwera, których nazwy kończą się znakiem podkreślenia, są uważane za prywatne. Funkcji tych nie można wywołać za pomocą funkcji google.script, a ich nazwy nigdy nie są wysyłane do klienta. Dzięki temu możesz ukryć szczegóły implementacji, które muszą być utrzymywane w tajemnicy na serwerze. google.script nie może też widzieć funkcji w bibliotekach ani funkcji, które nie są zadeklarowane na najwyższym poziomie skryptu.

W tym przykładzie funkcja getBankBalance() jest dostępna w kodzie klienta. Użytkownik, który przejrzy kod źródłowy, może poznać jej nazwę, nawet jeśli jej nie wywołasz. Jednak funkcje deepSecret_()obj.objectMethod() są całkowicie niewidoczne dla klienta.

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>

Zmienianie rozmiaru okien w aplikacji

Rozmiar niestandardowych okienek dialogowych w Dokumentach, Arkuszach lub Formularzach Google można zmienić, wywołując metody google.script.hostsetWidth(width) lub setHeight(height) w kodzie po stronie klienta. (Aby ustawić początkowy rozmiar okna dialogowego, użyj metod HtmlOutput setWidth(width) i setHeight(height)). Pamiętaj, że po zmianie rozmiaru okna dialogowego nie jest ono ponownie wyśrodkowywane w oknie nadrzędnym, a nie można zmieniać rozmiaru pasków bocznych.

Zamknij okna dialogowe i paski boczne w 

Jeśli używasz usługi HTML do wyświetlania okna dialogowego lub paska bocznego w Dokumentach, Arkuszach lub Formularzach Google, nie możesz zamknąć interfejsu, wywołując funkcję window.close(). Zamiast tego musisz zadzwonić na numer google.script.host.close(). Przykład znajdziesz w sekcji Przesyłanie kodu HTML jako interfejsu użytkownika .

Przenoszenie aktywnego obszaru w przeglądarce

Aby w przeglądarce użytkownika przełączyć fokus z okna dialogowego lub paska bocznego z powrotem na edytor Dokumentów, Arkuszy lub Formularzy Google, po prostu wywołaj metodę google.script.host.editor.focus(). Ta metoda jest szczególnie przydatna w połączeniu z metodami usługi Dokumenty Document.setCursor(position)Document.setSelection(range).