HTML-Dienst: Vorlagen-HTML

Sie können Apps Script-Code und HTML kombinieren, um mit minimalem Aufwand dynamische Seiten zu erstellen. Wenn Sie eine Vorlagensprache verwendet haben, in der Code und HTML kombiniert sind (z. B. PHP, ASP oder JSP), sollte Ihnen die Syntax bekannt vorkommen.

Skriptlets

Apps Script-Vorlagen können drei spezielle Tags enthalten, sogenannte Scriptlets. In einem Scriptlet können Sie beliebigen Code schreiben, der in einer normalen Apps Script-Datei funktionieren würde: Scriptlets können Funktionen aufrufen, die in anderen Codedateien definiert sind, auf globale Variablen verweisen oder eine der Apps Script APIs verwenden. Sie können sogar Funktionen und Variablen in Scriptlets definieren, mit dem Vorbehalt, dass sie nicht von Funktionen aufgerufen werden können, die in Codedateien oder anderen Vorlagen definiert sind.

Wenn Sie das folgende Beispiel in den Skripteditor einfügen, wird der Inhalt des <?= ... ?>-Tags (ein Druck-Scriptlet) kursiv dargestellt. Der kursiv geschriebene Code wird auf dem Server ausgeführt, bevor die Seite für den Nutzer bereitgestellt wird. Da Scriptlet-Code vor dem Bereitstellen der Seite ausgeführt wird, kann er pro Seite nur einmal ausgeführt werden. Im Gegensatz zu clientseitigen JavaScript- oder Apps Script-Funktionen, die Sie über google.script.run aufrufen, können Scriptlets nach dem Laden der Seite nicht noch einmal ausgeführt werden.

Code.gs

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    Hello, World! The time is <?= new Date() ?>.
  </body>
</html>

Die doGet()-Funktion für HTML-Vorlagen unterscheidet sich von den Beispielen zum Erstellen und Bereitstellen von einfachem HTML. Die hier gezeigte Funktion generiert ein HtmlTemplate-Objekt aus der HTML-Datei und ruft dann die Methode evaluate() auf, um die Scriptlets auszuführen und die Vorlage in ein HtmlOutput-Objekt zu konvertieren, das das Skript für den Nutzer bereitstellen kann.

Standard-Scriptlets

Standard-Scriptlets mit der Syntax <? ... ?> führen Code aus, ohne explizit Inhalte an die Seite auszugeben. Wie in diesem Beispiel gezeigt, kann sich das Ergebnis des Codes in einem Scriptlet jedoch weiterhin auf den HTML-Inhalt außerhalb des Scriptlets auswirken:

Code.gs

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? if (true) { ?>
      <p>This will always be served!</p>
    <? } else  { ?>
      <p>This will never be served.</p>
    <? } ?>
  </body>
</html>

Scriptlets drucken

Beim Drucken von Scriptlets, die die Syntax <?= ... ?> verwenden, werden die Ergebnisse ihres Codes mithilfe von kontextbezogenen Escapezeichen auf der Seite ausgegeben.

Kontextbezogene Maskierung bedeutet, dass Apps Script den Kontext der Ausgabe auf der Seite verfolgt – in einem HTML-Attribut, in einem clientseitigen script-Tag oder an einer anderen Stelle – und automatisch Escape-Zeichen hinzufügt, um vor Cross-Site-Scripting-Angriffen (XSS) zu schützen.

In diesem Beispiel gibt das erste Druck-Scriptlet direkt einen String aus. Es folgt ein Standard-Scriptlet, das ein Array und eine Schleife einrichtet, gefolgt von einem weiteren Druck-Scriptlet zur Ausgabe des Arrayinhalts.

Code.gs

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <?= 'My favorite Google products:' ?>
    <? var data = ['Gmail', 'Docs', 'Android'];
      for (var i = 0; i < data.length; i++) { ?>
        <b><?= data[i] ?></b>
    <? } ?>
  </body>
</html>

