Usługa HTML: szablon HTML

Możesz połączyć kod Apps Script i HTML, aby tworzyć dynamiczne strony przy minimalnym nakładzie pracy. Jeśli używasz języka szablonów, który łączy kod i HTML, np. PHP, ASP lub JSP, składnia powinna wyglądać znajomo.

Skrypty

Szablony Apps Script mogą zawierać trzy specjalne tagi, nazywane skryptami. W skryptach można umieścić dowolny kod, który działa w zwykłym pliku Apps Script: skrypty mogą wywoływać funkcje zdefiniowane w innych plikach kodu, odwoływać się do zmiennych globalnych lub używać dowolnych interfejsów API Apps Script. Możesz nawet definiować funkcje i zmienne w skryptach, ale pamiętaj, że nie mogą one być wywoływane przez funkcje zdefiniowane w plikach kodu ani innych szablonach.

Jeśli wkleisz poniższy przykład do edytora skryptów, zawartość tagu <?= ... ?> (skryptu drukowania) zostanie wyświetlona kursywą. Ten wyróżniony kursywą kod jest uruchamiany na serwerze przed wyświetleniem strony użytkownikowi. Ponieważ kod skryptu jest wykonywany przed wyświetleniem strony, może zostać uruchomiony tylko raz na stronę. W przeciwieństwie do funkcji JavaScript lub Apps Script po stronie klienta, które wywołujesz przez google.script.run, skryptlet nie może zostać ponownie wykonany po załadowaniu strony.

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>

Pamiętaj, że funkcja doGet() w przypadku kodu HTML opartego na szablonie różni się od przykładów tworzenia i wyświetlania podstawowego kodu HTML. Pokazana tutaj funkcja generuje obiekt HtmlTemplate z pliku HTML, a następnie wywołuje metodę evaluate(), aby wykonać skrypty i przekonwertować szablon na obiekt HtmlOutput, który ten skrypt może wyświetlić użytkownikowi.

Standardowe skrypty

Standardowe skrypty, które używają składni <? ... ?>, wykonują kod bez bezpośredniego przekazywania treści na stronę. Jednak jak widać w tym przykładzie, wynik kodu w skrypcie nadal może wpływać na treść HTML spoza skryptu:

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>

Drukowanie skryptów

Skrypty drukowania, które używają składni <?= ... ?>, wyświetlają wyniki kodu na stronie za pomocą kontekstowego zmiany znaczenia.

Zmiana znaczenia kontekstowego oznacza, że Apps Script śledzi kontekst danych wyjściowych na stronie – w atrybucie HTML, w tagu script po stronie klienta czy w dowolnym innym miejscu – i automatycznie dodaje takie znaki, aby chronić użytkowników przed atakami typu cross-site scripting (XSS).

W tym przykładzie pierwszy skrypt skryptu generuje bezpośrednio ciąg znaków. Po nim następuje standardowy skrypt, który konfiguruje tablicę i pętlę, oraz kolejny skrypt drukowania wyświetlający zawartość tablicy.

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>

Pamiętaj, że skrypt drukowania zwraca tylko wartość pierwszej instrukcji. Pozostałe instrukcje zachowują się tak, jakby były zawarte w standardowym skryptach. Dlatego na przykład skrypt <?= 'Hello, world!'; 'abc' ?> wyświetla tylko „Hello, world!”

Wymuszanie drukowania skryptów

Skrypty z wymuszaniem drukowania, które używają składni <?!= ... ?>, są jak skrypty drukujące, różnią się jednak tym, że zapobiegają kontekstowym zmianom ucieczki.

Zmiana znaczenia kontekstowego jest ważna, jeśli skrypt zezwala na dostęp do niezaufanych danych wejściowych użytkownika. Dla kontrastu musisz wymusić drukowanie, jeśli dane wyjściowe skryptu celowo zawierają kod HTML lub skrypty, które chcesz wstawić dokładnie w takiej formie.

