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: Aufruf 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 deren URL aufrufen, sehen Sie nichts. Wenn Sie jedoch die Logs aufrufen, sehen Sie, dass die Serverfunktion doSomething() aufgerufen wurde.

Clientseitige Aufrufe von serverseitigen Funktionen sind asynchron: Nachdem der Browser angefordert hat, dass der Server die Funktion doSomething() ausführt, 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, kann nicht festgestellt werden, welche Funktion zuerst ausgeführt wird. Das Ergebnis kann sich bei jedem Laden der Seite unterscheiden. In diesem Fall helfen Erfolgs-Handler und Fehler-Handler dabei, den Fluss des Codes zu steuern.

Die google.script.run API ermöglicht 10 gleichzeitige Aufrufe von Serverfunktionen. Wenn Sie einen elften Aufruf ausführen, während noch 10 ausgeführt werden, wird die Serverfunktion verzögert, bis einer der zehn Spots freigegeben wird. In der Praxis sollten Sie diese Einschränkung selten berücksichtigen, insbesondere da die meisten Browser die Anzahl gleichzeitiger Anfragen an denselben Server bereits auf weniger als 10 begrenzen. In Firefox beträgt die Beschränkung beispielsweise sechs. In ähnlicher Weise verzögern die meisten Browser überschüssige 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 an den Client als Parameter zurückgeben, der an einen Erfolgs-Handler übergeben wird.

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 auf der Seite ist auch als Parameter zulässig, muss jedoch der einzige Parameter der Funktion sein und ist nicht als Rückgabewert zulässig. Anfragen schlagen fehl, wenn Sie versuchen, ein Date-, Function-, DOM-Element außer form oder einen anderen unzulässigen Typ zu übergeben, einschließlich unzulässiger Typen innerhalb von Objekten oder Arrays. Objekte, die kreisförmige Verweise erstellen, schlagen ebenfalls fehl und nicht definierte Felder in Arrays werden zu null.

Ein an den Server übergebenes Objekt wird zu einer Kopie des Originals. Wenn eine Serverfunktion ein Objekt empfängt und seine Attribute ändert, sind die Attribute des Clients nicht betroffen.

Erfolgs-Handler

Da clientseitiger Code in die nächste Zeile übergeht, 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. Beachten Sie, dass dieses Codebeispiel eine Autorisierung erfordert, da die serverseitige Funktion auf Ihr Gmail-Konto zugreift. Die einfachste Möglichkeit zur Autorisierung des Skripts besteht darin, die Funktion getUnreadEmails() manuell über den Skripteditor auszuführen, bevor Sie die Seite laden. Wenn Sie die Webanwendung bereitstellen, können Sie sie alternativ als „Nutzer, der auf die Webanwendung zugreift“ ausführen. 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) anstelle eines Erfolgs-Handlers einen Fehler-Handler 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 ausführt.

Die Syntax für Fehler-Handler ist fast identisch mit Erfolgs-Handlern, wie in diesem Beispiel gezeigt wird.

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 an den Server 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 reagieren, in dem der Client den Server kontaktiert hat. Da Nutzerobjekte nicht an den Server gesendet werden, kann es sich um so gut wie alles handeln, einschließlich Funktionen, DOM-Elemente usw., ohne die Einschränkungen für Parameter und Rückgabewerte für Serveraufrufe. Nutzerobjekte können jedoch keine Objekte sein, die mit dem Operator new erstellt wurden.

Wenn Sie in diesem Beispiel auf eine von zwei Schaltflächen klicken, wird diese mit einem Wert vom Server aktualisiert. Die andere Schaltfläche bleibt unverändert, obwohl sie denselben Erfolgs-Handler verwenden. Innerhalb des onclick-Handlers bezieht sich das Schlüsselwort this auf 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 Werte. 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. Innerhalb des onsubmit-Handlers bezieht sich das Schlüsselwort this auf das Formular selbst. Beachten Sie, dass beim Laden aller Formulare auf der Seite die standardmäßige Sendeaktion von preventFormSubmit deaktiviert wird. Dadurch wird verhindert, dass die Seite im Falle einer Ausnahme zu einer falschen 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>

Skript-Runner

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

Sie können eine beliebige Kombination und beliebige 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 gemeinsamer 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, werden als privat betrachtet. Diese Funktionen können nicht von google.script aufgerufen werden und ihre Namen werden nie an den Client gesendet. Sie können sie somit verwenden, um Implementierungsdetails auszublenden, die auf dem Server geheim gehalten werden müssen. google.script kann auch keine Funktionen in Bibliotheken und Funktionen sehen, die nicht auf der obersten Ebene des Skripts deklariert sind.

In diesem Beispiel ist die Funktion getBankBalance() im Clientcode verfügbar. Ein Nutzer, der Ihren Quellcode untersucht, kann ihren Namen auch dann ermitteln, wenn Sie ihn nicht aufrufen. Die Funktionen deepSecret_() und obj.objectMethod() sind für den Client jedoch nicht sichtbar.

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 von benutzerdefinierten Dialogfeldern in Google Docs, Google Tabellen und Google Formulare kann durch Aufrufen der google.script.host-Methoden setWidth(width) oder setHeight(height) in clientseitigem Code angepasst werden. Verwenden Sie die HtmlOutput-Methoden setWidth(width) und setHeight(height), um die anfängliche Größe eines Dialogfelds festzulegen.

Dialogfelder und Seitenleisten in Google Workspaceschließen

Wenn Sie den HTML-Dienst zum Anzeigen eines Dialogfelds oder einer Seitenleiste in Google Docs, Google Tabellen oder Google Formulare verwenden, 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.

Browserfokus wird nach Google Workspaceverschoben

Wenn Sie den Fokus im Browser des Nutzers von einem Dialogfeld oder einer Seitenleiste wieder zum Editor für Google Docs, Google Tabellen oder Google Formulare wechseln möchten, rufen Sie einfach die Methode google.script.host.editor.focus() auf. Diese Methode ist besonders nützlich in Kombination mit den Document Service-Methoden Document.setCursor(position) und Document.setSelection(range).