Intercambiar vistas en aplicaciones Javascript

Las aplicaciones web se están transformando. En los últimos años hemos asistido a un proceso por el cual la típica aplicación web, formada por decenas o cientos de páginas, ha evolucionado a lo que se conoce como SPA (Single Page Application). Las SPA, en lugar de tener un número indefinido de páginas individuales, se componen de una única página web en la que se intercambian las vistas.

Una vista en una aplicación Javascript equivale a lo que sería una página en una aplicación web tradicional, como las que se construyen con PHP por ejemplo. La diferencia es que las vistas son intercambiables sin necesidad de recargar toda la página, ofreciendo al usuario una experiencia mucho más cercana a lo que son las aplicaciones para móviles o escritorio, con mayor velocidad y sin refrescos.

Como las vistas se despliegan en la misma página podríamos pensar que todas tienen la misma URL. No es así, ya que en Javascript se usan lo que se conoce como sistemas de routing, que permiten asociar cada vista a una página distinta. Hablaremos de sistemas de routing para Javascript en otra ocasión. Hoy vamos a explicar cómo se intercambian las vistas.

Método con CSS

Vamos a comenzar viendo un método sencillo para intercambiar vistas, que consiste en manipular el CSS de las distintas páginas que queremos mostrar u ocultar. Básicamente, cuando se pulse un enlace haremos que se oculten todas las vistas menos una (aquella que queremos mostrar). Para ello tendremos todos los contenidos de las vistas precargados en la página y simplemente usaremos los atributos «display» de CSS para mostrar (display: block) y ocultar (display: none) lo que corresponda. El código podría ser más o menos como sigue:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Intercambiando vistas</title>
  <style media="screen">
    #vista2 {
      display: none;
    }
    nav {
      margin-bottom: 20px;
    }
  </style>
</head>
<body>
  <nav>
    <a href="#" id="enlace1">enlace a vista 1</a>
    <a href="#" id="enlace2">Enlace a vista 2</a>
  </nav>

  <div id="vista1">
    <h1>Primera vista</h1>
    <p>Esta es la página de inicio.</p>
  </div>

  <div id="vista2">
    <h1>Segunda vista</h1>
    <p>Esta es la vista que vamos a intercambiar</p>
  </div>

  <script>
    var vista1 = document.getElementById('vista1');
    var vista2 = document.getElementById('vista2');
    document.getElementById('enlace1').addEventListener('click', function(e) {
      e.preventDefault();
      vista1.style.display = 'block';
      vista2.style.display = 'none';
    });
    document.getElementById('enlace2').addEventListener('click', function(e) {
      e.preventDefault();
      vista1.style.display = 'none';
      vista2.style.display = 'block';
    });
  </script>
</body>
</html>

Este es un método sencillo, pero nos genera bastante código, como se puede comprobar. Aunque es bastante comprensible, no resultaría demasiado práctico si tenemos una cantidad grande de vistas. Podríamos mejorarlo colocando todas las divisiones en un array, pero seguiría siendo poco versátil.

Método con generación dinámica del contenido

El mayor problema del anterior ejemplo puede llegar cuando tenemos la necesidad de generar dinámicamente el contenido, incluso con datos que provienen de consultas a servicios web. En estos casos simplemente no podemos tener las páginas escritas de antemano en el código, sino que probablemente se generen automáticamente según el usuario realiza diversas acciones con la aplicación.

Para estos casos la alternativa más sencilla sería usar la propiedad innerHTML del elemento cuyo contenido deseamos intercambiar. Esta propiedad acepta un nuevo código HTML que se colocará dentro y que, por tanto, sustituirá el anterior. Un código posible, simplificado al máximo, sería el siguiente:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Intercambiando vistas</title>
  <style media="screen">
    #vista2 {
      display: none;
    }
    nav {
      margin-bottom: 20px;
    }
  </style>
</head>
<body>
  <nav>
    <a href="#" id="enlace1">enlace a vista 1</a>
    <a href="#" id="enlace2">Enlace a vista 2</a>
  </nav>

  <div id="vista">
    <h1>Primera vista</h1>
    <p>Esta es la página de inicio.</p>
  </div>

  <script>
    var vista = document.getElementById('vista');
    document.getElementById('enlace1').addEventListener('click', function(e) {
        e.preventDefault();
        vista.innerHTML = `
          <h1>Primera vista</h1>
          <p>Este es el contenido...</p>
        `;
    });
    document.getElementById('enlace2').addEventListener('click', function(e) {
      e.preventDefault();
      var dato = 'Un dato que puede provenir de un servicio web';
      vista.innerHTML = `
        <h1>Segunda vista vista</h1>
        <p>${dato}</p>
      `;
    });
  </script>
</body>
</html>

Vemos que los contenidos en la vista se intercambian por medio de enlaces, igual que antes. Solo que en esta ocasión actualizamos el valor de innerHTML. Para crear los contenidos de cada vista usamos Javascript, apoyándonos en los template strings, que nos permiten escribir cadenas en varias líneas e interpolar contenido que proviene de variables existentes en el código. Esos datos podrían perfectamente venir del acceso a servicios web, obtenidos mediante solicitudes con Ajax.

El único problema es que, aunque los template strings nos facilitan el mantenimiento del código HTML de las vistas, no hemos conseguido mucha modularidad. Para ello podríamos usar sistemas de templates en Javascript, como podría ser Handlebars, del que os hablaremos en un futuro artículo.

Conclusión

Hemos visto dos mecanismos elementales de intercambio de vistas en Javascript. Todo ha sido bastante artesanal, usando simplemente VanillaJS, es decir, sin apoyarnos en ninguna librería o componente. Puede ser suficiente para crear una página simple o un comportamiento muy puntual, pero a nivel de aplicación generalmente usamos algunas librerías o frameworks que nos facilitan mucho este trabajo, como podrían ser Angular, Vue o incluso Custom elements del estándar de Web Components.