Cómo usar Jade y manubrios en Express.js

Odiaba a Jade como lo hacen muchos otros desarrolladores de Node.js. Pero cambié 180 después de darme cuenta de que tiene muchas funciones.

En Storify y DocuSign usamos Jade para TODO. Usamos Jade incluso en el navegador. Hay un pequeño truco llamado navegador de jade. Fue desarrollado por gente de Storify. Lo mantuve por un tiempo.

Lo gracioso es que el equipo de DocuSign usó el navegador de jade mucho antes de conocerme. Juran que me contrataron sin saber que yo estaba involucrado en esa biblioteca. :-)

De todos modos, después de cubrir Jade y Handlebars en publicaciones anteriores, es hora de aplicarlos para hacer un trabajo real. En esta publicación, cubriré:

  • Uso de Jade y Handlebars en Express.js 4
  • Proyecto:agregar plantillas de Jade al blog

De forma predeterminada, Express.js 4.x (y 3.x) utiliza una extensión de plantilla proporcionada a res.render método o la extensión predeterminada establecida por el view engine configuración, para invocar el require y __express métodos en la biblioteca de plantillas. En otras palabras, para que Express.js utilice una biblioteca de motor de plantillas lista para usar, esa biblioteca debe tener el __express método.

Cuando la biblioteca del motor de plantillas no proporciona el __express method , o uno similar con (path , options , callback )parámetros, se recomienda que utilice Consolidate.js (https://github.com/visionmedia/consolidate.js/).

Este es un ejemplo rápido de Consolidate.js para Express.js 4 (versión 4.2.0 y la versión de Consolidate es 0.10.0):

var express = require('express'),
  cons = require('consolidate'),
  app = express()

app.engine('html', cons.swig)

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

var platforms = [
  { name: 'node' },
  { name: 'ruby' },
  { name: 'python' }
]

app.get('/', function(req, res){
  res.render('index', {
    title: 'Consolidate This'
  })
})
app.get('/platforms', function(req, res){
  res.render('platforms', {
    title: 'Platforms',
    platforms: platforms
  })
})

app.listen(3000)
console.log('Express server listening on port 3000')

Por lo general, el código fuente está en el repositorio de GitHub y el fragmento está en el
ch4/consolidate carpeta.

Para obtener más información sobre cómo configurar los ajustes de Express.js y usar Consolidate.js, consulte el libro Pro Express.js 4 (Apress, 2014).

Jade y Express.js

Jade es compatible con Express.js listo para usar (de hecho, es la opción predeterminada), por lo que para usar Jade con Express.js, solo necesita instalar un módulo de motor de plantilla jade (https://www.npmjs.org /package/jade) y proporcione una extensión a Express.js a través de view engine ajuste).

Por ejemplo, en el archivo del servidor principal establecemos la configuración:

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

■ Nota Si usa $ express <app_name> herramienta de línea de comandos, puede agregar la opción de compatibilidad con el motor, es decir, –e opción para EJS y –H para Hogan. Esto agregará EJS o Hogan automáticamente a su nuevo proyecto. Sin ninguna de estas opciones, express-generator (versiones 4.0.0–4.2.0) usará Jade.

En el archivo de ruta, podemos llamar a la plantilla, por ejemplo, views/page.jade  (el views el nombre de la carpeta es otro valor predeterminado de Express.js, que se puede sobrescribir con el
view ajuste):

[Nota al margen]

Leer publicaciones de blog es bueno, pero ver cursos en video es aún mejor porque son más atractivos.

Muchos desarrolladores se quejaron de la falta de material de video de calidad asequible en Node. Es una distracción ver videos de YouTube y una locura pagar $ 500 por un curso de video de Node.

Visite Node University, que tiene cursos de video GRATUITOS en Node:node.university.

[Fin de la nota al margen]

app.get('/page', function(req, res, next){
  //get the data dynamically
  res.render('page', data);
});

Si no especificamos el views engine configuración, entonces la extensión debe pasarse explícitamente a res.render() :

res.render('page.jade', data);

Manillares y Express.js

A diferencia de Jade, la biblioteca Handlebars de http://handlebarsjs.com/ no viene con el método __express, pero hay algunas opciones para hacer que Handlebars funcione con Express.js:

  • consolidate :una navaja suiza de bibliotecas de motor de plantilla Express.js (que se muestra arriba)
  • hbs (https://github.com/donpark/hbs):biblioteca contenedora para manillares
  • express-Handlebars (file://pchns-f01/TECHNOLOGY/BPR/Techutilities/Apress/Apress%20Outline/express3-handlebars ):a pesar del nombre, este módulo debería funcionar bien con Express.js 4 y con la versión 3.x

Así es como podemos usar hbs enfoque (extensión hbs ). Dentro del código típico de la aplicación Express.js (es decir, la sección de configuración del archivo principal que lanzamos con el $ node comando) escribe las siguientes declaraciones:

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

O, si prefiere otra extensión, como html , vemos lo siguiente:

...
app.set('view engine', 'html');
pp.engine('html', require('hbs').__express); 
...

El express3-handlebars el uso del enfoque es el siguiente:

...
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');
...

Proyecto:Adición de plantillas de Jade al blog

Por último, podemos continuar con Blog. En esta sección agregamos páginas principales usando Jade, además agregamos un diseño y algunos parciales:

  • layout.jade :plantilla global para toda la aplicación
  • index.jade :página de inicio con la lista de publicaciones
  • article.jade :página de artículo individual
  • login.jade :página con un formulario de inicio de sesión
  • post.jade :página para agregar un nuevo artículo
  • admin.jade :página para administrar artículos después de iniciar sesión

La demostración en la que conectaremos la base de datos MongoDB está en ch5 carpeta del repositorio PracticalNode de GitHub:https://github.com/azat-co/practicalnode. Entonces, el código fuente de las plantillas de Jade es exactamente el mismo que en ese proyecto de GitHub. Siéntase libre de copiarlo desde allí o siga las instrucciones a continuación.

diseño.jade

Abramos el proyecto donde lo dejamos en el ch3 desde https://github.com/azat-co/practicalnode y agrega layout.jade con la declaración del tipo de documento:

doctype html

■ Nota doctype 5 quedó en desuso alrededor de v1.0. Ahora podemos agregar las etiquetas principales de la página:

html
  head

El título de cada página se proporciona desde el appTitle variable (alias, local):

title= appTitle

Luego, en el head etiqueta, enumeramos todos los activos front-end que necesitamos en toda la aplicación (en cada página):

script(type="text/javascript", src="js/jquery-2.0.3.min.js")
link(rel='stylesheet', href='https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/bootstrap-3.0.2/css/bootstrap.min.css')
link(rel="stylesheet", href="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/bootstrap-3.0.2/css/bootstrap-theme.min.css")
link(rel="stylesheet", href="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/style.css")
script(type="text/javascript", src="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/bootstrap-3.0.2/js/bootstrap.min.js")
script(type="text/javascript", src="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/js/blog.js")
meta(name="viewport", content="width=device-width, initial-scale=1.0")

El contenido principal vive en body que tiene el mismo nivel de sangría que head :

body

Dentro del cuerpo, escribimos una ID y algunas clases para los estilos que agregaremos más adelante:

#wrap
  .container

El appTitle El valor se imprime dinámicamente, pero el p.lead elemento solo tiene textos:

h1.page-header= appTitle
p.lead Welcome to example from Express.js Experience by&nbsp;
a(href="http://twitter.com/azat_co") @azat_co
|. Please enjoy.

El block las secciones pueden sobrescribirse con las plantillas secundarias (plantillas que amplían este archivo):

block page
block header
  div

El menú es un parcial (es decir, un include) que se almacena en el views/includes carpeta. Tenga en cuenta la ausencia de comillas:

include includes/menu

En este bloque, podemos mostrar mensajes para los usuarios:

block alert
  div.alert.alert-warning.hidden

El contenido principal va en este bloque:

.content
  block content

Por último, el pie de página tiene el siguiente aspecto:

block footer
  footer
    .container
      p
        | Copyright &copy; 2014 | Issues? Submit to a(href="https://github.com/azat-co/blog-express/issues") GitHub
        | .

El código completo de layout.jade es el siguiente:

doctype html
html
  head
    title= appTitle
    script(type="text/javascript", src="js/jquery-2.0.3.min.js")
    link(rel="stylesheet", href="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/bootstrap-3.0.2/css/bootstrap.min.css")
    link(rel="stylesheet", href="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/bootstrap-3.0.2/css/bootstrap-theme.min.css")
    link(rel="stylesheet", href="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/style.css")
    script(type="text/javascript", src="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/bootstrap-3.0.2/js/bootstrap.min.js")
    script(type="text/javascript", src="https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/js/blog.js")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")
  body
    #wrap
      .container
        h1.page-header= appTitle
        p.lead Welcome to example from Express.js Experience by&nbsp;
          a(href="http://twitter.com/azat_co") @azat_co
          |. Please enjoy.
        block page
        block header
          div
            include includes/menu
        block alert
          div.alert.alert-warning.hidden
        .content
          block content
    block footer
      footer
        .container
          p
            | Copyright &copy; 2014 | Issues? Submit to
            a(href=" https://github.com/azat-co/blog-express/issues") GitHub
            | .

index.jade

Ahora podemos ver la plantilla de la página de inicio index.jade que extiende el diseño:

extends layout

Configuramos el menu variable a index , por lo que el menú incluye (es decir, menu.jade ) puede determinar qué pestaña mostrar como activa:

block page
  - var menu = 'index'

El contenido principal con la lista de artículos que proviene de locals es el siguiente:

block content
  if (articles.length === 0)
    | There's no published content yet.
    a(href="/login") Log in
    | to post and publish.
  else
    each article, index in articles
      div
        h2
          a(href="/articles/#{article.slug}")= article.title

El código completo de index.jade es el siguiente:

extends layout

block page
  - var menu = 'index'
block content
  if (articles.length === 0)
    | There's no published content yet.
    a(href="/login") Log in
    | to post and publish.
  else
    each article, index in articles
      div
        h2
          a(href="/articles/#{article.slug}")= article.title

La Figura 4–4 muestra el aspecto de la página de inicio después de agregar hojas de estilo.

Figura 4–4. La página de inicio

artículo.jade

La página del artículo individual (Figura 4–5) es relativamente poco sofisticada porque la mayoría de los elementos están resumidos en layout.jade :

extends layout

block content
  p
    h1= title
    p= text 

Figura 4–5. La página del artículo

iniciar sesión.jade

Del mismo modo, la página de inicio de sesión contiene solo un formulario y un botón (con las clases/marcas de Twitter Bootstrap):

extends layout

block page
  - var menu = 'login'

block content
  .col-md-4.col-md-offset-4
    h2 Log in
    div= error
    div
      form(action="/login", method="POST")
        p
          input.form-control(name="email", type="text", placeholder="[email protected]")
        p
          input.form-control(name="password", type="password", placeholder="***")
        p
          button.btn.btn-lg.btn-primary.btn-block(type="submit") Log in
        p
          input.form-control(name="password", type="password", placeholder="***")
        p
          button.btn.btn-lg.btn-primary.btn-block(type="submit") Log in

La Figura 4–6 muestra el aspecto de la página de inicio de sesión.

Figura 4–6. La página de inicio de sesión

post.jade

La página de publicación (Figura 4–7) tiene otra forma. Esta vez, el formulario contiene un elemento de área de texto:

extends layout
block page
  - var menu = 'post'
block content 
h2 Post an Article
div= error
div.col-md-8
  form(action="/post", method="POST", role="form")
    div.form-group
      label(for="title") Title
      input#title.form-control(name="title", type="text", placeholder="JavaScript is good")
    div.form-group
      label(for="slug") Slug
      input#slug.form-control(name="slug", type="text", placeholder="js-good")
      span.help-block This string will be used in the URL.
    div.form-group
      label(for="text") Text
      textarea#text.form-control(rows="5", name="text", placeholder="Text")
    p
      button.btn.btn-primary(type="submit") Save 

Figura 4–7. La página de publicación

admin.jade

La página de administración (Figura 4–8) tiene un bucle de artículos al igual que la página de inicio. Además, podemos incluir un script front-end (js/admin.js ) específico de esta página:

extends layout

block page
  - var menu = 'admin'

block content
  div.admin
    if (articles.length === 0 )
      p
        | Nothing to display. Add a new
        a(href="/post") article
        |.
    else

      table.table.table-stripped
        thead
          tr
            th(colspan="2") Actions
            th Post Title
        tbody
          each article, index in articles
            tr(data-id="#{article._id}", class=(!article.published)?'unpublished':'')
              td.action
                button.btn.btn-danger.btn-sm.remove(type="button")
                  span.glyphicon.glyphicon-remove(title="Remove")
              td.action
                button.btn.btn-default.btn-sm.publish(type="button")
                  span.glyphicon(class=(article.published)?"glyphicon-pause":"glyphicon-play",
title=(article.published)?"Unpublish":"Publish")
              td= article.title
      script(type="text/javascript", src="js/admin.js") 

Figura 4–8. La página de administración

Usamos interpolación para imprimir ID de artículos como atributos data-id :

tr(data-id="#{article._id}", class=(!article.published)?'unpublished':'')

Y se usa un operador condicional (ternario) (https://github.com/donpark/hbs) para clases y atributos de título. Recuerda, ¡es JavaScript!

                  span.glyphicon(class=(article.published)?"glyphicon-pause":"glyphicon-play",
title=(article.published)?"Unpublish":"Publish") 

Resumen

Aprendió sobre las plantillas de Jade y Handlebars (variables, iteraciones, condiciones, parciales, sin escape, etc.) y cómo usarlas en un script independiente de Node.js o dentro de Express.js. Además, las páginas principales de Blog se crearon con Jade.

En otro tutorial, examinamos un aspecto importante del desarrollo web moderno y la ingeniería de software:el desarrollo basado en pruebas. Examinamos el módulo Mocha y escribimos algunas pruebas para Blog con un verdadero estilo TDD/BDD.

El ejemplo con una base de datos agregada a Blog para completar estas plantillas está en ch5 de https://github.com/azat-co/practicalnode. ¡Le muestra cómo convertir las plantillas de Jade en páginas HTML funcionales!