Ein Druck-Scriptlet gibt nur den Wert der ersten Anweisung aus. Alle verbleibenden Anweisungen verhalten sich so, als wären sie in einem Standard-Scriptlet enthalten. Beispielsweise gibt das Skriptlet <?= 'Hello, world!'; 'abc' ?> nur „Hello, world!“ aus.

Drucken von Scriptlets erzwingen

Scriptlets mit erzwungener Ausgabe, die die Syntax <?!= ... ?> verwenden, ähneln dem Drucken von Scriptlets, enthalten jedoch keine kontextbezogenen Escapezeichen.

Kontextbezogene Maskierung ist wichtig, wenn Ihr Skript Eingaben durch nicht vertrauenswürdige Nutzer zulässt. Im Gegensatz dazu müssen Sie einen Druckvorgang erzwingen, wenn die Ausgabe Ihres Scriptlets absichtlich HTML oder Skripts enthält, die genau wie angegeben eingefügt werden sollen.

Generell sollten Sie Druck-Scriptlets anstelle von erzwungenem Drucken verwenden, es sei denn, Sie wissen, dass Sie HTML oder JavaScript unverändert drucken müssen.

Apps Script-Code in Scriptlets

Scriptlets sind nicht auf die Ausführung von normalem JavaScript beschränkt. Sie können auch eine der folgenden drei Techniken verwenden, um Ihren Vorlagen Zugriff auf Apps Script-Daten zu gewähren.

Da der Vorlagencode jedoch vor dem Bereitstellen der Seite für den Nutzer ausgeführt wird, können mit diesen Techniken nur die ursprünglichen Inhalte einer Seite bereitgestellt werden. Wenn Sie von einer Seite aus interaktiv auf Apps Script-Daten zugreifen möchten, verwenden Sie stattdessen die google.script.run API.

Apps Script-Funktionen über eine Vorlage aufrufen

Scriptlets können jede Funktion aufrufen, die in einer Apps Script-Codedatei oder -bibliothek definiert ist. Dieses Beispiel zeigt eine Möglichkeit, Daten aus einer Tabelle in eine Vorlage zu ziehen und dann eine HTML-Tabelle aus den Daten zu erstellen.

Code.gs

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

