Usługa HTML: komunikacja z funkcjami serwera

google.script.run to asynchroniczny interfejs API JavaScript 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 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 zobaczysz, ale przeglądając logi, zobaczysz, że wywołano funkcję serwera doSomething().

Wywołania funkcji po stronie klienta są asynchroniczne: gdy przeglądarka zażąda, by serwer uruchomił funkcję doSomething(), przeglądarka przechodzi od razu do następnego wiersza kodu, nie czekając na odpowiedź. Oznacza to, że wywołania funkcji serwera mogą nie być wykonywane w oczekiwanej kolejności. Jeśli wykonujesz 2 wywołania funkcji jednocześnie, nie da się przewidzieć, 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 google.script.run API zezwala na 10 równoczesnych wywołań funkcji serwera. Jeśli 11 wywołanie zostanie wykonane, a 10 jest wciąż uruchomionych, funkcja serwera będzie opóźniona do momentu zwolnienia jednego z 10 pozycji. W praktyce rzadko trzeba o tym pamiętać, zwłaszcza że większość przeglądarek już teraz ogranicza liczbę żądań równoczesnych do jednego serwera do 10. Na przykład w przeglądarce Firefox limit wynosi 6. Większość przeglądarek w podobny sposób opóźnia nadmiarowe żądania serwerów do momentu zrealizowania jednego z istniejących żądań.

Parametry i zwracane wartości

Możesz wywołać funkcję serwera z parametrami od 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 elementy podstawowe JavaScript, takie jak Number, Boolean, String i null, a także obiekty i tablice JavaScriptu składające się 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ć zwracaną wartością. Żądania kończą się niepowodzeniem, jeśli próbujesz przekazać element Date, Function, element DOM poza obiektem form lub innym zabronionym typem, w tym zabronionymi typami w obiektach lub tablicach. Obiekty, które tworzą odwołania kołowe, również przestaną działać, a niezdefiniowane pola w tablicach staną się wartością null.

Pamiętaj, że obiekt przekazany do serwera 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 klienta.

Moduły obsługi sukcesu

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

Przykład poniżej wyświetla alert przeglądarki po odpowiedzi serwera. Ten przykładowy kod 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 wczytaniem strony. Możesz też wdrażać aplikację internetową 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

Na wypadek, gdyby serwer nie odpowiadał lub zgłosił błąd, withFailureHandler(function) umożliwia określenie modułu obsługi awarii zamiast modułu obsługi powodzenia, a obiekt Error (jeśli jest) przekazywany jako argument.

Jeśli nie określisz modułu obsługi błędów, błędy będą domyślnie rejestrowane w konsoli JavaScriptu. Aby go zastąpić, wywołaj withFailureHandler(null) lub podaj moduł obsługi błędów, który nie wykonuje żadnych działań.

Składnia modułów obsługi błędów jest niemal identyczna 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żyć tego samego modułu obsługi powodzenia lub niepowodzenia w wielu wywołaniach serwera, wywołując metodę withUserObject(object) w celu określenia obiektu, który będzie przekazywany do modułu obsługi jako drugi parametr. Ten „obiekt użytkownika” (nie należy mylić z klasą User) pozwala odpowiadać na kontekst, w którym klient skontaktował się z serwerem. Obiekty użytkowników nie są wysyłane na serwer, dlatego mogą to być prawie wszystko, w tym funkcje, elementy DOM itp. bez ograniczeń dotyczących parametrów i zwracania wartości w przypadku wywołań 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 go o wartość z serwera, a drugi bez zmian, mimo że są one współużytkowane przez jeden moduł obsługi powodzenia. Słowo kluczowe this wewnątrz modułu obsługi onclick odnosi się do samego 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 staje się pojedynczym obiektem z nazwami pól jako kluczami, a wartości pól jako wartościami. Wszystkie wartości są konwertowane na ciągi tekstowe z wyjątkiem zawartości pól wejściowych, które stają się obiektami Blob.

Ten przykład przetwarza formularz, w tym pole do wprowadzania pliku, bez ponownego wczytywania strony; przesyła plik na Dysk Google, a następnie drukuje URL pliku 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 domyślne działanie związane z przesyłaniem zostanie wyłączone przez preventFormSubmit. Zapobiega to przekierowywaniu strony na nieprawidłowy adres URL w przypadku wystąpienia 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>

Uruchomienia skryptu

google.script.run można uznać za konstruktora do uruchamiania skryptów. Dodanie modułu obsługi sukcesu, modułu obsługi awarii lub obiektu użytkownika do mechanizmu uruchamiania skryptu nie spowoduje zmiany istniejącego mechanizmu uruchamiania skryptu – zamiast tego otrzymasz nowy mechanizm uruchamiający skrypt z nowym sposobem działania.

Możesz użyć dowolnej kombinacji i dowolnej kolejności withSuccessHandler(), withFailureHandler() i withUserObject(). Możesz też wywołać dowolną funkcję modyfikującą w programie uruchamiającym skrypt, który ma już ustawioną wartość. Nowa wartość 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. Tych funkcji nie można wywoływać przez google.script, a ich nazwy nigdy nie są wysyłane do klienta. Dzięki temu można ich używać do ukrycia szczegółów implementacji, które muszą być ukryte na serwerze. google.script nie widzi też 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 analizuje kod źródłowy, może poznać jej nazwę, nawet jeśli jej nie wywołasz. Funkcje deepSecret_() i obj.objectMethod() są jednak 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>

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.hostsetWidth(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 nie są wyśrodkowane w oknie nadrzędnym i nie można zmienić rozmiaru pasków bocznych.

Zamykam okna i paski boczne w aplikacji Google Workspace

Jeśli korzystasz z 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 metodę window.close(). Musisz wywołać metodę google.script.host.close(). Przykład znajdziesz w sekcji poświęconej wyświetlaniu kodu HTML jako Google Workspace interfejsowi.

Przenoszę zaznaczenie przeglądarki w Google Workspace

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