HTML-Dienst: Mit Serverfunktionen kommunizieren

google.script.run ist eine asynchrone clientseitige JavaScript API, mit der HTML-Dienstseiten serverseitige Apps Script-Funktionen aufrufen können. Das folgende Beispiel zeigt die grundlegendsten Funktionen von google.script.run: das Aufrufen einer Funktion auf dem Server über clientseitiges JavaScript.

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>

Wenn Sie dieses Skript als Webanwendung bereitstellen und dessen URL aufrufen, sehen Sie nichts. In den Logs sehen Sie jedoch, dass die Serverfunktion doSomething() aufgerufen wurde.

Clientseitige Aufrufe serverseitiger Funktionen sind asynchron: Wenn der Browser die Ausführung der Funktion doSomething() vom Server anfordert, fährt der Browser sofort mit der nächsten Codezeile fort, ohne auf eine Antwort zu warten. Das bedeutet, dass Serverfunktionsaufrufe möglicherweise nicht in der erwarteten Reihenfolge ausgeführt werden. Wenn Sie zwei Funktionsaufrufe gleichzeitig ausführen, gibt es keine Möglichkeit zu wissen, welche Funktion zuerst ausgeführt wird. Das Ergebnis kann bei jedem Laden der Seite abweichen. In diesem Fall helfen Erfolgs-Handler und Fehler-Handler, den Codefluss zu steuern.

Die google.script.run API ermöglicht zehn gleichzeitige Aufrufe von Serverfunktionen. Wenn Sie einen elften Aufruf ausführen, während 10 noch ausgeführt wird, verzögert sich die Serverfunktion, bis einer der 10 Plätze freigegeben wurde. In der Praxis sollten Sie über diese Einschränkung nur selten nachdenken, insbesondere da die meisten Browser die Anzahl gleichzeitiger Anfragen an denselben Server bereits auf weniger als 10 begrenzen. In Firefox zum Beispiel beträgt die Beschränkung 6. Die meisten Browser verzögern ähnliche Serveranfragen, bis eine der vorhandenen Anfragen abgeschlossen ist.

Parameter und Rückgabewerte

Sie können eine Serverfunktion mit Parametern aus dem Client aufrufen. Ebenso kann eine Serverfunktion einen Wert als Parameter, der an einen Erfolgs-Handler übergeben wird, an den Client zurückgeben.

Zulässige Parameter und Rückgabewerte sind JavaScript-Primitive wie Number, Boolean, String oder null sowie JavaScript-Objekte und -Arrays, die aus Primitiven, Objekten und Arrays bestehen. Ein form-Element innerhalb der Seite ist auch als Parameter zulässig, muss aber der einzige Parameter der Funktion sein. Es ist nicht als Rückgabewert zulässig. Anfragen schlagen fehl, wenn Sie versuchen, ein Date-, Function- oder DOM-Element neben einem form-Typ oder einem anderen unzulässigen Typ zu übergeben, einschließlich unzulässiger Typen in Objekten oder Arrays. Objekte, die kreisförmige Verweise erstellen, schlagen ebenfalls fehl und nicht definierte Felder innerhalb von Arrays werden zu null.

Beachten Sie, dass ein an den Server übergebenes Objekt zu einer Kopie des Originals wird. Wenn eine Serverfunktion ein Objekt empfängt und seine Attribute ändert, sind die Attribute auf dem Client nicht betroffen.

Erfolgs-Handler

Da clientseitiger Code mit der nächsten Zeile fortgesetzt wird, ohne auf den Abschluss eines Serveraufrufs zu warten, können Sie mit withSuccessHandler(function) eine clientseitige Callback-Funktion angeben, die ausgeführt wird, wenn der Server antwortet. Wenn die Serverfunktion einen Wert zurückgibt, übergibt die API den Wert als Parameter an die neue Funktion.

Im folgenden Beispiel wird eine Browserwarnung angezeigt, wenn der Server antwortet. Für dieses Codebeispiel ist eine Autorisierung erforderlich, da die serverseitige Funktion auf Ihr Gmail-Konto zugreift. Die einfachste Möglichkeit, das Skript zu autorisieren, besteht darin, die Funktion getUnreadEmails() einmal manuell über den Skripteditor auszuführen, bevor Sie die Seite laden. Wenn Sie die Webanwendung bereitstellen, können Sie sie alternativ als den Nutzer ausführen, der auf die Webanwendung zugreift. In diesem Fall werden Sie beim Laden der Anwendung zur Autorisierung aufgefordert.

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>

Fehler-Handler

Falls der Server nicht antwortet oder einen Fehler ausgibt, können Sie mit withFailureHandler(function) einen Fehler-Handler anstelle eines Erfolgs-Handlers angeben. Dabei wird das Objekt Error (falls vorhanden) als Argument übergeben.

Wenn Sie keinen Fehler-Handler angeben, werden Fehler standardmäßig in der JavaScript-Konsole protokolliert. Wenn Sie dies überschreiben möchten, rufen Sie withFailureHandler(null) auf oder geben Sie einen Fehler-Handler an, der nichts bewirkt.

Wie in diesem Beispiel gezeigt, ist die Syntax für Fehler-Handler fast identisch mit Erfolgs-Handlern.

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>

Nutzerobjekte

