Service HTML: modèle HTML

Vous pouvez combiner le code Apps Script et le code HTML pour produire des pages dynamiques en toute simplicité. Si vous avez utilisé un langage de création de modèles qui associe du code et du code HTML, tel que PHP, ASP ou JSP, la syntaxe doit vous sembler familière.

Scriptlets

Les modèles Apps Script peuvent contenir trois tags spéciaux, appelés scriptlets. Dans un scriptlet, vous pouvez écrire n'importe quel code pouvant fonctionner dans un fichier Apps Script normal: les scriptslets peuvent appeler des fonctions définies dans d'autres fichiers de code, référencer des variables globales ou utiliser n'importe quelle API Apps Script. Vous pouvez même définir des fonctions et des variables dans des scriptslets, à condition qu'elles ne puissent pas être appelées par des fonctions définies dans des fichiers de code ou d'autres modèles.

Si vous collez l'exemple ci-dessous dans l'éditeur de scripts, le contenu de la balise <?= ... ?> (un script d'impression) s'affiche en italique. Ce code en italique s'exécute sur le serveur avant que la page soit diffusée auprès de l'utilisateur. Étant donné que le code de scriptlet s'exécute avant que la page ne soit diffusée, il ne peut s'exécuter qu'une fois par page. Contrairement aux fonctions JavaScript ou Apps Script côté client que vous appelez via google.script.run, les scriptlets ne peuvent plus s'exécuter une fois la page chargée.

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>

Notez que la fonction doGet() pour le modèle HTML diffère des exemples de création et de diffusion de code HTML de base. La fonction présentée ici génère un objet HtmlTemplate à partir du fichier HTML, puis appelle sa méthode evaluate() pour exécuter les scriptlets et convertir le modèle en objet HtmlOutput que le script peut diffuser à l'utilisateur.

Scriptlets standards

Les scriptslets standards, qui utilisent la syntaxe <? ... ?>, exécutent du code sans afficher explicitement de contenu sur la page. Toutefois, comme le montre cet exemple, le résultat du code contenu dans un scriptlet peut toujours affecter le contenu HTML en dehors du scriptlet:

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>

Scriptlet d'impression

Les scriptslets d'impression, qui utilisent la syntaxe <?= ... ?>, génèrent les résultats de leur code sur la page à l'aide de l'échappement contextuel.

L'échappement contextuel signifie qu'Apps Script effectue un suivi du contexte de sortie sur la page (à l'intérieur d'un attribut HTML, dans une balise script côté client ou ailleurs) et ajoute automatiquement des caractères d'échappement pour vous protéger contre les attaques XSS (cross-site scripting).

Dans cet exemple, le premier scriptlet affiche une chaîne directement. Il est suivi d'un scriptlet standard qui configure un tableau et une boucle, puis d'un autre scriptlet d'impression pour générer le contenu du tableau.

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>

Notez qu'un scriptlet d'impression ne génère que la valeur de sa première instruction. Toutes les instructions restantes se comportent comme si elles figuraient dans un scriptlet standard. Ainsi, le scriptlet <?= 'Hello, world!'; 'abc' ?> n'imprime que "Hello, world!"

Forcer l'impression de scripts

Les scriptslets d'impression forcée, qui utilisent la syntaxe <?!= ... ?>, sont semblables aux scriptlets, sauf qu'ils évitent l'échappement contextuel.

L'échappement contextuel est important si votre script autorise la saisie non approuvée par l'utilisateur. Par contre, vous devrez forcer l'impression si la sortie de votre scriptlet contient intentionnellement du code HTML ou des scripts que vous souhaitez insérer exactement comme spécifié.

En règle générale, préférez l'impression de scriptslets plutôt que d'office, sauf si vous devez modifier le code HTML ou JavaScript tel quel.

Code Apps Script dans les scriptlets

Les scriptslets ne sont pas limités à l'exécution de JavaScript normal. Vous pouvez également utiliser l'une des trois techniques suivantes pour autoriser vos modèles à accéder aux données Apps Script.

Toutefois, n'oubliez pas que, comme le code de modèle s'exécute avant que la page ne soit diffusée auprès de l'utilisateur, ces techniques ne peuvent alimenter que le contenu initial d'une page. Pour accéder aux données Apps Script de manière interactive à partir d'une page, utilisez plutôt l'API google.script.run.

Appeler des fonctions Apps Script depuis un modèle

Les scriptslets peuvent appeler n'importe quelle fonction définie dans un fichier de code ou une bibliothèque Apps Script. Cet exemple montre comment extraire des données d'une feuille de calcul dans un modèle, puis créer une table HTML à partir des données.

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>

Appeler directement les API Apps Script

Vous pouvez également utiliser le code Apps Script directement dans des scripts. Cet exemple permet d'obtenir le même résultat que l'exemple précédent en chargeant les données dans le modèle lui-même plutôt que via une fonction distincte.

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>

Transfert de variables vers des modèles

Enfin, vous pouvez transférer des variables dans un modèle en les attribuant en tant que propriétés de l'objet HtmlTemplate. Là encore, cet exemple fournit le même résultat que les exemples précédents.

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>

Modèles de débogage

Les modèles peuvent être difficiles à déboguer, car le code que vous écrivez n'est pas exécuté directement. Au lieu de cela, le serveur transforme votre modèle en code, puis exécute ce code.

Si la manière dont le modèle interprète vos scripts n'est pas évidente, deux méthodes de débogage dans la classe HtmlTemplate peuvent vous aider à mieux comprendre la situation.

getCode()

getCode() renvoie une chaîne contenant le code que le serveur crée à partir du modèle. Si vous enregistrez le code, puis que vous le collez dans l'éditeur de scripts, vous pouvez l'exécuter et le déboguer comme vous le feriez avec le code Apps Script normal.

Voici le modèle simple qui affiche à nouveau la liste des produits Google, suivi du résultat 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>

JOURNAL (ÉVALUÉ)

(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() est semblable à getCode(), mais renvoie le code évalué sous forme de commentaires qui apparaissent côte à côte avec le modèle d'origine.

Parcourir le code évalué

La première chose que vous remarquerez dans l'un des exemples de code évalué est l'objet output implicite créé par la méthode HtmlService.initTemplate(). Cette méthode n'est pas documentée, car seuls les modèles eux-mêmes doivent l'utiliser. output est un objet HtmlOutput spécial comportant deux propriétés inhabituellement nommées, _ et _$, qui sont des raccourcis pour appeler append() et appendUntrusted().

output comporte une autre propriété spéciale, $out, qui fait référence à un objet HtmlOutput standard qui ne possède pas ces propriétés spéciales. Le modèle renvoie cet objet normal à la fin du code.

Maintenant que vous comprenez cette syntaxe, le reste du code doit être assez facile à suivre. Le contenu HTML en dehors des scripts (comme la balise b) est ajouté avec output._ = (sans échappement contextuel), et les scriptslets sont ajoutés en tant que JavaScript (avec ou sans échappement contextuel, en fonction du type de scriptlet).

Notez que le code évalué conserve les numéros de ligne du modèle. Si vous obtenez une erreur lors de l'exécution du code évalué, la ligne correspond au contenu équivalent dans le modèle.

Hiérarchie des commentaires

Étant donné que le code évalué conserve les numéros de ligne, il est possible que les commentaires dans les scripts soient en train de commenter d'autres scripts, voire du code HTML. Voici quelques exemples surprenants des commentaires:

<? 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. */ ?>