HTML-Dienst: Vorlagen-HTML

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

Scriptlets

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. Es ist sogar möglich, Funktionen und Variablen in Scriptlets zu definieren. Allerdings können sie nicht von Funktionen aufgerufen werden, die in Codedateien oder anderen Vorlagen definiert sind.

Wenn Sie das folgende Beispiel in den Script-Editor einfügen, wird der Inhalt des <?= ... ?>-Tags (eines Druck-Scriptlets) kursiv angezeigt. Dieser kursiv dargestellte 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 nur einmal pro Seite 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 Funktion doGet() für HTML-Vorlagen unterscheidet sich von den Beispielen für das Erstellen und Bereitstellen von einfachem HTML. Die hier gezeigte Funktion generiert aus der HTML-Datei ein HtmlTemplate-Objekt und ruft dann die Methode evaluate() auf, um die Scriptlets auszuführen und die Vorlage in ein HtmlOutput-Objekt zu konvertieren, das dem Nutzer vom Skript bereitgestellt werden 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 dennoch 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

Wenn Sie Scriptlets mit der Syntax <?= ... ?> drucken, werden die Ergebnisse des Codes mithilfe von kontextbezogenem Escapezeichen auf der Seite ausgegeben.

Kontextbezogenes Escaping bedeutet, dass Apps Script den Ausgabekontext auf der Seite – in einem HTML-Attribut, in einem clientseitigen script-Tag oder an einem anderen Ort – erfasst und automatisch Escape-Zeichen hinzufügt, um Cross-Site-Scripting-Angriffe (XSS) zu schützen.

In diesem Beispiel gibt das erste Druckskriptlet direkt einen String aus. Darauf folgt ein Standard-Scriptlet, das ein Array und eine Schleife einrichtet, gefolgt von einem weiteren Druckskriptlet, mit dem der Inhalt des Arrays ausgegeben wird.

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>

Beachten Sie, dass ein Druck-Scriptlet nur den Wert seiner ersten Anweisung ausgibt. Alle verbleibenden Anweisungen verhalten sich so, als wären sie in einem Standard-Scriptlet enthalten. So gibt beispielsweise das Skriptlet <?= 'Hello, world!'; 'abc' ?> nur "Hello, world!" aus.

Scriptlets zum erzwungenen Drucken

Scriptlets, die das Drucken erzwingen, die die Syntax <?!= ... ?> verwenden, ähneln dem Drucken von Scriptlets, mit der Ausnahme, dass sie ein kontextabhängiges Escapen vermeiden.

Kontextbezogenes Escaping ist wichtig, wenn Ihr Skript nicht vertrauenswürdige Nutzereingaben zulässt. Im Gegensatz dazu müssen Sie die Ausgabe erzwingen, wenn die Ausgabe Ihres Scriptlets absichtlich HTML-Code oder Skripts enthält, die Sie genau wie angegeben einfügen möchten.

Im Allgemeinen sollten Sie Scriptlets zum Drucken verwenden, anstatt Scriptlets zu erzwingen, 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 ausgeführt wird, bevor die Seite an den Nutzer ausgeliefert wird, können mit diesen Verfahren nur erste Inhalte in eine Seite eingespeist werden. Wenn Sie interaktiv über eine Seite auf Apps Script-Daten zugreifen möchten, verwenden Sie stattdessen die google.script.run API.

Apps Script-Funktionen aus einer 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 Tabellenkalkulation in eine Vorlage abzurufen und dann aus den Daten eine HTML-Tabelle 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 übertragen

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 schwierig sein, da der von Ihnen geschriebene Code nicht direkt ausgeführt wird. Stattdessen wandelt der Server Ihre Vorlage in Code um und führt dann den resultierenden Code aus.

Wenn nicht offensichtlich ist, wie die Vorlage Ihre Scriptlets interpretiert, können Sie mit zwei Fehlerbehebungsmethoden in der Klasse HtmlTemplate den Grund 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 loggen und dann in den Skripteditor einfügen, können Sie ihn ausführen und wie normalen Apps Script-Code Fehler beheben.

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

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

Schritt-für-Schritt-Anleitung für ausgewerteten Code

Zuerst sehen Sie in beiden Beispielen des ausgewerteten Codes das implizite output-Objekt, das von der Methode HtmlService.initTemplate() erstellt wurde. 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 _$, die kurz zum Aufrufen 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.

Da Sie nun diese Syntax verstehen, sollte der Rest des Codes recht einfach zu befolgen sein. HTML-Inhalte außerhalb von Scriptlets (z. B. das b-Tag) werden mit output._ = (ohne kontextbezogenes Maskieren) angehängt. Scriptlets werden als JavaScript (mit oder ohne kontextbezogenes Escape-Zeichen, abhängig vom Scriptlet-Typ) angehängt.

Beachten Sie, dass der ausgewertete Code Zeilennummern aus der Vorlage beibehält. Wenn Sie beim Ausführen von ausgewertetem Code eine Fehlermeldung erhalten, entspricht die Zeile dem entsprechenden Inhalt in der Vorlage.

Hierarchie der Kommentare

Da beim ausgewerteten Code die Zeilennummern beibehalten werden, ist es möglich, dass Kommentare in Scriptlets andere Scriptlets und sogar HTML-Code auskommentieren. Die folgenden Beispiele zeigen einige überraschende Effekte 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. */ ?>