Try   HackMD

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Express | Vistas Dinámicas

Introducción

ExpressJS puede enviar texto al navegador con solo unas pocas líneas de código:

var express = require('express');
var app = express();

app.get('/', (request, response, next) => {
  response.send('hello world');
});

app.listen(3000);

También podemos enviar HTML más complejo al navegador:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Nos referimos a los argumentos en el callback de nuestra ruta como request y response como demostración.

Estos se representan comúnmente como req y res en la documentación, por lo que los usaremos en el futuro.

app.get('/hello', (req, res, next) => {
  res.send(`
    <!doctype html>
    <html>
      <head>
        <link rel="stylesheet" href="stylesheets/style.css">
      </head>
      <body>
        This is my second route
      </body>
    </html>
  `);
});

De esta forma resultaría tedioso y complicado a medida que nuestra aplicación crezca. ¿Te imaginas que nuestro app.js tenga miles de líneas? ¡Tiene que haber una mejor manera!

En ExpressJS -y en la mayoría de los frameworks- puedes crear archivos específicamente para nuestro HTML. De esta forma, podemos mantener el HTML separado de la lógica de nuestra aplicación.

Estos archivos se llamarán vistas, y una vez que aprendamos a usarlos, simplemente podemos llamar a res.render en lugar de res.send y enviar un archivo HTML al navegador:

app.get('/', (req, res, next) => {
  res.render('index.html');
});

Vistas dinámicas

Las vistas son plantillas específicamente para HTML. HTML es lo que verá el cliente en su navegador.

Para comenzar a usar vistas, debemos crear una carpeta dentro de nuestro proyecto llamada views para agruparlas. Crearemos nuestra primera vista index.hbs:

$ mkdir views
$ touch views/index.hbs
$ tree .
.
├── app.js
├── package.json
├── stylesheets
│   └── style.css
└── views
    └── index.hbs

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Observe que usamos una nueva extensión .hbs en lugar de .html.

La ventaja de separar las vistas es que separamos la lógica del servidor ExpressJS (rutas, configuración del servidor, inicio del servidor, etc.) y la presentación (HTML), haciendo nuestro código más manejable y bien estructurado.

ExpressJS no sabrá por sí solo dónde decidimos agrupar nuestras vistas, pero hay una solución fácil. Podemos decirle a nuestra aplicación Express dónde buscar nuestras vistas:

// creates an absolute path pointing to a folder called "views"
app.set('views', __dirname + '/views');

En Express, en lugar de usar HTML simple, podemos usar una versión más elegante de HTML: hbs, o Handlebars.

En breve entraremos en más detalles sobre HBS, pero por ahora, asegúrese de instalarlo en nuestra aplicación:

$ npm install hbs --save

Y decirle a nuestra aplicación Express que HBS se encargará de renderizar el HTML:

app.set('views', __dirname + '/views');
app.set('view engine', 'hbs');

Abrimos el archivo views/index.hbs y le agregamos un poco de contenido:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>My first view</title>
  <link rel="stylesheet" href="stylesheets/style.css">
</head>
<body>
  <h1>Ironhacker Be Like</h1>
  <img src="https://media.giphy.com/media/l0MYEqEzwMWFCg8rm/giphy.gif">
</body>
</html>

Finalmente, en lugar de res.send() tenemos que decirle a Express que envíe y muestre nuestra vista de index al cliente:

app.get('/', (req, res, next) => {
  // send views/index.hbs for displaying in the browser
  res.render('index');
});

Cuando visitemos localhost:3000, veremos nuestro HTML renderizado!

Handlebars

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Como vimos en el ejemplo anterior, nuestro archivo tenía una extensión .hbs. Esta extensión se utiliza por la palabra Handlebars. Handlebars.js es una biblioteca de JavaScript para crear plantillas limpias y sin lógica basadas en Moustache Templating Language.

Una de las características esenciales de usar Handlebars es que podemos hacer que las plantillas sean dinámicas enviándoles información y usando esos datos para renderizar nuestra aplicación web.

El método res.render() puede tomar un parámetro adicional que contendrá un objeto JavaScript con información que podemos usar en la vista.

Veamos un ejemplo:

// app.js

app.get('/', (req, res, next) => {
  let data = {
    name: "UCOM",
    curso: "Interfaces Web"
  };

  res.render('index', data);
});
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Home</title>
</head>
<body>
  <h1>Hola {{name}}!</h1>
  <p>Bienvenido al curso de {{curso}}!!</p>
</body>
</html>

Cualquier clave pasada en el objeto estará disponible en la vista, con una variable del mismo nombre.

{{variableName}} significa que una variable se enviará al HTML que enviamos al cliente

Las plantillas son en su mayoría HTML, pero HBS las analizará y ejecutará JavaScript antes de que muestre el HTML final y lo envíe al navegador:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Handlebars - Scaping HTML

Por defecto, Handlebars escapa a los valores HTML incluidos en una expresión con el {{ }}. Eso significa que si enviamos datos como este:

app.get('/', (req, res, next) => {
  let data = {
    name: "UCOM",
    curso: "<span>Interfaces Web</span>"
  };
  res.render('index', data);
});

Y lo imprimimos en nuestro archivo HBS:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Home</title>
</head>
<body>
  <h1>Hola {{name}}!</h1>
  <p>Bienvenido al curso de {{curso}}!!</p>
</body>
</html>

Veríamos el contenido con las <span>:

Si no queremos que Handlebars escape de un valor, deberíamos usar triple llave: {{{ }}}.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Home</title>
</head>
<body>
  <h1>Hola {{name}}!</h1>
  <p>Bienvenido al curso de {{{curso}}}!!</p>
</body>
</html>

Built-In Helpers

