Comienza a usar las visualizaciones personalizadas en Looker

La API de Visualization de Looker es una API de JavaScript que se ejecuta en un iframe en un entorno de pruebas y se aloja dentro de la aplicación de Looker.

Puedes usar el mismo código de visualización para proporcionar una visualización en cualquier lugar de Looker: Exploraciones, Looks, paneles, incorporaciones o imágenes renderizadas o en PDF.

Cada visualización representa una vista de una sola consulta de Looker. Looker ejecuta la consulta y pasa los resultados al código de visualización. Looker también pasa un elemento DOM en el que el código de visualización puede dibujar.

Requisitos

Antes de comenzar, asegúrate de cumplir con los siguientes requisitos:

Crea una visualización de Hello World

En esta sección, se describe cómo crear una secuencia de comandos de visualización personalizada que muestre la primera dimensión de una consulta determinada.

El resultado final debería verse similar a la siguiente imagen:

Configuración

Para desarrollar y probar una visualización en Looker, tienes dos opciones:

Después de configurar una de estas opciones, puedes seguir el ejemplo de esta sección para crear hello_world.js.

Estructura de código esencial

Para ver el código fuente final de este ejemplo, consulta el repositorio de GitHub.

En la carpeta que alojas, crea un archivo JavaScript en blanco nuevo y asígnale el nombre hello_world.js.

Para registrar una nueva visualización personalizada con Looker, llama a la función looker.plugins.visualizations.add y pásale un objeto de visualización. Este objeto contiene la definición de tu visualización y su IU de configuración.

En el siguiente ejemplo, se muestra la estructura de una visualización con todas las propiedades requeridas completadas. Contiene una función create y una función updateAsync en la que puedes escribir tu código de visualización:

looker.plugins.visualizations.add({
  create: function(element, config) {

  },
  updateAsync: function(data, element, config, queryResponse, details, done) {

  }
})

Este código es una visualización válida de Looker, pero aún no es visual y aparece como un cuadro en blanco.

Introducción

Considera la función create, que tiene dos argumentos: element y config.

Looker provides an element, which is the elemento DOM que Looker requiere que coloques en tu visualización. Looker compila este elemento y lo hace del tamaño adecuado. Debes colocar contenido dentro de él.

La función create brinda la oportunidad de realizar la configuración inicial de tu elemento. Para este ejemplo, quieres crear un gráfico que se parezca a la siguiente estructura HTML:

<style>
  /* custom styles for your chart */
</style>
<div class="hello-world-vis">
  <div>Data Goes Here</div>
</div>

Con JavaScript, puedes insertar elementos en tu elemento DOM para que coincida con esta estructura.

En el siguiente ejemplo, se muestra esta función con más detalles agregados:

create: function(element, config) {

  // Insert a <style> tag with some styles to use later.
  var css = element.innerHTML = `
    <style>
      .hello-world-vis {
        /* Vertical centering */
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: center;
        text-align: center;
      }
    </style>
  `;

  // Create a container element to center the text.
  var container = element.appendChild(document.createElement("div"));
  container.className = "hello-world-vis";

  // Create an element to contain the text.
  this._textElement = container.appendChild(document.createElement("div"));

},

Este código agrega CSS, crea el contenedor hello-world-vis y agrega un div dentro de él.

El código también asigna this._textElement al contenedor en el que deseas renderizar. Esto es una comodidad para que no tengas que volver a buscarlo más tarde cuando el gráfico comience a renderizarse.

La función create es un lugar conveniente para realizar tareas de configuración que solo deben realizarse una vez.

Renderización

En esta sección, se describe cómo expandir el método updateAsync para visualizar datos. Se llama a este método cada vez que el gráfico debe visualizar cambios o cuando ocurre cualquier otro evento que pueda afectar la forma en que se renderiza el gráfico (por ejemplo, se cambia el tamaño del gráfico o cambia una opción de configuración).

Para este ejemplo, solo necesitas encontrar la primera dimensión en la primera celda. Puedes hacerlo con data, que es un array de cada fila del conjunto de datos, y queryResponse, que contiene metadatos sobre la consulta, como nombres y tipos de campos.

También puedes usar un método auxiliar llamado LookerCharts.Utils.htmlForCell para proporcionar la representación HTML adecuada del punto de datos en esa celda. Este método controla automáticamente elementos como vínculos de exploración, formato y acciones de datos:

  updateAsync: function(data, element, config, queryResponse, details, done) {

    // Grab the first cell of the data.
    var firstRow = data[0];
    var firstCell = firstRow[queryResponse.fields.dimensions[0].name];

    // Insert the data into the page.
    this._textElement.innerHTML = LookerCharts.Utils.htmlForCell(firstCell);

    // Always call done to indicate a visualization has finished rendering.
    done()
  }

Esto completa los pasos necesarios para tener una visualización personalizada en funcionamiento. Sin embargo, es importante incluir un método para controlar los errores.

Maneja los errores

El código updateAsync toma la primera fila de datos, pero pueden ocurrir errores si la consulta no muestra resultados o no contiene dimensiones.

Sin el manejo de errores, aparece un error de JavaScript en la consola del navegador, lo que podría confundir a los usuarios cuando el gráfico no se renderiza.

