![](https://i.imgur.com/ld6aL74.png =200x) # Express | Vistas Dinámicas ## Introducción ExpressJS puede enviar texto al navegador con solo unas pocas líneas de código: ```javascript 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: :::info :bulb: 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. ::: ```javascript 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: ```javascript 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 ``` :::warning :bulb: 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: ```javascript // 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](http://handlebarsjs.com/)**. 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: ```javascript app.set('views', __dirname + '/views'); app.set('view engine', 'hbs'); ``` Abrimos el archivo `views/index.hbs` y le agregamos un poco de contenido: ```htmlmixed <!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: ```javascript 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](https://user-images.githubusercontent.com/23629340/34818880-6dab9b04-f6bc-11e7-8719-99981c59d03a.png) 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**](http://handlebarsjs.com/) es una biblioteca de JavaScript para crear plantillas limpias y sin lógica basadas en **[Moustache Templating Language](https://mustache.github.io/)**. 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: ```javascript // app.js app.get('/', (req, res, next) => { let data = { name: "UCOM", curso: "Interfaces Web" }; res.render('index', data); }); ``` ```htmlmixed <!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: ![](https://s3-eu-west-1.amazonaws.com/ih-materials/uploads/upload_91c4bf4372d96660bac0f7e1996828c0.png) ## 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: ```javascript 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`: ```htmlmixed <!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: **`{{{ }}}`**. ```htmlmixed <!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. :wink: ### 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. ```javascript app.get('/', (req, res, next) => { let data = { name: "UCOM", }; res.render('index', data); }); ``` ```htmlmixed <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! ```javascript app.get('/', (req, res, next) => { let data = { name: "UCOM", lastName: "University" }; res.render('index', data); }); ``` ```htmlmixed <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! ```htmlmixed <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**. ```htmlmixed <h1>Hola {{name}} {{lastName}}!</h1> {{#unless address}} <h3>NO PODEMOS ENCONTRAR LA DIRECCIÓN!</h3> {{/unless}} ``` :::info 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! ```javascript 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: ```htmlmixed <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: ```javascript 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`: ```htmlmixed <ul> {{#each materias}} <li>{{this}}</li> {{/each}} </ul> ``` :::info :bulb: 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. ```htmlmixed <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}} ` ```htmlmixed <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: ```htmlmixed {{#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. ```htmlmixed <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> ``` :::info 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: ```javascript 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: ```htmlmixed <h1>Hola {{name}} {{lastName}}!</h1> {{#with address}} <p>{{street}}, {{number}}</p> {{/with}} ``` :::info Usando el bloque`with`, cambiamos el contexto, entonces nos podemos referir a `{{address.street}}` y `{{address.number}}`, como `{{street}}` y `{{number}}`. ::: ## Recursos extra - [HandlebarsJS documentation](http://handlebarsjs.com/)