Guía de manillares: motor de plantillas para Node / JavaScript

G

Introducción

En este artículo, veremos cómo usar el motor de plantillas de Handlebars con Node.js y Express. Cubriremos qué son los motores de plantilla y cómo se pueden usar Handlebars para crear aplicaciones web Server Side Rendered (SSR).

También discutiremos cómo configurar manillares con el Express.js framework y cómo utilizar ayudantes integrados para crear páginas dinámicas. Finalmente, veremos cómo desarrollar un asistente personalizado cuando sea necesario.

¿Qué es un motor de plantillas?

En los años 90, cuando se introdujo Internet en el mundo, se utilizaba principalmente con fines científicos, como la publicación de artículos de investigación y como canal de comunicación entre universidades y científicos. La mayoría de las páginas web en ese entonces eran estáticas. Una página web estática es la misma para todos los usuarios y no cambia por usuario. Si se va a cambiar algo en una página, se habría hecho manualmente.

En el mundo moderno, las cosas son mucho más interactivas y se adaptan a cada usuario. Hoy, casi todo el mundo tiene acceso a Internet. La mayoría de las aplicaciones web actuales son dinámicas. Por ejemplo, en Facebook, usted y yo veremos feeds de noticias muy diferentes cuando inicie sesión. Para cada persona, la página seguirá la misma plantilla (es decir, publicaciones secuenciales con nombres de usuario encima), pero el contenido será diferente.

Este es el trabajo de un motor de plantillas: se define la plantilla para la fuente de noticias y luego, en función del usuario actual y la consulta a la base de datos, la plantilla se completa con el contenido recibido.

Podemos usar motores de plantilla tanto en el backend como en el front-end. Si usamos un motor de plantilla en el backend para generar el HTML, lo llamamos renderización del lado del servidor (SSR).

Bigote daliniano

Los manillares son populares tanto para las plantillas traseras como para las frontales. Por ejemplo, el popular framework front-end Ascua utiliza Handlebars como motor de plantillas.

Manillar es una extensión del Bigote lenguaje de plantilla, que se centra principalmente en la simplicidad y las plantillas mínimas.

Usar manubrios con Node.js

Para comenzar, cree una carpeta vacía, abra el símbolo del sistema dentro de esa carpeta y luego ejecute npm init -y para crear un proyecto Node.js vacío con la configuración predeterminada.

Antes de comenzar, debemos instalar las bibliotecas de Node.js necesarias. Puede instalar el Rápido y manillares express módulos ejecutando:

$ npm install --save express express-handlebars

Nota: Cuando use Handlebars en el lado del servidor, probablemente use un módulo de ayuda como express-handlebars que integra Handlebars con tu framework web. En este artículo nos centraremos principalmente en la sintaxis de las plantillas, por lo que estamos usando express-handlebars, pero en caso de que esté manejando la compilación de la plantilla y renderizándose usted mismo, querrá consultar la referencia de la API de compilación también.

Luego, vamos a recrear la estructura de directorios predeterminada de Handlebars. los views La carpeta contiene todas las plantillas de manillares:

.
├── app.js
└── views
    ├── home.hbs
    └── layouts
        └── main.hbs

los layouts carpeta dentro de la views La carpeta contendrá los diseños o los envoltorios de la plantilla. Esos diseños contendrán la estructura HTML, las hojas de estilo y los scripts que se comparten entre las plantillas.

los main.hbs archivo es el diseño principal. los home.hbs El archivo es un ejemplo de plantilla de manillares sobre el que vamos a construir.

Agregaremos más plantillas y carpetas a medida que continuemos.

En nuestro ejemplo, usaremos un script para mantener esto simple. Importemos las bibliotecas necesarias en nuestro app.js archivo:

const express = require('express');
const exphbs = require('express-handlebars');

Entonces, creemos una aplicación Express:

const app = express();

Ahora podemos configurar express-handlebars como nuestro motor de vista:

app.engine('hbs', exphbs({
    defaultLayout: 'main',
    extname: '.hbs'
}));

app.set('view engine', 'hbs');

De forma predeterminada, la extensión para las plantillas de manillares es .handlebars. Pero en la configuración aquí, lo cambiamos a .hbs mediante el extname bandera porque es más corta.

