Node.js : Express, gestion des liens actifs avec Handlebars

Node.js : Express, gestion des liens actifs avec Handlebars
Categories:
Posté le

La gestion de l'indication de la page actuelle ou "active" est une problématique qui revient très souvent lors du développement du routing d'une application web.

Une manière de gérer cette problématique est d'ajouter une class css 'active' sur la balise menu du lien :

Se trouve alors comme problématique l'intégration de cette logique de routing dans le moteur de template.

Pour cet exemple nous allons nous tourner vers Express, qui est probablement le framework le plus connu de la communauté Node.js.

Mise en place d'Express

Générer un projet Express grâce à la CLI fournit :

$ npx express-generator --view=hbs

Puis installer les dépendances :

$ npm i

Enfin pour démarrer le serveur HTTP :

$ PORT=8900  npm start

Puis de vous rendre sur http://localhost:8900/

Pour nous simplifier de développement nous allons utiliser le hot realoading qui va nous éviter de redémarrer l'application à chaque modification.

$ PORT=8900 npx nodemon bin/www --watch routes --watch views --watch helpers --watch app.js

Les sources du projet d'exemple : https://github.com/Zenicheck/blog/tree/master/active-link-express

Pour cet exemple nous allons utiliser le moteur de template Handlebars, bien connu dans la communauté JS mais également dans d'autres stacks grâce à sa grande compatibilité avec Mustache.

À noter que l'implémentation change dans le détail avec l'utilisation de différents moteurs de template, la logique elle, restant la même.

La première chose à faire est donc de définir cette class css :
/* stylesheets/style.css */
a {
  color: #00B7FF;
}

a.active {
  color: rgb(17, 0, 255);
}
Puis de définir les routes de notre application :
/* routes/index.mjs */
import express from 'express'
import { addReq } from '../helpers/helpers.mjs'

const router = express.Router()

/* GET home page. */
router.get('/', (req, res) => {
  res.render('index', addReq(req)({ title: 'Accueil' }))
})

/* GET specific service page. */
router.get('/service/:id', (req, res) => {
  res.render('service', addReq(req)({ title: 'Service' }))
})

/* GET service page. */
router.get('/service', (req, res) => {
  res.render('service', addReq(req)({ title: 'Nos services' }))
})

/* GET contact page. */
router.get('/contact', (req, res) => {
  res.render('contact', addReq(req)({ title: 'Nous contacter' }))
})

export default router
Ainsi que de la méthode addReq qui va nous permettre de passer la requête à notre moteur de template :
/* helpers/helpers.mjs */

const addReq = req => e => ({ req, ...e })

export { addReq }
Ainsi que la base du layout pour les vues :
<!-- views/layout.hbs -->
<!DOCTYPE html>
<html>
<head>
  <title>{{title}}</title>
  <link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
  <ul>
    <li><a href="/">Accueil</a></li>
    <li><a href="/service">Nos expertises</a></li>
    <li><a href="/contact">Nous contacter</a></li>
  </ul>
  {{{body}}}
</body>
</html>

Ce qui donne le visuel :

Implémentation de la logique d'ajout de la class active :

L'objectif est donc maintenant de pouvoir ajouter la class 'active' sur le bon lien en fonction de l'url actuelle.

C'est la raison pour laquelle req est passée à Handlebars afin d'obtenir le path.

Il s'agit maintenant de pouvoir comparer le path actuel avec le path de la route.

Pour cela nous allons définir deux helpers pour simplifier ces comparaisons dans Handlebars :
import hbs from 'hbs'
/*
** ...
*/

/* app.mjs */
hbs.registerHelper('isActive', (url, dest) => url && url.indexOf(dest) != -1)
hbs.registerHelper('eq', (a, b) => a == b)

/*
** ...
*/

L'obtention de path  actuel passe par la propriété originalUrl de l'objet req.

Puis ajouter les comparaisons pour chaque route en faisant une comparaison stricte sur '/' :
<!-- views/layout -->
  <ul>
    <li><a href="/" class="{{# if (eq req.originalUrl '/') }}active{{/if}}">Accueil</a></li>
    <li><a href="/service" class="{{# if (isActive req.originalUrl '/service') }}active{{/if}}">Nos expertises</a></li>
    <li><a href="/contact" class="{{# if (isActive req.originalUrl '/contact') }}active{{/if}}">Nous contacter</a></li>
  </ul>

Ce qui donne pour l'url : http://localhost:8900/service

L'avantage de cette technique est que l'url suivante : http://localhost:8900/service/techs fonctionne également et permet donc de gérer les paramètres des routes sans modifier cette logique.

Sources : https://github.com/Zenicheck/blog/tree/master/active-link-express


Si vous cherchez un développeur Node.js afin de réaliser votre site ou de vous accompagner sur une autre problématique n'hésitez pas à me contacter !