Ciclo de vida Web Components V1 + Polymer 2

  • Por
Qué es el ciclo de vida de los componentes y los métodos callback disponibles en el estándar de Web Components V1 y en la librería Polymer 2.

Para el desarrollo basado en componentes, el estándar de Web Components V1 ha establecido una serie de métodos que nos permiten realizar tareas a medida que ocurren ciertas cosas con los elementos personalizados, como su creación, inserción en la página, eliminación de la página, etc. Todos estos estados por los que pasa un componente se llaman el ciclo de vida y es algo que debemos conocer cuando vamos a trabajar con Polymer 2.

Para cada estado se puede asociar lo que se llama una "función callback", que contiene el código que queremos ejecutar como respuesta a ese cambio en el estado del elemento. En este artículo vamos a conocer los diversos métodos relacionados con el ciclo de vida, las funciones callback que podemos asociar a cada uno de ellos y ejemplos que nos ayudarán a comprenderlo todo mucho mejor.

Ciclo de vida de Web Components V1

La mayor parte de los estados del ciclo de vida de un componente Polymer 2 son derivados directamente del estándar Web Components V1. Es decir, el propio Javascript nativo, el entendido por los navegadores, es el que nos ofrece mayormente las funciones callback que podemos asociar a un componente.

Los estados del componente a lo largo del ciclo de vida son los siguientes:

  • constructor: Es el propio constructor de la clase ES6 que se escribe para implementar un custom element. El constructor se invoca una vez por cada elemento singular creado de un tipo. Es decir, si tenemos 3 instancias (etiquetas, elementos) del custom element "mi-elemento-personalizado", el constructor se llamará 3 veces, uno por cada uno.
  • connectedCallback: Este estado del ciclo de vida ocurre cuando el elemento se inyecta en el DOM de la página.
  • disconnectedCallback: Este estado ocurre cuando el elemento se borra del DOM de la página.
  • attributeChangedCallback: Este momento del ciclo de vida de los componentes se produce cada vez que un atributo o propiedad del componente cambia.
Nota: Estos callbacks para hacer cosas durante el ciclo de vida son opcionales. Es decir, si no los escribes en la implementación del componente simplemente no se realizarán ningunas acciones específicas en tales momentos.

La mayoría de las veces podrías observar que, al usar un componente, se ejecuta el constructor y seguidamente el connectedCallback, pero no tiene por qué ser así. Para entenderlo tienes que conocer los tres mecanismos por los que podríamos crear un componente:

1.- Colocando la etiqueta de ese custom element en el cuerpo de la página

En este caso el constructor del componente se ejecuta y enseguida el connectedCallback, puesto que el elemento se construye y se inyecta en el contenido de la página en el mismo paso.

<mi-custom-element></mi-custom-element>

2.- Con document.createElement()

Este método de Javascript nativo crea un elemento en la página, que podría ser cualquier elemento del HTML, pero también un elemento personalizado. En este caso el elemento se crea en la memoria de Javascript, pero no se inyecta en la página, así que solo se ejecutaría el constructor.

var el1 = document.createElement('mi-custom-element');

3.- Con new, como un objeto de una clase

Ya que los custom elements se definen a partir de clases, nada nos impide crear el componente mediante el operador "new", igual que hacemos al instanciar objetos en la programación orientada a objetos de toda la vida. En este caso el componente también se crea sólo en la memoria de Javascript y por ello solo se ejecutaría el constructor.

var el2 = new MiCustomElement();
Nota: Sobra decir que en este caso usamos el nombre de la clase y no el nombre de la etiqueta, puesto lo que estamos haciendo es instanciar un elemento de la clase.

Tanto el el caso 2 como en el 3 descritos arriba, si deseas inyectar en la página esos componentes, creados tan solo en la memoria de Javascript, tendrías que usar los mecanismos conocidos del propio lenguaje para la manipulación del DOM, como el método appendChild(). En el momento que inyectes ese componente es en el que se ejecuta finalmente el correspondiente método connectedCallback. Luego veremos ejemplos de todos estos casos.

Callback específico de Polymer 2: Inicialización del componente

Además de los anteriores métodos callback, Polymer 2 agrega uno adicional denominado "ready", que seguramente conozcan los desarrolladores que vienen de usar Polymer en su versión 1.x.

  • ready: este callback para definir acciones durante el ciclo de vida se denomina "One-time initialization" en la documentación de Polymer 2. Básicamente es como un "connectedCallback", solo que no se ejecuta más que 1 vez, la primera que se inyecta en el DOM de la página.

La diferencia entonces entre connectedCallback y ready es que un elemento particular podría producir muchos connectedCallback pero un único ready. Esto podría ocurrir en el caso que un determinado elemento se coloque en el DOM, luego se quite y se vuelva a colocar. Cada vez que se vuelva a colocar se ejecuta de nuevo connectedCallback, pero el ready solo se ejecutará la primera vez que se inyectó en la página.

Nota: "One-time initialization" podría prestarse a confusión, pensando que solo se inicializa una vez, independientemente del número de instancias del componente. Eso sería incorrecto. Es decir, puedes tener un número indeterminado de elementos de un custom element determinado. Ready se ejecuta una tantas veces como número de elementos que hayas creado e incluido dentro del documento o DOM de la página.

