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 sembrarti familiare.

Scriptlet

I modelli di Apps Script possono contenere tre tag speciali, chiamati scriptlet. All'interno di un scriptlet puoi scrivere qualsiasi codice che funzionerebbe 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 qualsiasi API di Apps Script. Puoi anche definire funzioni e variabili all'interno degli scriptlet, con l'avvertenza che non possono essere richiamate dalle 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 presentata all'utente. Poiché il codice degli scriptlet viene eseguito prima della pubblicazione della pagina, può essere eseguito una sola volta per pagina. A differenza delle funzioni JavaScript lato client o 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 basato su modelli è diversa dagli esempi per la creazione e la pubblicazione di HTML di base. La funzione riportata qui genera un oggetto HtmlTemplate 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ò fornire all'utente.

Scriptlet standard

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

Stampare scriptlet

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

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

In questo esempio, il primo scriptlet di stampa genera direttamente una stringa, seguito da uno scriptlet standard che configura un array e un ciclo, 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. Tutte 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, tranne per il fatto che evitano l'escapismo contestuale.

L'escapismo contestuale è importante se lo script consente l'input di utenti non attendibili. Al contrario, dovrai eseguire l'output forzato 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é quelli di stampa forzata a meno che tu non sappia che devi stampare HTML o JavaScript invariati.

Codice di Apps Script negli scriptlet

Gli scriptlet non sono limitati all'esecuzione di normale codice JavaScript; puoi anche utilizzare una delle tre tecniche riportate di seguito per consentire ai tuoi modelli di accedere 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 una pagina solo con i contenuti iniziali. Per accedere ai dati di Apps Script da una pagina in modo interattivo, utilizza l'API google.script.run.

Chiamare le funzioni di Apps Script da un modello

Gli scriptlet possono chiamare qualsiasi funzione definita in una raccolta o in un file di codice di Apps Script. Questo esempio mostra un modo per estrarre i dati da un foglio di lavoro in un modello, quindi costruire 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 consente di ottenere 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>

Caricamento delle variabili nei modelli

Infine, puoi inserire variabili in un modello assegnandole come proprietà dell'oggetto HtmlTemplate. 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>

Modelli di debug

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

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

getCode()

getCode() restituisce una stringa contenente il codice creato dal server dal modello. Se lo registri e lo incolli nell'editor di script, puoi eseguirlo e eseguirne il debug come un normale codice di 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 affiancati al modello originale.

Esplorazione del codice valutato

La prima cosa che noterai in entrambi i campioni 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 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 questo normale oggetto alla fine del codice.

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

Tieni presente che il codice valutato conserva i numeri di riga del modello. Se ricevi un messaggio di 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 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. */ ?>