Usługa HTML: komunikacja z funkcjami serwera

google.script.run to asynchroniczny interfejs API JavaScript działający po stronie klienta, który umożliwia stronom usługi HTML wywoływanie funkcji Apps Script po stronie serwera. Poniższy przykład przedstawia najbardziej podstawowe funkcje funkcji google.script.runwywoływanie funkcji na serwerze z kodu JavaScript 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 jego adres URL, nic nie będzie widoczne, ale jeśli wyświetlisz logi, zobaczysz, że została wywołana funkcja serwera doSomething().

Wywołania funkcji po stronie klienta do funkcji po stronie serwera są asynchroniczne: gdy przeglądarka zażąda uruchomienia funkcji doSomething(), przeglądarka przechodzi od razu 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 jednocześnie, nie da się stwierdzić, która z nich zostanie uruchomiona jako pierwsza. Wynik może się różnić przy każdym wczytaniu strony. W takiej sytuacji moduły obsługi sukcesu i moduły obsługi błędów pomagają kontrolować przepływ kodu.

Interfejs API google.script.run umożliwia 10 równoczesnych wywołań funkcji serwera. Jeśli wykonasz 11 wywołanie, gdy 10 z nich będzie nadal uruchomionych, funkcja serwera będzie opóźniona do czasu zwolnienia jednego z 10 miejsc. W praktyce nie jest to zbyt dużym problemem, zwłaszcza że większość przeglądarek już teraz ogranicza liczbę jednoczesnych żądań do tego samego serwera do wartości mniejszej niż 10. Na przykład w przeglądarce Firefox limit wynosi 6. Większość przeglądarek w podobny sposób opóźnia nadmiarowe żądania serwera do momentu zakończenia jednego z istniejących żądań.

Parametry i zwracane wartości

Możesz wywołać funkcję serwera za pomocą parametrów z klienta. Podobnie funkcja serwera może zwrócić wartość klientowi jako parametr przekazywany do modułu obsługi powodzenia.

Parametry prawne i wartości zwracane to podstawowe elementy JavaScriptu, np. Number, Boolean, String lub null, a także obiekty i tablice JavaScript złożone z elementów podstawowych, obiektów i tablic. Element form na stronie też jest dozwolony jako parametr, ale musi być jedynym parametrem funkcji i nie może być zwracany jako wartość zwracana. Żądania kończą się niepowodzeniem, jeśli próbujesz przekazać element Date, Function lub DOM inny niż form albo inny zabroniony typ, w tym niedozwolone typy w obiektach lub tablicach. Obiekty tworzące odwołania okrągłe również będą działać nieprawidłowo, a niezdefiniowane pola w tablicach staną się null.

Zauważ, że obiekt przekazywany do serwera staje się kopią oryginału. Jeśli funkcja serwera odbiera obiekt i zmienia jego właściwości, nie ma to wpływu na właściwości klienta.

Moduły obsługi sukcesu

Ponieważ kod po stronie klienta przechodzi do następnego wiersza bez oczekiwania na wykonanie wywołania serwera, withSuccessHandler(function) umożliwia określenie funkcji wywołania zwrotnego po stronie klienta, która ma być wykonywana po odpowiedzi serwera. Jeśli funkcja serwera zwraca wartość, interfejs API przekazuje ją jako parametr do nowej funkcji.

Poniższy przykład pokazuje alert przeglądarki, gdy serwer odpowiada. Ten przykładowy kod wymaga autoryzacji, bo 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 wczytaniem strony. Jeśli wdrażasz aplikację internetową, możesz też uruchomić ją jako „użytkownik uzyskujący dostęp do aplikacji internetowej”. W takim przypadku podczas wczytywania aplikacji pojawi się prośba 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 odpowie lub zwróci błąd, withFailureHandler(function) umożliwia określenie modułu obsługi błędów zamiast modułu obsługi powodzenia za pomocą obiektu Error (jeśli występuje) przekazywany jako argument.

