Usługa HTML: szablon HTML

Możesz łączyć kod Apps Script i HTML, aby w prosty sposób tworzyć dynamiczne strony. Jeśli używasz języka szablonów, który łączy kod z kodem HTML, np. PHP, ASP lub JSP, jego składnia powinna wydawać się znajoma.

Skrypty

Szablony Apps Script mogą zawierać trzy specjalne tagi nazywane skryptami. Można w nim zapisać dowolny kod, który działa w zwykłym pliku Apps Script. Mogą one wywoływać funkcje zdefiniowane w innych plikach kodu, odwoływać się do zmiennych globalnych lub korzystać z dowolnego z interfejsów API Apps Script. Możesz nawet definiować w skryptletach funkcje i zmienne, z zastrzeżeniem, że nie można ich wywoływać przez funkcje zdefiniowane w plikach kodu ani w innych szablonach.

Jeśli wkleisz poniższy przykład do edytora skryptów, zawartość tagu <?= ... ?> (skryptu drukowania) będzie pisana kursywą. Ten kod wyróżniony kursywą jest uruchamiany na serwerze, zanim strona zostanie udostępniona użytkownikowi. Kod skryptowy jest uruchamiany przed wyświetleniem strony, więc może być uruchamiany tylko raz na stronę. W przeciwieństwie do funkcji JavaScriptu po stronie klienta lub funkcji Apps Script wywoływanych przez google.script.run po wczytaniu strony skrypt skryptowy nie może go ponownie wykonać.

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 z szablonu różni się od przykładów tworzenia i wyświetlania podstawowego kodu HTML. Ta funkcja generuje obiekt HtmlTemplate z pliku HTML, a następnie wywołuje metodę evaluate(), aby wykonać skryptozę, i przekształcić szablon w obiekt HtmlOutput, który skrypt może wyświetlić użytkownikowi.

Standardowe skrypty

Standardowe skrypty skryptowe, które używają składni <? ... ?>, wykonują kod bez wyświetlania treści bezpośrednio na stronie. Jednak jak widać w tym przykładzie, wynik kodu wewnątrz skryptu może nadal wpływać na zawartość HTML poza tym skryptem:

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 drukujące skrypty, które używają składni <?= ... ?>, wyświetlają wyniki kodu na stronie z wykorzystaniem kontekstu ucieczki.

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

W tym przykładzie pierwszy skrypt generuje bezpośrednio ciąg znaków. Po nim standardowy skrypt konfiguruje tablicę i pętlę, po czym następuje kolejny skrypt drukujący, który zapisuje 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 drukarski zwraca wartość tylko pierwszej instrukcji. Pozostałe instrukcje zachowują się tak, jakby były zawarte w standardowym skrypcie. Na przykład skrypt <?= 'Hello, world!'; 'abc' ?> wyświetla tylko tekst „Hello, world!”.

Skrypty do wymuszania drukowania

Skrypty wymuszające drukowanie, które używają składni <?!= ... ?>, są jak skrypty drukarskie, z tą różnicą, że nie zawierają kontekstu ucieczki.

Zmiany w kontekście są ważne, jeśli skrypt zezwala na wprowadzanie danych przez niezaufanych użytkowników. Z kolei wymuszanie drukowania jest wymagane, jeśli dane wyjściowe skryptustron celowo zawierają kod HTML lub skrypty, które chcesz wstawić dokładnie w określonym miejscu.

Ogólna zasada jest stosowana, gdy zamiast wymuszania drukowania skryptowych skryptów należy drukować skrypty, chyba że wiesz, że konieczne jest drukowanie kodu HTML lub JavaScript w niezmienionej postaci.

Kod Apps Script w skryptach

Skrypty Scriptlety nie są ograniczone do uruchamiania zwykłego kodu JavaScriptu. Aby zapewnić szablonom dostęp do danych Apps Script, możesz też użyć dowolnej z poniższych trzech technik.

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

Wywoływanie funkcji Apps Script z poziomu szablonu

Skrypty Scriptlety mogą wywoływać dowolną funkcję zdefiniowaną w bibliotece lub pliku z kodem Apps Script. Ten przykład pokazuje, jak pobrać dane z arkusza kalkulacyjnego do szablonu, a następnie z nich utworzyć tabelę HTML.

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 jest taki sam jak w poprzednim przykładzie, ponieważ wczytuje dane 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

Możesz też przekazać zmienne do szablonu, przypisując je jako właściwości obiektu HtmlTemplate. Po raz kolejny okazuje się, że w tym przykładzie rezultat jest taki sam jak w poprzednich przykładach.

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ż kod, który piszesz, nie jest wykonywany bezpośrednio. Serwer przekształca szablon w kod, a potem wykonuje otrzymany kod.

Jeśli nie jest oczywiste, jak szablon interpretuje skrypty, 2 metody debugowania z klasy HtmlTemplate mogą pomóc Ci lepiej zrozumieć, co się dzieje.

getCode()

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

Oto prosty szablon, który ponownie wyświetla listę usług Google i wynik 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>

ZAPISZ (OCENA)

(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()

Działanie getCodeWithComments() jest podobne do getCode(), ale zwraca oceniony kod jako komentarze, które pojawiają się obok pierwotnego szablonu.

Omówienie ocenionego 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ż wymagają jej tylko same szablony. output to specjalny obiekt HtmlOutput z 2 właściwościami o nietypowych nazwach: _ i _$, które są skrótami do 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 ten zwykły obiekt na końcu kodu.

Znając już składnię, reszta kodu powinna być dość łatwa do opanowania. Treść HTML poza skryptami (takimi jak tag b) jest dołączana za pomocą parametru output._ = (bez kontekstowej zmiany znaczenia), a skrypty są dołączane jako kod JavaScript (z kontekstową zmianą znaczenia lub bez niej, w zależności od typu skryptu).

Pamiętaj, że w ocenionym kodzie zostaną zachowane numery wierszy z szablonu. Jeśli podczas uruchamiania ocenianego kodu pojawi się błąd, wiersz będzie odpowiadać treści równoważnej treści z szablonu.

Hierarchia komentarzy

Ponieważ oceniany kod zachowuje numery wierszy, możliwe jest, że komentarze w skryptletach mogą dodawać komentarze do innych skryptów, a nawet kodu HTML. Poniższe przykłady pokazują kilka zaskakujących efektów publikowania 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. */ ?>