Invocar a super en los métodos callback de Polymer

Es importante que, en todo método callback del ciclo de vida de Polymer, se invoque siempre al método correspondiente, pero en la clase padre, por medio de "super".

En el caso del constructor ya lo habíamos comentado en artículos anteriores, la necesidad de invocar a super(). Pero también debemos tener en cuenta hacerlo en el caso de los método callback del estándar, o el propio "ready", para que Polymer pueda enganchar debidamente el código de nuestros propios callbacks a las acciones que él debe de realizar para que los procesos de la librería se realicen correctamente.

Solo que en el caso de los métodos callback la llamada a super se realiza con el objeto super y seguido del método callback que corresponda, por ejemplo: super.disconnectedCallback() o super.ready(). Lo verás más tarde en un ejemplo en este mismo artículo.

Declaración de propiedades en attributeChangedCallback

Otra cosa que debes saber sobre los callbacks del ciclo de vida en Polymer está relacionada con el "attributeChangedCallback", ya que en Polymer 2 sólo se invocará ese callback con las propiedades que hayas declarado en el objeto "properties". Dicho de otro modo, si el componente sufre un cambio en uno de sus atributos del HTML no se invocará attributeChangedCallback en el caso que ese atributo no sea una de sus propiedades declaradas.

Ejemplo de componente y los callbacks del ciclo de vida

Ahora vamos a ver un componente en el que se han implementado todos los callbacks del ciclo de vida que acabas de conocer. En este componente no se hace nada en particular, solo lo tenemos para ir mandando mensajes a la consola cada vez que el componente cambia de estado dentro de su ciclo de vida.

<link rel="import" href="../bower_components/polymer/polymer-element.html">

<dom-module id="ciclo-vida">
  <template>
    <style>
      :host {
        display: block
      }
    </style>

    <div>Este es un elemento "ciclo-vida"</div>

  </template>

  <script>
    class CicloVida extends Polymer.Element {

      static get is() {
        return 'ciclo-vida';
      }

      static get properties() {
        return {
          test: String
        };
      }

      constructor() {
        super();
        console.log('Ejecutándose el constructor')
      }

      connectedCallback() {
        super.connectedCallback();
        console.log('Ejecutándose connectedCallback');
      }

      disconnectedCallback() {
        super.disconnectedCallback();
        console.log('Ejecutándose disconnectedCallback');
      }

      attributeChangedCallback(nombre, valorAnterior, valorNuevo) {
        super.attributeChangedCallback();
        console.log('Ejecutándose attributeChangedCallback', nombre, valorAnterior, valorNuevo);
      }

      ready() {
        super.ready();
        console.log('Ejecutándose ready');
      }
    }

    window.customElements.define(CicloVida.is, CicloVida);
  </script>
</dom-module>

No debe pasar desapercibido el detalle de las invocaciones a "super" en los callbacks y la declaración "properties", con la propiedad "test", que es necesaria para que el attributeChangedCallback se llegue a ejecutar. Si te interesa saber algo más sobre esta llamada a super, por favor, consulta el artículo sobre herencia en las clases de Javascript ES6.

Para poder ejecutar este componente y asegurarnos que los distintos estados del ciclo de vida se produzcan, a su tiempo y de manera que podamos percibir cambios en el componente y los mensajes que ocurren, tenemos que usar el componente con una página HTML que podría tener una forma como esta:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">

  <link rel="import" href="src/ciclo-vida.html">
  
  <title>Uso del componente ciclo-vida y botones para poder producir cambios en el estado</title>
</head>
<body>
  <h1>
      Uso del componente ciclo-vida
  </h1>
  <p>Se colocan dos elementos en el body y se generan dos elementos en la memoria de Javascript.</p>
  <p>Pulsa además los botones para poder producir cambios en el estado de un elemento y ver los otros mensajes del ciclo de vida.</p>
  
  <ciclo-vida></ciclo-vida>
  <ciclo-vida></ciclo-vida>

  <button id="anadirAlDOM">Añadir un elemento al DOM</button>
  <button id="eliminarDelDOM">Eliminar un elemento del DOM</button>
  <button id="cambiarAtributo">Cambiar un atributo de elemento</button>
  <script>
  var el1 = document.createElement('ciclo-vida');
  el1.setAttribute('id', 'el1');
  var el2 = new CicloVida();

  document.getElementById('anadirAlDOM').addEventListener('click', function() {
    document.body.appendChild(el1);
  });

  document.getElementById('eliminarDelDOM').addEventListener('click', function() {
    document.body.removeChild(document.getElementById('el1'));
  });

  document.getElementById('cambiarAtributo').addEventListener('click', function() {
    el1.setAttribute('test', 'valortest' + Date.now());
  });
  </script>
</body>
</html>

Te recomendamos poner en marcha estos ejemplos en tu propio proyecto o carpeta de trabajo, para apreciar el funcionamiento y asimilar el conocimiento contenido en este artículo. Recuerda seguir el Manual de Polymer 2 para obtener más información sobre el desarrollo con esta estupenda librería basada en Web Components.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir

Comentarios

Sergi

12/9/2017
Muy aclarador el artículo
Hola, revisando vuestras informaciones encuentro muy aclarador tu artículo y la información sobre los web components.