Servicio HTML: HTML basado en plantillas

Puedes mezclar código de Apps Script y HTML para producir páginas dinámicas con un esfuerzo. Si usaste un lenguaje de plantillas que combina código y HTML, como PHP, ASP o JSP, la sintaxis debe resultarte familiar.

Scriptlets

Las plantillas de Apps Script pueden contener tres etiquetas especiales llamadas scriptlets. Interior un scriptlet, puedes escribir cualquier código que funcione en un scriptlet : los scriptlets pueden llamar a funciones definidas en otros archivos de código, referencia variables globales o usar cualquiera de las APIs de Apps Script. Incluso puedes definir funciones y variables en los scriptlets, con la salvedad de que no pueden realizan llamadas con funciones definidas en archivos de código o en otras plantillas.

Si pegas el siguiente ejemplo en el editor de secuencias de comandos, el contenido del La etiqueta <?= ... ?> (un scriptlet de impresión) aparecerá en cursiva. El código en cursiva se ejecuta en el servidor antes de que se publique la página. para el usuario. Debido a que el código scriptlet se ejecuta antes de que se entregue la página, solo se pueden ejecutar una vez por página; a diferencia de JavaScript o Apps Script del cliente funciones que llamas mediante google.script.run, los scriptlets no pueden volverá a ejecutarse luego de que se cargue la página.

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>

Ten en cuenta que la función doGet() para el código HTML de plantilla difiere de los ejemplos. para crear y publicar HTML básico. La función que se muestra aquí genera un El objeto HtmlTemplate de HTML y, luego, llama a su evaluate() para ejecutar los scriptlets y convertir la plantilla en HtmlOutput que la secuencia de comandos puede entregar al usuario.

Scriptlets estándar

Los scriptlets estándar, que usan la sintaxis <? ... ?>, ejecutan código sin y que muestre explícitamente el contenido en la página. Sin embargo, como se muestra en este ejemplo, El result del código dentro de un scriptlet puede afectar el contenido HTML fuera del 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>

Cómo imprimir scriptlets

La impresión de scriptlets, que usan la sintaxis <?= ... ?>, generan los resultados de su código a la página mediante el escape contextual.

El escape contextual significa que Apps Script realiza un seguimiento del contexto del resultado en la página (en un atributo HTML o en una etiqueta script del cliente) en cualquier otro lugar y agrega automáticamente caracteres de escape para brindar protección contra ataques de secuencias de comandos entre sitios (XSS).

En este ejemplo, el primer scriptlet de impresión genera una cadena directamente: Está seguido de un scriptlet estándar que configura un array y un bucle, otro scriptlet de impresión para obtener el contenido del 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>

Observa que un scriptlet de impresión solo muestra el valor de su primera sentencia; las demás sentencias se comportan como si estuvieran en un scriptlet. Por ejemplo, el scriptlet <?= 'Hello, world!'; 'abc' ?> solo imprime "Hello, World!"

Impresión forzada de scriptlets

La impresión forzada de los scriptlets, que usan la sintaxis <?!= ... ?>, son como la impresión scriptlets, excepto que evitan el escape contextual.

El escape contextual es importante si la secuencia de comandos permite la entrada de usuarios que no son de confianza. De el contraste, deberás forzar la impresión si la salida del scriptlet contiene HTML o secuencias de comandos que deseas insertar exactamente como se especifica.

Como regla general, usa scriptlets de impresión en lugar de scriptlets de impresión forzada. a menos que sepas que debes imprimir HTML o JavaScript sin cambios.

Código de Apps Script en scriptlets

Los Scriptlets no están restringidos a ejecutar JavaScript normal. también puedes usar cualquier de las siguientes tres técnicas para otorgarles a tus plantillas acceso a Apps Script de datos no estructurados.

Sin embargo, recuerda que, dado que el código de la plantilla se ejecuta antes de que al usuario, estas técnicas solo pueden introducir contenido inicial en una página. Cómo acceder Apps Script de una página de forma interactiva, usa el google.script.run en su lugar.

Llama a funciones de Apps Script desde una plantilla

Scriptlets pueden llamar a cualquier función definida en un archivo de código o una biblioteca de Apps Script. Este ejemplo muestra una manera de extraer datos de una hoja de cálculo a una plantilla, luego construir una tabla HTML a partir de los datos.

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>

Llama directamente a las APIs de Apps Script

También puedes usar el código de Apps Script directamente en scriptlets. En este ejemplo, logra el mismo resultado que el ejemplo anterior cargando los datos en el plantilla en sí, en lugar de hacerlo a través de una función separada.

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>

Envía variables a plantillas

Por último, puedes enviar variables a una plantilla asignándolas como propiedades del objeto HtmlTemplate. Una vez de nuevo, este ejemplo logra el mismo resultado que los ejemplos anteriores.

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>

Plantillas de depuración

Puede ser difícil depurar las plantillas porque el código que escribes no se ejecuta directly; En su lugar, el servidor transforma la plantilla en código y, luego, la ejecuta ese código resultante.

Si no es evidente cómo la plantilla interpreta tus {i>scriptlets<i}, dos de los métodos de depuración HtmlTemplate puede ayudarte entender mejor lo que está pasando.

getCode()

getCode() muestra un o una cadena que contiene el código que el servidor crea a partir de la plantilla. Si registra código y, luego, pégalo en el editor de secuencias de comandos, puedes ejecutarlo y depurarlo como de costumbre. Código de Apps Script

Esta es la plantilla sencilla que muestra una lista de productos de Google seguida del resultado de 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>

REGISTRO (EVALUADO)

(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() es similar a getCode(), pero muestra el código evaluado como comentarios que aparecerán en paralelo con la plantilla original.

Explicación del código evaluado

Lo primero que notarás en cualquiera de las muestras de código evaluado es la Objeto output creado por el método HtmlService.initTemplate() Este método está indocumentado porque solo las plantillas necesitan usarlo. output es un un objeto HtmlOutput especial con dos propiedades con nombres inusuales, _ y _$, que son la abreviatura para llamar append() y appendUntrusted().

output tiene una propiedad más especial, $out, que hace referencia a una propiedad normal Un objeto HtmlOutput que no posee estas propiedades especiales. La plantilla devuelve el objeto normal al final del código.

Ahora que comprendes esta sintaxis, el resto del código debería ser bastante fácil de seguir. Se agrega contenido HTML fuera de los scriptlets (como la etiqueta b) con output._ = (sin escapar contextual) y scriptlets se agregan como JavaScript (con o sin escape contextual, según el tipo de {i>scriptlet<i}).

Ten en cuenta que el código evaluado conserva los números de línea de la plantilla. Si se produce un error mientras se ejecuta el código evaluado, la línea corresponderá al equivalente en la plantilla.

Jerarquía de los comentarios

Debido a que el código evaluado conserva los números de línea, es posible que los comentarios dentro de scriptlets para comentar otros scriptlets y hasta código HTML. Estos Los ejemplos muestran algunos efectos sorprendentes de los comentarios:

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