Sie können denselben Erfolgs- oder Fehler-Handler für mehrere Aufrufe des Servers wiederverwenden. Rufen Sie dazu withUserObject(object) auf, um ein Objekt anzugeben, das als zweiter Parameter an den Handler übergeben wird. Mit diesem „Nutzerobjekt“ – nicht zu verwechseln mit der Klasse User – können Sie auf den Kontext antworten, in dem der Client den Server kontaktiert hat. Da Nutzerobjekte nicht an den Server gesendet werden, können sie nahezu alles sein, einschließlich Funktionen, DOM-Elemente usw., ohne die Einschränkungen für Parameter und Rückgabewerte für Serveraufrufe. Nutzerobjekte können jedoch nicht mit dem Operator new erstellt werden.

In diesem Beispiel wird durch Klicken auf eine der beiden Schaltflächen die Schaltfläche mit einem Wert vom Server aktualisiert. Die andere Schaltfläche bleibt unverändert, obwohl sie denselben Erfolgs-Handler verwenden. Im onclick-Handler verweist das Schlüsselwort this auf das button selbst.

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>

Formulare

Wenn Sie eine Serverfunktion mit einem form-Element als Parameter aufrufen, wird das Formular zu einem einzelnen Objekt mit Feldnamen als Schlüssel und Feldwerten als Werten. Die Werte werden alle in Strings konvertiert, mit Ausnahme des Inhalts von Dateieingabefeldern, die zu Blob-Objekten werden.

In diesem Beispiel wird ein Formular einschließlich eines Dateieingabefelds verarbeitet, ohne die Seite neu zu laden. Die Datei wird in Google Drive hochgeladen und dann die URL für die Datei auf der clientseitigen Seite ausgegeben. Im onsubmit-Handler bezieht sich das Schlüsselwort this auf das Formular selbst. Beim Laden aller Formulare auf der Seite muss die Standardaktion zum Senden von preventFormSubmit deaktiviert werden. Dadurch wird verhindert, dass die Seite im Falle einer Ausnahme zu einer ungenauen URL weiterleitet.

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>

Script-Runner

Sie können sich google.script.run als einen Builder für einen Skript-Runner vorstellen. Wenn Sie einem Skript-Runner einen Erfolgs-Handler, einen Fehler-Handler oder ein Nutzerobjekt hinzufügen, ändert sich der vorhandene Runner nicht. Stattdessen wird ein neuer Skript-Runner mit einem neuen Verhalten zurückgegeben.

Sie können eine beliebige Kombination und Reihenfolge von withSuccessHandler(), withFailureHandler() und withUserObject() verwenden. Sie können auch eine der Änderungsfunktionen für einen Skript-Runner aufrufen, für den bereits ein Wert festgelegt ist. Der neue Wert überschreibt einfach den vorherigen Wert.

In diesem Beispiel wird ein allgemeiner Fehler-Handler für alle drei Serveraufrufe festgelegt, jedoch zwei separate Erfolgs-Handler:

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

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

Private Funktionen

Serverfunktionen, deren Namen mit einem Unterstrich enden, gelten als privat. Diese Funktionen können nicht von google.script aufgerufen werden und ihre Namen werden nie an den Client gesendet. Damit lassen sich Implementierungsdetails ausblenden, die auf dem Server geheim gehalten werden müssen. google.script kann auch keine Funktionen in Bibliotheken und Funktionen sehen, die nicht auf oberster Ebene des Skripts deklariert sind.

In diesem Beispiel ist die Funktion getBankBalance() im Clientcode verfügbar. Ein Nutzer, der sich den Quellcode ansieht, kann dessen Namen ermitteln, auch wenn Sie ihn nicht aufrufen. Die Funktionen deepSecret_() und obj.objectMethod() sind jedoch für den Client vollständig unsichtbar.

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>

Größe von Dialogfeldern in Google Workspace Anwendungen anpassen

Die Größe der benutzerdefinierten Dialogfelder in Google Docs, Google Tabellen und Google Formulare kann durch Aufrufen der google.script.host-Methoden setWidth(width) oder setHeight(height) im clientseitigen Code angepasst werden. Die anfängliche Größe eines Dialogfelds können Sie mit den HtmlOutput-Methoden setWidth(width) und setHeight(height) festlegen. Beachten Sie, dass Dialogfelder beim Ändern der Größe nicht wieder im übergeordneten Fenster zentriert werden. Außerdem ist es nicht möglich, die Größe der Seitenleisten anzupassen.

Dialogfelder und Seitenleisten in Google Workspacewerden geschlossen

Wenn Sie den HTML-Dienst verwenden, um in Google Docs, Google Tabellen oder Google Formulare ein Dialogfeld oder eine Seitenleiste anzuzeigen, können Sie die Oberfläche nicht durch Aufrufen von window.close() schließen. Stattdessen müssen Sie google.script.host.close() aufrufen. Ein Beispiel finden Sie im Abschnitt zur Bereitstellung von HTML als Google Workspace Benutzeroberfläche.

Fokus des Browsers wird nach Google Workspaceverschoben

Wenn der Fokus im Browser des Nutzers von einem Dialogfeld oder einer Seitenleiste zurück auf den Google Docs-, Tabellen- oder Formulare-Editor verschoben werden soll, rufen Sie einfach die Methode google.script.host.editor.focus() auf. Diese Methode ist besonders in Kombination mit den Document Service-Methoden Document.setCursor(position) und Document.setSelection(range) nützlich.