function getData() {
  return SpreadsheetApp
      .openById('1234567890abcdefghijklmnopqrstuvwxyz')
      .getActiveSheet()
      .getDataRange()
      .getValues();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? var data = getData(); ?>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

Apps Script APIs direkt aufrufen

Sie können Apps Script-Code auch direkt in Scriptlets verwenden. In diesem Beispiel wird dasselbe Ergebnis wie im vorherigen Beispiel erzielt, indem die Daten in die Vorlage selbst geladen werden und nicht über eine separate Funktion.

Code.gs

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? var data = SpreadsheetApp
        .openById('1234567890abcdefghijklmnopqrstuvwxyz')
        .getActiveSheet()
        .getDataRange()
        .getValues(); ?>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

Variablen an Vorlagen senden

Schließlich können Sie Variablen in eine Vorlage übertragen, indem Sie sie als Attribute des HtmlTemplate-Objekts zuweisen. Auch hier führt dieses Beispiel zum gleichen Ergebnis wie die vorherigen Beispiele.

Code.gs

function doGet() {
  var t = HtmlService.createTemplateFromFile('Index');
  t.data = SpreadsheetApp
      .openById('1234567890abcdefghijklmnopqrstuvwxyz')
      .getActiveSheet()
      .getDataRange()
      .getValues();
  return t.evaluate();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

Debugging-Vorlagen

Die Fehlerbehebung von Vorlagen kann eine Herausforderung darstellen, da der von Ihnen geschriebene Code nicht direkt ausgeführt wird. Stattdessen wandelt der Server Ihre Vorlage in Code um und führt den daraus resultierenden Code aus.

Wenn nicht offensichtlich ist, wie die Vorlage Ihre Scriptlets interpretiert, können Sie mit zwei Fehlerbehebungsmethoden in der Klasse HtmlTemplate das Problem besser nachvollziehen.

getCode()

getCode() gibt einen String zurück, der den Code enthält, den der Server aus der Vorlage erstellt. Wenn Sie den Code protokollieren und dann in den Skripteditor einfügen, können Sie ihn wie normalen Apps Script-Code ausführen und Fehler beheben.

Hier ist die einfache Vorlage, die wieder eine Liste von Google-Produkten, gefolgt vom Ergebnis von getCode(), anzeigt:

Code.gs

function myFunction() {
  Logger.log(HtmlService
      .createTemplateFromFile('Index')
      .getCode());
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <?= 'My favorite Google products:' ?>
    <? var data = ['Gmail', 'Docs', 'Android'];
      for (var i = 0; i < data.length; i++) { ?>
        <b><?= data[i] ?></b>
    <? } ?>
  </body>
</html>

PROTOKOLL (BEWERTET)

(function() { var output = HtmlService.initTemplate(); output._ =  '<!DOCTYPE html>\n';
  output._ =  '<html>\n' +
    '  <head>\n' +
    '    <base target=\"_top\">\n' +
    '  </head>\n' +
    '  <body>\n' +
    '    '; output._$ =  'My favorite Google products:' ;
  output._ =  '    ';  var data = ['Gmail', 'Docs', 'Android'];
        for (var i = 0; i < data.length; i++) { ;
  output._ =  '        <b>'; output._$ =  data[i] ; output._ =  '</b>\n';
  output._ =  '    ';  } ;
  output._ =  '  </body>\n';
  output._ =  '</html>';
  /* End of user code */
  return output.$out.append('');
})();

getCodeWithComments()

getCodeWithComments() ähnelt getCode(), gibt den ausgewerteten Code jedoch als Kommentare zurück, die neben der ursprünglichen Vorlage angezeigt werden.

Schritt-für-Schritt-Anleitung zum bewerteten Code

In beiden Beispielen des bewerteten Codes wird zuerst das implizite output-Objekt angezeigt, das mit der Methode HtmlService.initTemplate() erstellt wurde. Diese Methode ist nicht dokumentiert, da sie nur von den Vorlagen selbst benötigt wird. output ist ein spezielles HtmlOutput-Objekt mit zwei ungewöhnlich benannten Attributen, _ und _$, die für den Aufruf von append() und appendUntrusted() stehen.

output hat eine weitere spezielle Eigenschaft, $out, die auf ein reguläres HtmlOutput-Objekt verweist, das diese speziellen Eigenschaften nicht hat. Die Vorlage gibt dieses normale Objekt am Ende des Codes zurück.

Jetzt, da Sie diese Syntax verstehen, sollte der Rest des Codes ziemlich einfach zu folgen sein. HTML-Inhalte außerhalb von Scriptlets (z. B. das b-Tag) werden mithilfe von output._ = (ohne kontextuelle Maskierung) angehängt. Scriptlets als JavaScript (mit oder ohne kontextbezogenen Escape-Code, je nach Scriptlet-Typ).

Beachten Sie, dass der ausgewertete Code die Zeilennummern aus der Vorlage beibehält. Wenn beim Ausführen des bewerteten Codes ein Fehler auftritt, entspricht die Zeile dem entsprechenden Inhalt in der Vorlage.

Hierarchie der Kommentare

Da beim ausgewerteten Code Zeilennummern beibehalten werden, können Kommentare innerhalb von Scriptlets andere Scriptlets und sogar HTML-Code auskommentieren. Die folgenden Beispiele zeigen einige überraschende Auswirkungen von Kommentaren:

<? var x; // a comment ?> This sentence won't print because a comment begins inside a scriptlet on the same line.

<? var y; // ?> <?= "This sentence won't print because a comment begins inside a scriptlet on the same line.";
output.append("This sentence will print because it's on the next line, even though it's in the same scriptlet.”) ?>

<? doSomething(); /* ?>
This entire block is commented out,
even if you add a */ in the HTML
or in a <script> */ </script> tag,
<? until you end the comment inside a scriptlet. */ ?>