Servizio HTML: HTML basato su modello

Puoi utilizzare i modelli per combinare il codice di Google Apps Script e HTML per creare pagine dinamiche con il minimo sforzo. Se hai utilizzato linguaggi di modelli che combinano codice e HTML, come PHP, ASP o JSP, la sintassi dovrebbe risultarti familiare.

Scriptlet

I modelli di Apps Script possono contenere tre tag speciali chiamati scriptlet. All'interno di uno scriptlet, puoi scrivere qualsiasi codice che funzioni in un normale file di Apps Script: gli scriptlet possono chiamare funzioni definite in altri file di codice, fare riferimento a variabili globali o utilizzare una qualsiasi delle API Apps Script. Puoi anche definire funzioni e variabili all'interno degli scriptlet, con la limitazione che non possono essere chiamate da funzioni definite in file di codice o altri modelli.

Se incolli l'esempio seguente nell'editor di script, i contenuti del <?= ... ?> tag (uno scriptlet di stampa) vengono visualizzati in corsivo. Questo codice viene eseguito sul server prima che la pagina venga pubblicata per l'utente. Poiché il codice dello scriptlet viene eseguito prima che la pagina venga pubblicata, può essere eseguito una sola volta per pagina. A differenza di JavaScript lato client o delle funzioni di Apps Script che chiami tramite google.script.run, gli scriptlet non possono essere eseguiti di nuovo dopo il caricamento della pagina.

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>

Tieni presente che la funzione doGet per l'HTML con modelli è diversa dagli esempi per la creazione e la pubblicazione di HTML di base. La funzione mostrata qui genera un HtmlTemplate oggetto dal file HTML, quindi chiama il relativo metodo evaluate per eseguire gli scriptlet e convertire il modello in un oggetto HtmlOutput che lo script può pubblicare per l'utente.

Scriptlet standard

Gli scriptlet standard, che utilizzano la sintassi <? ... ?>, eseguono il codice senza generare esplicitamente contenuti nella pagina. Tuttavia, come mostra questo esempio, il risultato del codice all'interno di uno scriptlet può comunque influire sui contenuti HTML al di fuori dello 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 is always served!</p>
    <? } else  { ?>
      <p>This is never served.</p>
    <? } ?>
  </body>
</html>

Gli scriptlet di stampa, che utilizzano la sintassi <?= ... ?>, generano i risultati del codice nella pagina utilizzando l'escape contestuale.

L'escape contestuale significa che Apps Script tiene traccia del contesto dell'output nella pagina (all'interno di un attributo HTML, all'interno di un tag lato client script o in qualsiasi altro punto) e aggiunge automaticamente caratteri di escape per proteggere dagli attacchi di cross-site scripting (XSS).

In questo esempio, il primo scriptlet di stampa genera una stringa direttamente, seguita da uno scriptlet standard che configura un array e un loop, seguito da un altro scriptlet di stampa per generare i contenuti dell'array.

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>

Tieni presente che uno scriptlet di stampa genera solo il valore della prima istruzione; le istruzioni rimanenti si comportano come se fossero contenute in uno scriptlet standard. Ad esempio, lo scriptlet <?= 'Hello, world!'; 'abc' ?> stampa solo "Hello, world!"

Scriptlet di stampa forzata

Gli scriptlet di stampa forzata, che utilizzano la sintassi <?!= ... ?>, sono simili agli scriptlet di stampa , ma evitano l'escape contestuale.

L'escape contestuale è importante se lo script consente l'input dell'utente non attendibile. Al contrario, dovrai forzare la stampa se l'output dello scriptlet contiene intenzionalmente HTML o script che vuoi inserire esattamente come specificato.

Come regola generale, utilizza gli scriptlet di stampa anziché gli scriptlet di stampa forzata, a meno che tu non sappia di dover stampare HTML o JavaScript senza modifiche.

Codice di Apps Script negli scriptlet

Gli scriptlet non sono limitati all'esecuzione di JavaScript normale; puoi anche utilizzare una delle tre tecniche seguenti per consentire ai modelli di accedere ai dati di Apps Script.

Tieni presente, tuttavia, che poiché il codice del modello viene eseguito prima che la pagina venga pubblicata per l'utente, queste tecniche possono solo fornire contenuti iniziali a una pagina. Per accedere ai dati di Apps Script da una pagina in modo interattivo, utilizza invece l' google.script.run API.

Chiamare le funzioni di Apps Script da un modello

Gli scriptlet possono chiamare qualsiasi funzione definita in un file di codice o in una libreria di Apps Script. Questo esempio mostra un modo per estrarre i dati da un foglio di lavoro in un modello, quindi creare una tabella HTML dai dati.

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>

Chiamare direttamente le API Apps Script

Puoi anche utilizzare il codice di Apps Script direttamente negli scriptlet. Questo esempio ottiene lo stesso risultato dell'esempio precedente caricando i dati nel modello stesso anziché tramite una funzione separata.

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>

Inserire variabili nei modelli

Infine, puoi inserire variabili in un modello assegnandole come proprietà dell'HtmlTemplate oggetto. Anche in questo caso, l'esempio ottiene lo stesso risultato degli esempi precedenti.

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>

Eseguire il debug dei modelli

Il debug dei modelli può essere difficile perché il codice che scrivi non viene eseguito direttamente. Il server trasforma invece il modello in codice, quindi esegue il codice risultante.

Se non è chiaro come il modello interpreta gli scriptlet, due metodi di debug nella HtmlTemplate classe possono aiutarti a capire meglio cosa sta succedendo.

La funzione getCode

La funzione getCode restituisce una stringa contenente il codice che il server crea dal modello. Se registri il codice, quindi lo incolli nell'editor di script, puoi eseguirlo e eseguirne il debug come un normale codice di Apps Script.

Ecco di nuovo il modello che mostra un elenco di prodotti Google, seguito dal risultato di 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>

REGISTRA (VALUTATO)

(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('');
})();

La funzione getCodeWithComments

La getCodeWithComments funzione è simile a getCode(), ma restituisce il codice valutato come commenti che vengono visualizzati affiancati al modello originale.

Esaminare il codice valutato

La prima cosa che noterai in entrambi gli esempi di codice valutato è l'oggetto output implicito creato dal metodo HtmlService.initTemplate. Questo metodo non è documentato perché solo i modelli stessi devono utilizzarlo. output è un oggetto HtmlOutput speciale con due proprietà con nomi insoliti, _ e _$, che sono abbreviazioni per chiamare append e appendUntrusted.

output ha un'altra proprietà speciale, $out, che fa riferimento a un oggetto HtmlOutput normale che non possiede queste proprietà speciali. Il modello restituisce l'oggetto normale alla fine del codice.

Ora che hai compreso questa sintassi, puoi seguire il resto del codice. I contenuti HTML al di fuori degli scriptlet (come il tag b) vengono aggiunti utilizzando output._ = (senza escape contestuale) e gli scriptlet vengono aggiunti come JavaScript (con o senza escape contestuale, a seconda del tipo di scriptlet).

Il codice valutato conserva i numeri di riga del modello. Se si verifica un errore durante l'esecuzione del codice valutato, la riga corrisponde ai contenuti equivalenti nel modello.

Gerarchia dei commenti

Poiché il codice valutato conserva i numeri di riga, è possibile che i commenti all'interno degli scriptlet commentino altri scriptlet e persino codice HTML. Questi esempi mostrano alcuni effetti sorprendenti dei commenti:

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