Ogólnie rzecz biorąc, lepiej używać skryptów drukujących zamiast wymuszać drukowanie skryptów, chyba że wiesz, że kod HTML lub JavaScript trzeba wydrukować bez zmian.

Kod Apps Script w skryptach

Skrypty nie są ograniczone do normalnego działania JavaScriptu. Możesz też użyć dowolnej z 3 metod opisanych poniżej, aby przyznać szablonom dostęp do danych Apps Script.

Pamiętaj jednak, że kod szablonu jest uruchamiany przed wyświetleniem strony użytkownikowi, więc te techniki mogą przesyłać na stronę tylko początkową treść. Aby interaktywnie uzyskać dostęp do danych Apps Script ze strony, użyj interfejsu API google.script.run.

Wywoływanie funkcji Apps Script z szablonu

Skrypty mogą wywoływać dowolną funkcję zdefiniowaną w pliku kodu Apps Script lub bibliotece. Ten przykład przedstawia jeden ze sposobów pobrania danych z arkusza kalkulacyjnego do szablonu, a następnie skonstruowania tabeli HTML z tych danych.

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>

Bezpośrednie wywoływanie interfejsów Apps Script API

Możesz też używać kodu Apps Script bezpośrednio w skryptach. Ten przykład daje taki sam efekt jak w poprzednim przykładzie przez wczytanie danych w samym szablonie, a nie za pomocą osobnej funkcji.

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>

Przekazywanie zmiennych do szablonów

Na koniec możesz przekazać zmienne do szablonu, przypisując je jako właściwości obiektu HtmlTemplate. Ten przykład daje taki sam efekt jak poprzednie przykłady.

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>

Szablony debugowania

Debugowanie szablonów może być trudne, ponieważ napisany przez Ciebie kod nie jest wykonywany bezpośrednio. Zamiast tego serwer przekształca szablon w kod, a potem wykonuje ten kod.

Jeśli nie wiesz, jak szablon interpretuje Twoje skrypty, możesz skorzystać z 2 metod debugowania w klasie HtmlTemplate, aby lepiej zrozumieć, co się dzieje.

getCode()

getCode() zwraca ciąg znaków zawierający kod utworzony przez serwer na podstawie szablonu. Jeśli zalogujesz kod, a następnie wkleisz go do edytora skryptów, możesz go uruchomić i debugować jak zwykły kod Apps Script.

Oto prosty szablon, który ponownie wyświetla listę usług Google, a po niej wynik funkcji 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 (OCENY)

(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() jest podobny do getCode(), ale zwraca oceniony kod jako komentarze wyświetlane obok oryginalnego szablonu.

Omówienie oceny kodu

Pierwszą rzeczą, jaką zauważysz w obu przykładach ocenionego kodu, jest niejawny obiekt output utworzony przez metodę HtmlService.initTemplate(). Ta metoda nie jest udokumentowana, ponieważ mogą jej używać tylko szablony. output to specjalny obiekt HtmlOutput z 2 nietypowo nazwanymi właściwościami: _ i _$, czyli skróconymi skrótami wywoływania append() i appendUntrusted().

output ma jeszcze jedną właściwość specjalną – $out, która odnosi się do zwykłego obiektu HtmlOutput, który nie ma tych właściwości specjalnych. Szablon zwraca normalny obiekt na końcu kodu.

Skoro znasz już tę składnię, reszta kodu powinna być prosta w obsłudze. Treści HTML poza skryptami (np. tag b) są dołączane za pomocą output._ = (bez zamiany kontekstowej), a skrypty jako JavaScript (z kontekstem lub bez niego, w zależności od typu skryptu).

Pamiętaj, że sprawdzany kod zachowuje numery wierszy z szablonu. Jeśli podczas uruchamiania ocenionego kodu wystąpi błąd, wiersz będzie odpowiadał treściom w szablonie.

Hierarchia komentarzy

Ponieważ oceniony kod zachowuje numery wierszy, może się zdarzyć, że komentarze w skryptach będą mogły komentować inne skrypty, a nawet kod HTML. Oto kilka zaskakujących efektów komentarzy:

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