Domyślnie, jeśli nie określisz modułu obsługi błędów, błędy będą rejestrowane w konsoli JavaScriptu. Aby to zastąpić, wywołaj withFailureHandler(null) lub podaj moduł obsługi błędów, który nic nie robi.

Składnia modułów obsługi błędów jest prawie taka sama jak 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 powodzenia lub niepowodzenia w wielu wywołaniach serwera. W tym celu wywołaj metodę withUserObject(object), aby określić obiekt, który będzie przekazywany jako drugi parametr. Ten „obiekt użytkownika” – nie należy mylić z klasą User – umożliwia reagowanie w kontekście, w którym klient skontaktował się z serwerem. Obiekty użytkownika nie są wysyłane do serwera, więc mogą być niemal wszystkim, w tym funkcjami, elementami DOM itp. bez ograniczeń w parametrach i zwracanych wartościach wywołań serwera. Obiekty użytkownika nie mogą być jednak obiektami utworzonymi za pomocą operatora new.

W tym przykładzie kliknięcie jednego z tych przycisków spowoduje zaktualizowanie go o wartość z serwera, a drugi przycisk bez zmian, mimo że ma wspólny moduł obsługi powodzenia. Słowo kluczowe this w module obsługi onclick odnosi się do samego elementu 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 parametrem form, formularz staje się pojedynczym obiektem z nazwami pól jako kluczami, a wartościami polami jako wartościami. Wszystkie wartości są konwertowane na ciągi znaków, z wyjątkiem zawartości pól file-input, które stają się obiektami Blob.

W tym przykładzie przetwarzamy formularz, w tym pole pliku do wprowadzania danych, bez ponownego wczytywania strony. Plik jest przesyłany na Dysk Google, a potem drukowany jest na stronie po stronie klienta. Słowo kluczowe this w module obsługi onsubmit odnosi się do samego formularza. Pamiętaj, że po wczytaniu wszystkich formularzy na stronie preventFormSubmit domyślnie wyłączy funkcję przesyłania. 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>

Uruchomione skrypty

google.script.run jest narzędziem do tworzenia skryptu. Jeśli dodasz do niego moduł obsługi powodzenia, moduł obsługi błędów lub obiekt użytkownika, nie zmienisz istniejącego mechanizmu uruchamiania – zamiast tego otrzymasz nowy mechanizm uruchamiający skrypt z nowym działaniem.

Możesz użyć dowolnej kombinacji i dowolnej kolejności atrybutów withSuccessHandler(), withFailureHandler() i withUserObject(). Możesz też wywołać dowolną funkcję modyfikującą w uruchomieniu skryptu, który ma już ustawioną wartość. Nowa wartość po prostu zastępuje poprzednią.

W tym przykładzie ustawiany jest wspólny moduł obsługi błędów dla wszystkich 3 wywołań serwera, ale 2 osobne 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ę podkreśleniem, są uznawane za prywatne. google.script nie może ich wywoływać, a ich nazwy nigdy nie są wysyłane do klienta. Dzięki temu możesz ich używać do ukrywania szczegółów implementacji, które muszą być poufne na serwerze. google.script nie widzi też funkcji w bibliotekach ani w funkcjach, które nie są zadeklarowane na najwyższym poziomie skryptu.

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

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>

Zmiana rozmiaru okien Google Workspace w aplikacjach

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

Zamykam okna dialogowe i paski boczne w Google Workspace

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 wywołać google.script.host.close(). Przykład znajdziesz w sekcji o wyświetlaniu kodu HTML jako Google Workspace interfejsu użytkownika.

Przenoszenie fokusu przeglądarki za Google Workspace

Aby przełączyć zaznaczenie w przeglądarce użytkownika z okna dialogowego lub paska bocznego z powrotem do edytora 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 dokumentów Document.setCursor(position) i Document.setSelection(range).