Usługa HTML: szablon HTML

Możesz łączyć kod Apps Script i HTML, aby tworzyć strony dynamiczne z minimalnym nakładem pracy. Jeśli używasz języka szablonów, który łączy kod i HTML, np. PHP, ASP lub JSP, składnia powinna być Ci znajoma.

Scenariusze

Szablony Apps Script mogą zawierać 3 specjalne tagi, zwane skrypletami. W skrypletach możesz pisać dowolny kod, który działa w zwykłym pliku skryptu Apps Script: skryplety mogą wywoływać funkcje zdefiniowane w innych plikach kodu, odwoływać się do zmiennych globalnych lub używać dowolnego interfejsu Apps Script API. Możesz nawet definiować funkcje i zmienną w ramach skrypletów, z tym że nie mogą być one wywoływane przez funkcje zdefiniowane w plikach kodu lub innych szablonach.

Jeśli wkleisz ten przykład do edytora skryptu, zawartość tagu <?= ... ?> (skryptlet do drukowania) będzie wyświetlana kursywą. Kod w kursywie jest wykonywany na serwerze przed wyświetleniem strony użytkownikowi. Kod skryptuletu jest wykonywany przed wyświetleniem strony, dlatego może być uruchamiany tylko raz na stronę. W odróżnieniu od funkcji JavaScript po stronie klienta lub funkcji Apps Script wywoływanych za pomocą funkcji google.script.run skryptylety nie mogą być ponownie wykonywane 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 szablonu HTML różni się od przykładów tworzenia i wyświetlania podstawowego kodu HTML. Funkcja pokazana tutaj generuje obiekt HtmlTemplate z pliku HTML, a następnie wywołuje metodę evaluate(), aby wykonać skryptylety i przekształcić szablon w obiekt HtmlOutput, który 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ę. Jak jednak pokazuje ten przykład, wynik kodu w skrypletach może nadal wpływać na zawartość HTML poza skrypletem:

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 skrypletów

Drukowanie skrypletów, które używają składni <?= ... ?>, wyświetla wyniki kodu na stronie za pomocą ucieczki kontekstowej.

Ucieczka kontekstowa oznacza, że Apps Script śledzi kontekst danych wyjściowych na stronie (w atrybucie HTML, tagu po stronie klienta script lub gdziekolwiek indziej) i automatycznie dodaje znaki ucieczki, aby chronić przed atakami z wykorzystaniem skryptów między witrynami (XSS).

W tym przykładzie pierwszy skrypt drukujący zwraca bezpośrednio ciąg znaków. Jest on poprzedzony przez standardowy skrypt, który tworzy tablicę i pętlę, a następnie przez kolejny skrypt drukujący, który zwraca 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 skryptlet do drukowania wyświetla tylko wartość pierwszego polecenia. Pozostałe polecenia działają tak, jakby były zawarte w standardowym skrypletcie. Dlatego np. skryptlet <?= 'Hello, world!'; 'abc' ?> wypisuje tylko „Witaj, świecie!”.

Skrypty do wymuszania drukowania

Skrypty wymuszające drukowanie, które używają składni <?!= ... ?>, są podobne do skryptów drukujących, z tym wyjątkiem, że nie wymagają ucieczki kontekstowej.

Użycie znaku ucieczki w kontekście jest ważne, jeśli skrypt pozwala na wprowadzanie danych przez nieznane osoby. Z drugiej strony, jeśli wyjście skryptu zawiera kod HTML lub skrypty, które chcesz wstawić dokładnie tak, jak są zapisane, musisz wymusić wydruk.

Zasadniczo używaj skryptów drukowania, a nie skryptów wymuszania drukowania, chyba że wiesz, że musisz wydrukować kod HTML lub JavaScript bez zmian.

Kod Apps Script w skrypletach

Scrptlety nie są ograniczone do zwykłego JavaScriptu. Możesz też użyć jednej z tych 3 technik, aby umożliwić dostęp do danych aplikacji Scriptlety do swoich szablonów.

Pamiętaj jednak, że kod szablonu jest wykonywany przed wyświetleniem strony użytkownikowi, więc te techniki mogą dostarczać tylko początkowej treści na stronę. Aby uzyskać dostęp do danych Apps Script z poziomu strony w sposób interaktywny, 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 API Apps Script

Możesz też używać kodu Apps Script bezpośrednio w skryptach. Ten przykład daje ten sam wynik co poprzedni, ale dane są wczytywane w ramach szablonu, 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>

Przesyłanie zmiennych do szablonów

Możesz też przesłać zmienne do szablonu, przypisując je jako właściwości obiektu HtmlTemplate. Podobnie jak w poprzednich przykładach, ten przykład daje ten sam wynik.

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 następnie wykonuje ten kod wynikowy.

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 zapiszesz kod, a potem wkleisz go do edytora skryptu, możesz go uruchomić i przetestować tak jak zwykły kod Apps Script.

Oto prosty szablon, który wyświetla listę usług Google, a następnie 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 (EVALUATED)

(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 podobne do getCode(), ale zwraca oceniony kod jako komentarze, które pojawiają się obok oryginalnego szablonu.

Przeglądanie ocenianego kodu

Pierwszą rzeczą, którą zauważysz w dowolnym z tych przykładów kodu, jest domyślny obiekt output utworzony przez metodę HtmlService.initTemplate(). Ta metoda nie jest udokumentowana, ponieważ tylko szablony mogą z niej korzystać. output to specjalny obiekt HtmlOutput z 2 właściwościami o nietypowych nazwach: __$, które są skrótem wywołania funkcji append()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ć łatwa w użyciu. Treści HTML spoza skrypletów (np. tag b) są dołączane za pomocą output._ = (bez wyrywania kontekstu), a skryplety są dołączane jako kod JavaScript (z wyrywaniem kontekstu lub bez niego, w zależności od typu skryptu).

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

Hierarchia komentarzy

Ponieważ oceniany kod zachowuje numery wierszy, komentarze wewnątrz skrypletów mogą służyć do komentowania innych skrypletów, a nawet kodu HTML. Te przykłady pokazują kilka zaskakujących efektów działania 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. */ ?>