Incluyamos el Oreja guiones y estilos en el main.hbs diseño:

<html lang="en">
<head>
    <!-- <meta> tags> -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
    <title>Book Face</title>
</head>

<body>
    <div class="container">
        {{{body}}}
    </div>

    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</body>
</html>

Y ahora, cambiemos nuestro home.hbs para incluir un mensaje:

<h1>Hello World from Handlebars</h1>

Para poder acceder a esta página, debemos configurar un controlador de solicitudes. Pongámoslo en la ruta raíz:

app.get("https://Pharos.sh.com/", (req, res) => {
    res.render('home');
});

Finalmente, solo necesitamos comenzar a escuchar en un puerto las solicitudes:

app.listen(3000, () => {
    console.log('The web server has started on port 3000');
});

Podemos ejecutar la aplicación con node app.js en la consola, sin embargo, también podemos optar por utilizar una herramienta como Nodemon. Con nodemon, no necesitamos reiniciar el servidor cada vez que hacemos un cambio; cuando cambiamos el código, nodemon actualizará el servidor.

Vamos a instalarlo:

$ npm i -g nodemon

Y ejecutar la aplicación con nodemon se realiza a través de:

$ nodemon app.js

Visitemos nuestra aplicación a través del navegador:

Con todo en su lugar, exploremos algunas características de Handlebars.

Funciones de idioma del manillar

Para mostrar algunas de las características de Handlebars, crearemos un feed de redes sociales. El feed extraerá datos de una matriz simple, simulando una base de datos.

El feed contendrá publicaciones con imágenes y comentarios. Si no hay comentarios en una imagen, aparecerá el mensaje “Sea el primero en comentar esta publicación”.

Actualicemos nuestro home.hbs Para empezar:

<nav class="navbar navbar-dark bg-dark">
    <a class="navbar-brand" href="#">Book Face</a>
</nav>

<div class="posts">
    <div class="row justify-content-center">
        <div class="col-lg-7" style="margin-top: 50px;">
            <div class="card">

                <img src="https://picsum.photos/500/500"
                    class="card-img-top" alt="...">
                <div class="card-body">
                    <h5 class="card-title">Posted by Janith Kasun</h5>

                    <ul class="list-group">
                        <li class="list-group-item">This is supposed to be a comment</li>
                        <li class="list-group-item">This is supposed to be a comment</li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>

Como puede ver en esta plantilla de manillares, hemos agregado un navbar y un card con algunos valores de marcador de posición codificados.

Nuestra página ahora se ve así:

Pasar parámetros a plantillas

Ahora, eliminemos estos valores codificados de la propia página y pasémoslos del script a la página. Estos luego serán reemplazados con valores de comentario en la matriz:

app.get("https://Pharos.sh.com/", function (req, res) {
    res.render('home', {
        post: {
            author: 'Janith Kasun',
            image: 'https://picsum.photos/500/500',
            comments: []
        }
    });
});

los post contiene campos como author, imagey comments. Podemos hacer referencia al post en nuestra plantilla Manillares {{post}}:

<div class="posts">
    <div class="row justify-content-center">
        <div class="col-lg-7" style="margin-top: 50px;">
            <div class="card">

                <img src="{{post.image}}"
                    class="card-img-top" alt="...">
                <div class="card-body">
                    <h5 class="card-title">Posted by {{post.author}}</h5>

                    <ul class="list-group">
                        <li class="list-group-item">This is suppose to be a comment</li>
                        <li class="list-group-item">This is suppose to be a comment</li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>

Al hacer referencia a estos valores con el controlador que representa la página, se insertan en el lado del servidor y al usuario se le ofrece HTML aparentemente estático con estos valores ya presentes.

Condiciones de uso

Como tenemos lógica condicional, es decir, mostrar los comentarios si están presentes y un mensaje si no lo están, veamos cómo podemos usar condicionales en las plantillas de Handlebars:

