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 che mescola codice e HTML, ad esempio PHP, ASP o JSP, la sintassi dovrebbe risultare familiare.

Scriptlet

I modelli di Apps Script possono contenere tre tag speciali, denominati scriptlet. All'interno di uno scriptlet, puoi scrivere qualsiasi codice che funzioni in un normale file Apps Script: gli scriptlet possono chiamare funzioni definite in altri file di codice, fare riferimento a variabili globali o utilizzare qualsiasi API Apps Script. Puoi anche definire funzioni e variabili all'interno degli scriptlet, con l'attenzione che non possono 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) appariranno 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 o Apps Script lato client che richiami 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 il codice HTML basato su modelli è diversa dagli esempi per la creazione e la pubblicazione di codice HTML di base. La funzione mostrata qui genera un oggetto HtmlTemplate dal file HTML, quindi chiama il 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 inviare esplicitamente contenuti alla 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 will always be served!</p>
    <? } else  { ?>
      <p>This will never be served.</p>
    <? } ?>
  </body>
</html>

Stampa di scriptlet

La stampa degli scriptlet, che utilizzano la sintassi <?= ... ?>, restituisce 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, in un tag script lato client o in qualsiasi altro punto, e aggiunge automaticamente caratteri di escape per proteggerti dagli attacchi di cross-site scripting (XSS).

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

Forza la stampa degli scriptlet

Gli scriptlet a stampa forzata, che utilizzano la sintassi <?!= ... ?>, sono come gli script di stampa, ma evitano l'escape contestuale.

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

Come regola generale, utilizza gli scriptlet di stampa anziché forzare la stampa degli scriptlet, a meno che tu non sappia che devi stampare il codice HTML o JavaScript senza modifiche.

Codice Apps Script negli scriptlet

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

Tuttavia, ricorda che, poiché il codice del modello viene eseguito prima che la pagina venga mostrata all'utente, queste tecniche possono alimentare solo i contenuti iniziali di una pagina. Per accedere ai dati di Apps Script da una pagina in modo interattivo, utilizza invece l'API google.script.run.

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 e poi 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>

Chiamata diretta alle API Apps Script

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

Invio delle variabili ai modelli

Infine, puoi eseguire il push delle variabili in un modello assegnandole come proprietà dell'oggetto HtmlTemplate. Ancora una volta, questo esempio produce 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

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

Se il modo in cui il modello interpreta i tuoi scriptlet non è chiaro, due metodi di debug nella classe HtmlTemplate possono aiutarti a capire meglio il problema.

getCode()

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

Ecco il semplice modello che mostra di nuovo un elenco di prodotti Google, seguito dal risultato 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 sotto forma di commenti visualizzati accanto al modello originale.

Panoramica del codice valutato

La prima cosa che noterai in entrambi gli esempi di codice valutato è l'oggetto output implicito creato con il 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 _$, un'abbreviazione per chiamare append() e appendUntrusted().

output ha un'altra proprietà speciale, $out, che fa riferimento a un oggetto HtmlOutput regolare 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) e 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 viene visualizzato un errore durante l'esecuzione del codice valutato, la riga corrisponderà 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 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. */ ?>