Servicio HTML: Cómo comunicarse con las funciones del servidor

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

google.script.run es una API de JavaScript asíncrona del cliente que permite que las páginas de servicios HTML llamen a funciones de Apps Script del servidor. En el siguiente ejemplo, se muestra la funcionalidad más básica de google.script.run: llamar a una función en el servidor desde JavaScript del cliente.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function doSomething() {
  Logger.log('I was called!');
}

Índice html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      google.script.run.doSomething();
    </script>
  </head>
</html>

Si implementas esta secuencia de comandos como una aplicación web y visitas su URL, no verás nada, pero si ves los registros, verás que se llamó a la función del servidor doSomething().

Las llamadas del lado del cliente a las funciones del servidor son asíncronas: después de que el navegador solicita que el servidor ejecute la función doSomething(), el navegador continúa inmediatamente con la siguiente línea de código sin esperar una respuesta. Esto significa que es posible que las llamadas a funciones del servidor no se ejecuten en el orden esperado. Si haces dos llamadas a la función al mismo tiempo, no hay forma de saber qué función se ejecutará primero; el resultado puede variar cada vez que cargues la página. En esta situación, los controladores de éxito y los controladores de fallas ayudan a controlar el flujo de tu código.

La API de google.script.run permite 10 llamadas simultáneas a las funciones del servidor. Si realizas una 11a llamada mientras las 10 aún están en ejecución, la función del servidor se retrasará hasta que se libere uno de los 10 espacios. En la práctica, casi nunca deberías tener en cuenta esta restricción, especialmente porque la mayoría de los navegadores ya limitan la cantidad de solicitudes simultáneas al mismo servidor en un número inferior a 10. En Firefox, por ejemplo, el límite es 6. De manera similar, la mayoría de los navegadores retrasan el exceso de solicitudes del servidor hasta que se completa una de las solicitudes existentes.

Parámetros y valores de muestra

Puedes llamar a una función de servidor con parámetros del cliente. De manera similar, una función de servidor puede mostrar un valor al cliente como un parámetro que se pasa a un controlador de éxito.

Los parámetros legales y los valores que se muestran son primitivos de JavaScript, como Number, Boolean, String o null, así como arreglos y objetos de JavaScript compuestos por primitivos, objetos y arreglos. Un elemento form dentro de la página también es legal como parámetro, pero debe ser el único parámetro de la función y no es legal como valor de retorno. Las solicitudes fallan si intentas pasar un Date, Function, elemento DOM además de un form o algún otro tipo prohibido, incluidos los tipos prohibidos dentro de objetos o arreglos. Los objetos que crean referencias circulares también fallarán y los campos no definidos dentro de los arreglos se convertirán en null.

Ten en cuenta que un objeto que se pasa al servidor se convierte en una copia del original. Si una función del servidor recibe un objeto y cambia sus propiedades, las propiedades del cliente no se verán afectadas.

Controladores de éxito

Debido a que el código del cliente continúa en la siguiente línea sin esperar a que se complete una llamada al servidor, withSuccessHandler(function) te permite especificar una función de devolución de llamada del lado del cliente para que se ejecute cuando el servidor responde. Si la función del servidor muestra un valor, la API pasa el valor a la función nueva como parámetro.

En el siguiente ejemplo, se muestra una alerta del navegador cuando el servidor responde. Ten en cuenta que este código de muestra requiere autorización porque la función del servidor accede a tu cuenta de Gmail. La forma más simple de autorizar la secuencia de comandos es ejecutar la función getUnreadEmails() de forma manual desde el editor de secuencias de comandos una vez antes de cargar la página. Como alternativa, cuando implementas la aplicación web, puedes elegir ejecutarla como el “usuario que accede a la aplicación web”, en cuyo caso se te solicitará autorización cuando cargues la aplicación.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  return GmailApp.getInboxUnreadCount();
}

Índice html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(numUnread) {
        var div = document.getElementById('output');
        div.innerHTML = 'You have ' + numUnread
            + ' unread messages in your Gmail inbox.';
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

Controladores de fallas

En caso de que el servidor no responda o muestre un error, withFailureHandler(function) te permite especificar un controlador de fallas en lugar de un controlador de éxito, con el objeto Error (si existe) pasado como argumento.

De forma predeterminada, si no especificas un controlador de fallas, las fallas se registran en la consola de JavaScript. Para anular esto, llama a withFailureHandler(null) o proporciona un controlador de fallas que no haga nada.

La sintaxis de los controladores de fallas es casi idéntica a la de los controladores de éxito, como se muestra en este ejemplo.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  // 'got' instead of 'get' will throw an error.
  return GmailApp.gotInboxUnreadCount();
}

Índice html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onFailure(error) {
        var div = document.getElementById('output');
        div.innerHTML = "ERROR: " + error.message;
      }

      google.script.run.withFailureHandler(onFailure)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

Objetos de usuario

Puedes volver a usar el mismo controlador de éxito o falla en varias llamadas al servidor. Para ello, llama a withUserObject(object) a fin de especificar un objeto que se pasará al controlador como un segundo parámetro. Este "objeto de usuario", que no debe confundirse con la clase User, te permite responder al contexto en el que el cliente se contactó con el servidor. Debido a que los objetos de usuario no se envían al servidor, pueden ser casi cualquier cosa, incluidas funciones, elementos de DOM, etc., sin las restricciones sobre parámetros y valores de retorno para las llamadas al servidor. Sin embargo, los objetos de usuario no pueden ser objetos construidos con el operador new.

En este ejemplo, si haces clic en cualquiera de los botones, se actualizará ese botón con un valor del servidor y se dejará el otro sin cambios, a pesar de que comparten un controlador de éxito. Dentro del controlador onclick, la palabra clave this hace referencia a la button.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getEmail() {
  return Session.getActiveUser().getEmail();
}

Índice html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function updateButton(email, button) {
        button.value = 'Clicked by ' + email;
      }
    </script>
  </head>
  <body>
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
  </body>
</html>

Formularios

Si llamas a una función de servidor con un elemento form como parámetro, el formulario se convierte en un objeto único con nombres de campo como claves y valores de campo como valores. Todos los valores se convierten en strings, excepto por el contenido de los campos de entrada de archivos, que se convierten en objetos Blob.

En este ejemplo, se procesa un formulario, incluido un campo de entrada de archivo, sin volver a cargar la página. Se sube el archivo a Google Drive y, luego, se imprime la URL del archivo en la página del cliente. Dentro del controlador onsubmit, la palabra clave this hace referencia al formulario en sí. Ten en cuenta que, después de cargar todos los formularios de la página, preventFormSubmit inhabilita la acción de envío predeterminada. Esto evita que la página redireccione a una URL imprecisa en caso de una excepción.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function processForm(formObject) {
  var formBlob = formObject.myFile;
  var driveFile = DriveApp.createFile(formBlob);
  return driveFile.getUrl();
}

Índice html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      // Prevent forms from submitting.
      function preventFormSubmit() {
        var forms = document.querySelectorAll('form');
        for (var i = 0; i < forms.length; i++) {
          forms[i].addEventListener('submit', function(event) {
            event.preventDefault();
          });
        }
      }
      window.addEventListener('load', preventFormSubmit);

      function handleFormSubmit(formObject) {
        google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
      }
      function updateUrl(url) {
        var div = document.getElementById('output');
        div.innerHTML = '<a href="' + url + '">Got it!</a>';
      }
    </script>
  </head>
  <body>
    <form id="myForm" onsubmit="handleFormSubmit(this)">
      <input name="myFile" type="file" />
      <input type="submit" value="Submit" />
    </form>
    <div id="output"></div>
 </body>
</html>

Ejecutores de secuencias de comandos

Puedes pensar en google.script.run como un compilador para un"ejecutor de secuencias de comandos". Si agregas un controlador de éxito, un controlador de fallas o un objeto de usuario a un ejecutor de secuencias de comandos, no cambiarás el ejecutor existente; en su lugar, obtendrás un nuevo ejecutor de secuencias de comandos con un comportamiento nuevo.

Puedes usar cualquier combinación y cualquier orden de withSuccessHandler(), withFailureHandler() y withUserObject(). También puedes llamar a cualquiera de las funciones de modificación en un ejecutor de secuencias de comandos que ya tenga un valor establecido. El nuevo valor simplemente anula el anterior.

En este ejemplo, se establece un controlador de fallas común para las tres llamadas al servidor, pero dos controladores de éxito separados:

var myRunner = google.script.run.withFailureHandler(onFailure);
var myRunner1 = myRunner.withSuccessHandler(onSuccess);
var myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);

myRunner1.doSomething();
myRunner1.doSomethingElse();
myRunner2.doSomething();

Funciones privadas

Las funciones de servidor cuyos nombres terminan con un guion bajo se consideran privadas. google.script no puede llamar a estas funciones y sus nombres nunca se envían al cliente. Por lo tanto, puedes usarlas para ocultar los detalles de implementación que deben mantenerse en secreto en el servidor. google.script tampoco puede ver las funciones dentro de las bibliotecas y las que no se declararon en el nivel superior de la secuencia de comandos.

En este ejemplo, la función getBankBalance() está disponible en el código de cliente; un usuario que inspecciona tu código fuente puede descubrir su nombre incluso si no lo llamas. Sin embargo, las funciones deepSecret_() y obj.objectMethod() son completamente invisibles para el cliente.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getBankBalance() {
  var email = Session.getActiveUser().getEmail()
  return deepSecret_(email);
}

function deepSecret_(email) {
 // Do some secret calculations
 return email + ' has $1,000,000 in the bank.';
}

var obj = {
  objectMethod: function() {
    // More secret calculations
  }
};

Índice html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(balance) {
        var div = document.getElementById('output');
        div.innerHTML = balance;
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getBankBalance();
    </script>
  </head>
  <body>
    <div id="output">No result yet...</div>
  </body>
</html>

Cambiando el tamaño de los diálogos en Google Workspace aplicaciones

Puedes cambiar el tamaño de los cuadros de diálogo personalizados en Documentos, Hojas de cálculo o Formularios de Google si llamas a los métodos google.script.host setWidth(width) o setHeight(height) en el código del cliente. (Para establecer el tamaño inicial de un diálogo, usa los métodos HtmlOutput setWidth(width) y setHeight(height)). Ten en cuenta que los diálogos no se vuelven a centrar en la ventana superior cuando se cambia el tamaño y que no es posible cambiar el tamaño de las barras laterales.

Cerrar diálogos y barras laterales en Google Workspace

Si usas el servicio HTML para mostrar un cuadro de diálogo o una barra lateral en Documentos, Hojas de cálculo o Formularios de Google, no puedes cerrar la interfaz mediante una llamada a window.close(). En su lugar, debes llamar a google.script.host.close(). Para ver un ejemplo, consulta la sección sobre cómo entregar HTML como una Google Workspace interfaz de usuario.

Trasladando el enfoque del navegador en Google Workspace

Para cambiar el enfoque del navegador del usuario desde un diálogo o una barra lateral al editor de Formularios, Hojas de cálculo o Documentos de Google, simplemente llama al método google.script.host.editor.focus(). Este método es particularmente útil en combinación con los métodos de servicio de Documentos Document.setCursor(position) y Document.setSelection(range).