Servizio HTML: HTML basato su modello

Puoi combinare codice Apps Script e HTML per produrre pagine dinamiche con il minimo sforzo. Se hai utilizzato un linguaggio di creazione di modelli che mescola codice e HTML, come PHP, ASP o JSP, la sintassi dovrebbe risultare familiare.

Scriptlet

I modelli Apps Script possono contenere tre tag speciali, chiamati scriptlet. All'interno di uno scriptlet, puoi scrivere tutto il codice che funzionerebbe in un normale file 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, facendo attenzione al fatto che non possano essere richiamate da funzioni definite nei file di codice o in altri modelli.

Se incolli l'esempio riportato di seguito nell'editor di script, i contenuti del tag <?= ... ?> (uno scriptlet di stampa) verranno visualizzati in corsivo. Il codice in corsivo viene eseguito sul server prima che la pagina venga pubblicata per l'utente. Poiché il codice scriptlet viene eseguito prima della pubblicazione della pagina, può essere eseguito solo una volta per pagina. A differenza delle funzioni JavaScript lato client o 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 basato su modelli è diversa dagli esempi per la creazione e pubblicazione di codice HTML di base. La funzione mostrata qui genera un oggetto HtmlTemplate dal file HTML, quindi chiama il suo metodo evaluate() per eseguire gli scriptlet e convertire il modello in un oggetto HtmlOutput che lo script può mostrare all'utente.

Scriptlet standard

Gli scriptlet standard, che utilizzano la sintassi <? ... ?>, eseguono il codice senza trasmettere esplicitamente i contenuti alla pagina. Tuttavia, come mostrato in 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 will always be served!</p>
    <? } else  { ?>
      <p>This will never be served.</p>
    <? } ?>
  </body>
</html>

Stampa degli scriptlet

La stampa degli scriptlet, che utilizzano la sintassi <?= ... ?>, restituisce i risultati del codice nella pagina utilizzando l'escape contestuale.

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

In questo esempio, il primo scriptlet di stampa restituisce una stringa direttamente; è seguito da uno scriptlet standard che imposta 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 restituisce solo il valore della prima istruzione; eventuali istruzioni rimanenti si comportano come se fossero contenute in uno scriptlet standard. Ad esempio, lo scriptlet <?= 'Hello, world!'; 'abc' ?> stampa solo "Hello, world!"

Stampa forzata degli scriptlet

Gli scriptlet con stampa forzata, che utilizzano la sintassi <?!= ... ?>, sono come la stampa degli scriptlet, tranne per il fatto che evitano l'utilizzo di caratteri di escape contestuali.

L'utilizzo di escape contestuale è importante se lo script consente l'input dell'utente non attendibile. Al contrario, devi 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 con stampa forzata, a meno che tu non sappia di dover stampare il codice HTML o JavaScript invariato.

Codice di Apps Script negli scriptlet

Gli scriptlet non sono limitati all'esecuzione di JavaScript normale. Puoi anche utilizzare una qualsiasi delle tre tecniche seguenti per concedere ai tuoi modelli l'accesso ai dati di Apps Script.

Ricorda tuttavia che, poiché il codice del modello viene eseguito prima che la pagina venga pubblicata per l'utente, queste tecniche possono indirizzare solo il contenuto iniziale alla pagina. Per accedere in modo interattivo ai dati di Apps Script da una pagina, utilizza invece l'API google.script.run.

Chiamata a funzioni 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 a un modello e creare una tabella HTML a partire 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>

Chiamata diretta alle API Apps Script

Puoi anche utilizzare il codice di Apps Script direttamente negli scriptlet. Questo esempio raggiunge 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>

Push delle variabili ai modelli

Infine, puoi inviare le variabili in un modello assegnandole come proprietà dell'oggetto HtmlTemplate. Ancora una volta, questo 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>

Modelli di debug

Può essere difficile eseguire il debug dei modelli perché il codice che scrivi non viene eseguito direttamente; al contrario, il server trasforma il tuo modello in codice e poi esegue il codice risultante.

Se il modo in cui il modello interpreta gli scriptlet non è chiaro, puoi utilizzare due metodi di debug nella classe HtmlTemplate per capire meglio cosa sta succedendo.

getCode()

getCode() restituisce una stringa contenente il codice che il server crea a partire dal modello. Se registri il codice e poi lo incolli nell'editor di script, puoi eseguirlo ed eseguire il debug come con il normale codice Apps Script.

Ecco il semplice modello che mostra di nuovo 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>

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

getCodeWithComments()

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

Presentazione del 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é deve essere utilizzato solo dai modelli stessi. output è un oggetto HtmlOutput speciale con due proprietà con nomi insolitamente, _ e _$, che sono un modo breve per chiamare append() e appendUntrusted().

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

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

Tieni presente che il codice valutato conserva i numeri di riga del modello. Se ricevi un errore durante l'esecuzione del codice valutato, la riga corrisponderà al contenuto equivalente 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 il 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 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. */ ?>