Además de la función de plantilla dinámica, Handlebars nos brinda excelentes ayudantes para hacernos la vida más fácil al codificar nuestra web.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

El bloque if

Puede usar el bloque if para renderizar un bloque condicionalmente. Eso significa que, si su argumento devuelve "falso", "indefinido", "nulo", "" "," 0 "o" [] ", Handlebars no mostrará el bloque.

app.get('/', (req, res, next) => {
  let data = {
    name: "UCOM",
  };
  res.render('index', data);
});
<h1>Hello {{name}}!</h1>
{{#if lastName}}
    <h2>Esto no se va a mostrar!!</h2>
{{/if}}

Ya que lastName es undefined, nuestro tag <h2> no se va a mostrar! Ahora, agreguemos la propiedad lastName a los datos!

app.get('/', (req, res, next) => {
  let data = {
    name: "UCOM",
    lastName: "University"
  };
  res.render('index', data);
});
<h1>Hola {{name}} {{lastName}}!</h1>
{{#if lastName}}
    <h2>Esto si se va a mostrar!!</h2>
{{/if}}

También podemos agregar una declaración else, ¡lo que hace que esto sea aún más poderoso!

<h1>Hola {{name}} {{lastName}}!</h1>
{{#if address}}
    <h2>Esto no se va a mostrar!!</h2>
{{else}}
  <h2>Esto no se va mostrar porque la propiedad `address` no existe!!</h2>
{{/if}}

El bloque unless

Puede usar el bloque unless como el inverso del bloque if. Representará el bloque si la expresión devuelve un valor falso.

<h1>Hola {{name}} {{lastName}}!</h1>
{{#unless address}}
  <h3>NO PODEMOS ENCONTRAR LA DIRECCIÓN!</h3>
{{/unless}}

Si buscar address en el contexto actual devuelve un valor falso, Handlebars mostrará el mensaje. De lo contrario, no mostrará nada. En nuestro ejemplo, mostrará el mensaje.

Si agregamos la propiedad address el mensaje va a desaparecer!

app.get('/', (req, res, next) => {
  let data = {
    name: "UCOM",
    lastName: "University",
    address: "Online"
  };
  res.render('index', data);
});

El bloque each

El bloque each nos ayuda a iterar sobre una lista de elementos, principalmente objetos y arrays. Imagínese imprimir la lista de materias que tiene una carrera. Podemos hacer algo como esto:

<ul>
  <li>Interfaces Web</li>
  <li>Nuevas Tecnologías</li>
  <li>Gestión de Proyectos</li>
  <li>Sistemas operativos</li>
  <li>Tecnologías</li>
  <li>Programación Unix/Linux</li>
</ul>

Estamos repitiendo la misma etiqueta <li> seis veces, solo cambiando el contenido dentro de las etiquetas. Usando el bloque each, podemos hacer lo siguiente:

Primero, necesitamos pasar los datos a nuestra vista:

app.get('/', (req, res, next) => {
  let data = {
     name: "UCOM",
    lastName: "University",
    address: "Online",
    materias: ["Interfaces Web", "Nuevas Tecnologías", "Gestión de Proyectos", "Sistemas operativos", "Tecnologías", "Programación Unix/Linux"]
  };
  res.render('index', data);
});

Una vez que tenemos los datos en nuestro archivo index.hbs:

<ul>
  {{#each materias}}
    <li>{{this}}</li>
  {{/each}}
</ul>

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Dentro del bloque, podemos usar this para hacer referencia al elemento que estamos iterando.

Opcionalmente, puede proporcionar una sección {{else}} que se mostrará solo cuando la lista esté vacía.

<ul>
  {{#each materias}}
    <li>{{this}}</li>
  {{else}}
    <p>No hay ninguna materia!</p>
  {{/each}}
</ul>

@index

Al recorrer los elementos de each, opcionalmente puedes hacer referencia al índice del ciclo actual a través de{{@index}}

<ul>
  {{#each materias}}
    <li>{{@index}}: {{this}}</li>
  {{/each}}
</ul>

@key

Además, para la iteración del object,{{@key}}hace referencia al nombre de la clave actual:

{{#each object}}
  {{@key}}: {{this}}
{{/each}}

@first - @last

Los primeros y últimos pasos de la iteración se anotan mediante las variables @first y @last cuando se itera sobre una matriz.

<ul>
  {{#each materias}}
    {{#if @first}}
      <li><b>{{this}}</b></li>
    {{else if @last}}
      <li><i>{{this}}</i></li>
    {{else}}
      <li>{{this}}</li>
    {{/if}}
  {{/each}}
</ul>

Es importante notar que los ayudantes @first y @last devuelven un boolean! Al iterar sobre un objeto, solo @first está disponible.

El bloque with

Por lo general, Handlebars evalúa su plantillas frente al contexto pasado al método compilado. Podemos cambiar ese contexto a una sección de una plantilla usando el bloque incorporado with.

Por ejemplo, pasando los siguientes datos:

app.get('/', (req, res, next) => {
  let data = {
     name: "UCOM",
    lastName: "University",
    address: {
        street: "Dr Juan Eulogio Estigarribia",
        number: 32
    },
    materias: ["Interfaces Web", "Nuevas Tecnologías", "Gestión de Proyectos", "Sistemas operativos", "Tecnologías", "Programación Unix/Linux"]
  };
  res.render('index', data);
});

Podemos hacer esto:

<h1>Hola {{name}} {{lastName}}!</h1>
{{#with address}}
  <p>{{street}}, {{number}}</p>
{{/with}}

Usando el bloquewith, cambiamos el contexto, entonces nos podemos referir a {{address.street}} y {{address.number}}, como {{street}} y {{number}}.

Recursos extra