<div class="posts">
    <div class="row justify-content-center">
        <div class="col-lg-7" style="margin-top: 50px;">
            <div class="card">

                <img src="{{post.image}}" class="card-img-top" alt="...">
                <div class="card-body">
                    <h5 class="card-title">Posted by {{post.author}}</h5>

                    {{#if post.comments}}
                    <ul class="list-group">
                        <!-- Display comment logic -->

                    </ul>
                    {{else}}
                    <ul class="list-group">
                        <li class="list-group-item">Be first to comment on this post!</li>
                    </ul>
                    {{/if}}
                </div>
            </div>
        </div>
    </div>
</div>

Ahora, solo debería ver la sección “Sea el primero en comentar esta publicación” representada en su página, ya que la matriz de comentarios está vacía:

los #if es un ayudante integrado en Handlebars. Si la sentencia if regresa true, el bloque dentro del #if se renderizará el bloque. Si false, undefined, null, "", 0o [] se devuelven, el bloque no se procesará.

Nuestra matriz está vacía ([]) para que el bloque no se procese.

#if solo acepta una condición y no puede usar la sintaxis de comparación de JavaScript (===). Si necesita utilizar varias condiciones o sintaxis adicional, puede crear una variable en el código y pasarla a la plantilla. Además, puede definir su propio ayudante, lo que haremos en la última sección.

Usar bucles

Dado que una publicación puede contener varios comentarios, necesitaremos un bucle para leerlos todos y representarlos. Primero llenemos nuestra matriz con algunos comentarios:

app.get("https://Pharos.sh.com/", function (req, res) {
    res.render('home', {
        post: {
            author: 'Janith Kasun',
            image: 'https://picsum.photos/500/500',
            comments: [
                'This is the first comment',
                'This is the second comment',
                'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum nec fermentum ligula. Sed vitae erat lectus.'
            ]
        }
    });
});

Y ahora, en nuestra plantilla, usaremos el #each bucle para revisarlos todos:

<div class="posts">
    <div class="row justify-content-center">
        <div class="col-lg-7" style="margin-top: 50px;">
            <div class="card">

                <img src="{{post.image}}" class="card-img-top" alt="...">
                <div class="card-body">
                    <h5 class="card-title">Posted by {{post.author}}</h5>

                    {{#if post.comments}}
                    <ul class="list-group">
                        {{#each post.comments}}
                        <li class="list-group-item">{{this}}</li>
                        {{/each}}
                    </ul>
                    {{else}}
                    <ul class="list-group">
                        <li class="list-group-item">Be first to comment on this post</li>
                    </ul>
                    {{/if}}
                </div>
            </div>
        </div>
    </div>
</div>

Dentro de #each bucle, puedes usar this para hacer referencia al elemento que está en la iteración actual. En nuestro caso, se refiere a una cadena que luego se representa:

Si tiene una matriz de objetos, también puede acceder a cualquier atributo de ese objeto. Por ejemplo, si hay una variedad de personas, simplemente puede usar this.name para acceder al name campo.

Ahora, cambiemos los parámetros de nuestra plantilla para que contengan múltiples publicaciones:

app.get("https://Pharos.sh.com/", function (req, res) {
    res.render('home', {
        posts: [
            {
                author: 'Janith Kasun',
                image: 'https://picsum.photos/500/500',
                comments: [
                    'This is the first comment',
                    'This is the second comment',
                    'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum nec fermentum ligula. Sed vitae erat lectus.'
                ]
            },
            {
                author: 'John Doe',
                image: 'https://picsum.photos/500/500?2',
                comments: [
                ]
            }
        ]
    });
});

Ahora también podemos poner un #each para recorrer las publicaciones:

<div class="posts">
    <div class="row justify-content-center">
        {{#each posts}}
        <div class="col-lg-7" style="margin-top: 50px;">
            <div class="card">
                <img src="{{this.image}}" class="card-img-top" alt="...">
                <div class="card-body">
                    <h5 class="card-title">Posted by {{this.author}}</h5>

                    {{#if this.comments}}
                    <ul class="list-group">
                        {{#each this.comments}}
                        <li class="list-group-item">{{this}}</li>
                        {{/each}}
                    </ul>
                    {{else}}
                    <ul class="list-group">
                        <li class="list-group-item">Be first to comment on this post</li>
                    </ul>
                    {{/if}}
                </div>
            </div>
        </div>
        {{/each}}
    </div>
</div>

Usando parcial

Prácticamente todas las páginas web contienen secciones diferentes. En un nivel básico, estas son las secciones Encabezado, Cuerpo y Pie de página. Dado que el encabezado y el pie de página generalmente se comparten entre muchas páginas, tener esto en todas las páginas web pronto se volverá extremadamente molesto y simplemente redundante.

Afortunadamente, podemos usar Handlebars para dividir estas secciones en plantillas y simplemente incluir estas plantillas como “parciales” en las propias páginas.

En nuestro caso, como no tenemos pie de página, hagamos un header.hbs y un posts.hbs archivo en un partials directorio:

.
├── app.js
└── views
    ├── home.hbs
    ├── layouts
    |  └── main.hbs
    └── paritials
       └── header.hbs
       └── posts.hbs

Luego, moveremos el código del encabezado al header.hbs archivo:

<nav class="navbar navbar-dark bg-dark">
    <a class="navbar-brand" href="#">Book Face</a>
</nav>

Y el código de alimentación en el posts.hbs archivo:

<div class="posts">
    <div class="row justify-content-center">
        {{#each posts}}
        <div class="col-lg-7" style="margin-top: 50px;">
            <div class="card">

                <img src="{{this.image}}" class="card-img-top" alt="...">
                <div class="card-body">
                    <h5 class="card-title">Posted by {{this.author}}</h5>

                    {{#if this.comments}}
                    <ul class="list-group">
                        {{#each this.comments}}
                        <li class="list-group-item">{{this}}</li>
                        {{/each}}
                    </ul>
                    {{else}}
                    <ul class="list-group">
                        <li class="list-group-item">Be first to comment on this post</li>
                    </ul>
                    {{/if}}
                </div>
            </div>
        </div>
        {{/each}}
    </div>
</div>

Y ahora, podemos incluirlos en el home.hbs archivo:

{{>header}}

{{>posts posts=posts}}

El usuario no verá la diferencia, pero nuestro home.hbs el archivo está mucho más limpio ahora. Esto se vuelve muy útil cuando tienes páginas web complejas.

Aquí, simplemente hemos incluido el header.hbs archivo y pasó un posts parámetro al posts campo de la posts.hbs archivo.

Lo que hace esto es que pasa el posts de nuestro manejador al posts parámetro en el posts.hbs archivo de página.

Creación de un asistente personalizado

Como puede ver en la página, tenemos un solo comentario que consume dos líneas. Creemos un ayudante personalizado para resumir ese texto.

Para ello, en la configuración de Handlebars, podemos definir nuestras funciones de ayuda. En nuestro caso, recortaremos los comentarios a 64 caracteres:

app.engine('hbs', exphbs({
    defaultLayout: 'main',
    extname: '.hbs',
    helpers: {
        getShortComment(comment) {
            if (comment.length < 64) {
                return comment;
            }

            return comment.substring(0, 61) + '...';
        }
    }
}));

Ahora usemos este ayudante en nuestro posts.hbs plantilla para resumir comentarios:

<div class="posts">
    <div class="row justify-content-center">
        {{#each posts}}
        <div class="col-lg-7" style="margin-top: 50px;">
            <div class="card">

                <img src="{{this.image}}" class="card-img-top" alt="...">
                <div class="card-body">
                    <h5 class="card-title">Posted by {{this.author}}</h5>

                    {{#if this.comments}}
                    <ul class="list-group">
                        {{#each this.comments}}
                        <li class="list-group-item">{{getShortComment this}}</li>
                        {{/each}}
                    </ul>
                    {{else}}
                    <ul class="list-group">
                        <li class="list-group-item">Be first to comment on this post</li>
                    </ul>
                    {{/if}}
                </div>
            </div>
        </div>
        {{/each}}
    </div>
</div>

Seguramente, los comentarios están recortados en nuestra página ahora:

Conclusión

En este artículo, cubrimos los conceptos básicos de Handlebars, un motor de plantillas para Node.js y JavaScript front-end. Usando Handlebars, podemos crear páginas web dinámicas que se renderizan en el lado del servidor o del cliente. Utilizando los condicionales, bucles, parciales y funciones de ayuda personalizadas de Handlebars, nuestras páginas web se convierten en algo más que HTML estático.

El código también está disponible en GitHub, como siempre. También puede encontrar más información sobre manillares en su página web oficial.

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad