HTML-Dienst: Vorlagen-HTML

Sie können Apps Script-Code und HTML kombinieren, um mit minimalem Aufwand dynamische Seiten zu erstellen. Wenn Sie schon einmal eine Vorlagensprache verwendet haben, in der Code und HTML kombiniert werden, z. B. PHP, ASP oder JSP, sollte Ihnen die Syntax vertraut sein.

Skriptlets

Apps Script-Vorlagen können drei spezielle Tags enthalten, die Scriptlets. In einem Scriptlet können Sie beliebigen Code schreiben, der auch 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. Dieser in Kursivschrift geschriebene Code wird auf dem Server ausgeführt, bevor die Seite an den Nutzer gesendet wird. Da Scriptlet-Code vor der Auslieferung der Seite ausgeführt wird, kann er nur einmal pro Seite ausgeführt werden. Im Gegensatz zu clientseitigen JavaScript- oder Apps Script-Funktionen, die über google.script.run aufgerufen werden, 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 Funktion doGet() für HTML-Vorlagen unterscheidet sich von den Beispielen zum Erstellen und Bereitstellen einfacher HTML-Inhalte. 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 umzuwandeln, das dem Nutzer vom Script bereitgestellt werden kann.

Standard-Scriptlets

Standard-Scriptlets mit der Syntax <? ... ?> führen Code aus, ohne Inhalte explizit auf der 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

Druck-Scriptlets, die die Syntax <?= ... ?> verwenden, geben die Ergebnisse ihres Codes mithilfe von kontextbezogenem Escape-Code auf der Seite aus.

Bei kontextbezogenem Entkommenttieren wird in Apps Script der Kontext der Ausgabe auf der Seite überwacht – innerhalb eines HTML-Attributs, eines clientseitigen script-Tags oder an anderer Stelle – und es werden automatisch Escape-Zeichen hinzugefügt, um vor Cross-Site-Scripting-Angriffen (XSS) zu schützen.

In diesem Beispiel gibt das erste Druckscriptlet einen String direkt aus. Es wird gefolgt von einem Standardscriptlet, das ein Array und eine Schleife einrichtet, und einem weiteren Druckscriptlet, das den Inhalt des Arrays ausgibt.

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>

Hinweis: Ein Druckscriptlet gibt nur den Wert seiner ersten Anweisung aus. Alle verbleibenden Anweisungen verhalten sich so, als wären sie in einem Standardscriptlet enthalten. So gibt das Scriptlet <?= 'Hello, world!'; 'abc' ?> beispielsweise nur „Hallo Welt!“ aus.

Drucken von Scriptlets erzwingen

Scriptlets für den Erzwingen des Druckens, die die Syntax <?!= ... ?> verwenden, ähneln Druckscriptlets, mit der Ausnahme, dass sie das kontextbezogene Entkommen vermeiden.

Kontextbezogenes Entkommenttieren ist wichtig, wenn Ihr Script nicht vertrauenswürdige Nutzereingaben zulässt. Im Gegensatz dazu müssen Sie den Druck erzwingen, wenn die Ausgabe Ihres Scriptlets absichtlich HTML oder Scripts enthält, die genau wie angegeben eingefügt werden sollen.

Verwenden Sie in der Regel Druck-Scriptlets anstelle von Erzwingungs-Druck-Scriptlets, es sei denn, Sie wissen, dass Sie HTML oder JavaScript unverändert drucken müssen.

Apps Script-Code in Scriptlets

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

Da der Vorlagencode jedoch ausgeführt wird, bevor die Seite für den Nutzer bereitgestellt wird, können mit diesen Techniken nur anfängliche Inhalte auf einer Seite bereitgestellt werden. Wenn Sie interaktiv auf Apps Script-Daten von einer Seite aus 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. In diesem Beispiel wird eine Möglichkeit gezeigt, Daten aus einer Tabelle in eine Vorlage einzufügen 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 das gleiche Ergebnis wie im vorherigen Beispiel erzielt, indem die Daten in die Vorlage selbst und nicht über eine separate Funktion geladen werden.

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 einfügen, indem Sie sie als Eigenschaften des Objekts HtmlTemplate 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>

Vorlagen zur Fehlerbehebung

Das Entfernen von Fehlern in Vorlagen kann schwierig sein, da der von Ihnen geschriebene Code nicht direkt ausgeführt wird. Stattdessen wandelt der Server die Vorlage in Code um und führt diesen dann aus.

Wenn Sie nicht sicher sind, wie die Vorlage Ihre Scriptlets interpretiert, können zwei Debugging-Methoden in der Klasse HtmlTemplate Ihnen dabei helfen, das Problem besser zu verstehen.

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, in der wieder eine Liste von Google-Produkten und dann das Ergebnis von getCode() angezeigt wird:

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>

LOG (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 aber als Kommentare zurück, die neben der ursprünglichen Vorlage angezeigt werden.

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

In beiden Beispielen für ausgewerteten Code fällt als Erstes das implizite output-Objekt auf, das von der Methode HtmlService.initTemplate() erstellt wird. Diese Methode ist nicht dokumentiert, da sie nur von Vorlagen selbst verwendet werden muss. output ist ein spezielles HtmlOutput-Objekt mit zwei ungewöhnlich benannten Properties, _ und _$. Sie sind eine Kurzform für append() und appendUntrusted().

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.

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

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

Hierarchie der Kommentare

Da bei ausgewertetem Code die Zeilennummern erhalten bleiben, können Kommentare in Scriptlets andere Scriptlets und sogar HTML-Code auskommentieren. Diese 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. */ ?>