Los gráficos pueden mostrar mensajes de error personalizados si tienen problemas mientras intentan renderizarse. Looker agrega dos funciones a tu objeto de visualización: addError y clearErrors. Puedes usar estas funciones para mostrar un mensaje de error que tenga sentido para todos los usuarios.

Debido a que estas funciones se agregan al objeto de visualización, están disponibles desde el contexto de tu función updateAsync a través del objeto this.

Puedes modificar el comienzo de tu método updateAsync para detectar una condición de error, notificar al usuario sobre un problema y salir en lugar de intentar renderizar:

  updateAsync: function(data, element, config, queryResponse, details, done) {

    // Clear any errors from previous updates.
    this.clearErrors();

    // Throw some errors and exit if the shape of the data isn't what this chart needs.
    if (queryResponse.fields.dimensions.length == 0) {
      this.addError({title: "No Dimensions", message: "This chart requires dimensions."});
      // Always call done() before exiting early to avoid dashboard exports hanging
      done();
      return;
    }

    if (!data || data.length === 0) {
      this.addError({title: "No Data", message: "The query returned no results."});
      done();
      return;
    }

    // ... the rest of the update code here ...

Si el usuario crea una consulta que solo contiene medidas, verá un mensaje de error similar al siguiente:

Renderización asíncrona y exportaciones de PDF

En el ejemplo anterior de "Hello World", se llama a la función done() inmediatamente después de que se actualiza el DOM. Este enfoque es adecuado porque la configuración de innerHTML es síncrona.

Sin embargo, si usas bibliotecas de gráficos como D3.js, Chart.js o React, es probable que la renderización sea asíncrona. Si llamas a done() en la parte inferior de la secuencia de comandos, es posible que Looker capture la captura de pantalla para una exportación de PDF o PNG antes de que el gráfico termine de dibujar, lo que puede generar un mosaico en blanco.

Cuando Looker exporta un panel, usa un navegador sin encabezado y se basa en la devolución de llamada done() para saber cuándo es seguro tomar una captura de pantalla.

Para asegurarte de que los gráficos se exporten correctamente, debes vincular la devolución de llamada done() al evento de finalización de tu biblioteca y, luego, inhabilitar las animaciones durante las exportaciones con la marca details.print.

En el siguiente ejemplo, se muestra cómo controlar la renderización asíncrona con D3.js:

  updateAsync: function(data, element, config, queryResponse, details, done) {
    this.clearErrors();

    // Check if Looker is exporting the chart to a PDF/PNG
    const isExport = details && details.print === true;

    // Clear previous elements
    this.svg.selectAll("*").remove();

    const circle = this.svg.append("circle")
      .attr("cx", 50)
      .attr("cy", 50);

    if (isExport) {
      // Instant render for exports. Draw the final state immediately with no transitions
      circle.attr("r", 40);

      // Tell Looker to take the screenshot right now
      done();
    } else {
      // If it's for the browser, start small and animate
      circle.attr("r", 0)
        .transition()
        .duration(1000)
        .attr("r", 40)
        .on("end", () => {
           // Tell Looker the animation is fully complete
           done();
        });
    }
  }

Si usas React, asegúrate de pasar la devolución de llamada done como el tercer argumento a tu función de renderización para que Looker espere a que React monte los componentes:

  this.chart = ReactDOM.render(
    <MyCustomChart data={data} />,
    element,
    done // Looker waits for React to finish
  );

Configuración

Puedes permitir que los usuarios personalicen aspectos de su visualización.

Además de las otras propiedades de tu objeto de visualización, hay una propiedad llamada options disponible.

Puedes usar esta propiedad para especificar qué tipo de opciones requiere el gráfico:

looker.plugins.visualizations.add({
  options: {
    font_size: {
      type: "string",
      label: "Font Size",
      values: [
        {"Large": "large"},
        {"Small": "small"}
      ],
      display: "radio",
      default: "large"
    }
  },
  // ... rest of visualization object ...

En este ejemplo, se crea una opción font_size que se muestra como botones de selección, lo que permite a los usuarios elegir entre tamaños de fuente "pequeños" y "grandes".

Debes especificar la propiedad options para agregar la opción al selector de visualización.

Para usar esta opción en tu código, recuerda que en el método updateAsync hay un parámetro config. Este parámetro contiene las siguientes opciones seleccionadas:

updateAsync: function(data, element, config, queryResponse, details, done) {

El objeto config se parece a lo siguiente:

{font_size: "large"}

Cuando actualizas el gráfico, puedes verificar este objeto y responder en consecuencia. Agrega la siguiente verificación al final del método de actualización para implementar los cambios de tamaño de fuente:

if (config.font_size == "small") {
  this._textElement.className = "hello-world-text-small";
} else {
  this._textElement.className = "hello-world-text-large";
}

También debes actualizar la etiqueta <style> agregada en el método create para definir las clases CSS hello-world-text-small y hello-world-text-large:

.hello-world-text-large {
  font-size: 72px;
}
.hello-world-text-small {
  font-size: 18px;
}

Debido a que se llama a la función updateAsync cada vez que cambia la configuración y Looker guarda automáticamente la configuración, esto es todo lo que debes hacer para responder a la configuración.

Próximos pasos

Puedes ver el código fuente completo de esta visualización de "Hello World" en el repositorio de GitHub de código abierto de Looker.

Para obtener más información sobre la funcionalidad adicional, consulta la referencia de la API de Visualization.