Una introducció a expressjs

Lectura de 6 minuts Publicat el 2021-11-18

Una introducció a expressjs per a crear pàgines web dinàmiques.

Que és expressjs?

És una web framework minimalista per a nodejs.

Gràcies al seu minimalisme és bastant fàcil d'aprendre i fer funcionar ràpidament.

Crear un projecte expressjs

Per anar més rápid, utilitzarem un generador de projectes:

npm install -g express-generator

Si mirem l'ajuda del programa:

 express --help

  Usage: express [options] [dir]

  Options:

        --version        output the version number
    -e, --ejs            add ejs engine support
        --pug            add pug engine support
        --hbs            add handlebars engine support
    -H, --hogan          add hogan.js engine support
    -v, --view <engine>  add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
        --no-view        use static html instead of view engine
    -c, --css <engine>   add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
        --git            add .gitignore
    -f, --force          force on non-empty directory
    -h, --help           output usage information

Podem veure que a l'hora de generar el projecte podem escollir diferents motors per les plantilles i el CSS, en el nostre cas utilitzarem ejs i cap motor de css:

express --view=ejs intro-express

Entra al directori i instal·la les dependències:

cd intro-express
npm install

Estructura del projecte

El generador ha creat els següents directoris i arxius:

.
├── app.js
├── bin
   └── www
├── package.json
├── public
   ├── images
   ├── javascripts
   └── stylesheets
       └── style.css
├── routes
   ├── index.js
   └── users.js
└── views
    ├── error.ejs
    └── index.ejs

7 directories, 8 files

El directori bin

Aquest directori conté un únic arxiu 'www', aquest arxiu s'encarrega de crear el servidor http, no cal modificar-lo massa.

El directori public

Aquest directori conte els arxius estàtics de la teva pàgina web com imatges, fitxers JavaScript utilitzats per part del client i estils CSS.

El directori routes

Aquí és on resideix el codi per definir les rutes que el nostre servidor http, per exemple users.js té el codi que s'encarrega de respondre a peticions GET en la ruta /users

El directori views

Aquí és on estan les plantilles que generen codi HTML quan són renderitzades, això ens permet tenir valors dinàmics, bucles, etc.

El fitxer app.js

Aquest fitxer és el cor de l'aplicació, aquí és on es configuren tots els middlewares com el registre (logger), quins motors fer servir (ejs, sass, etc) i les rutes definides al directori routes.

També podem trobar el error handler que s'encarrega de crear una resposta en cas que hi hagui un error, com per exemple quan la ruta demanada no existeix.

Afegir routes

Al directori routes, crear un nou fitxer: todo.js, en aquest fitxer afegirem rutes per implementar la metodologia CRUD:

Primer creem el nostre router i l'exportem.

// todos.js

var express = require('express');
var router = express.Router();

// Rutes aquí!

module.exports = router;

Després al fitxer app.js l'importem i l'afegim a la ruta /todos:

// app.js

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
// Importem el router
var todosRouter = require('./routes/todos');

// ...

app.use('/', indexRouter);
app.use('/users', usersRouter);
// L'afegim a la ruta /todos
app.use('/todos', todosRouter);

Tornem al fitxer todos.js, ara crearem un array on guardarem les tasques (todos) i crearem la primera ruta per veure la llista de tasques:

// todos.js

// ...

const tasques = [{
    titol: "Crear una pàgina web.",
    completada: false,
    creada: new Date()
}];

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('todos', { title: 'Les meves tasques', tasques: tasques });
});

module.exports = router;

La funció res.render s'encarrega de cridar el motor de renderitzat configurat, en el nostre cas ejs, el primer argument és el nom del fitxer sense cap extensió (e.g .ejs) i el segon argument és un objecte amb les dades dinàmiques que utilitzarà la plantilla.

A la carpeta views creem la plantilla todos.ejs que s'encarregarà de mostra la llista de tasques:

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Tasques:</p>
    <ul>
    <% for(var tasca of tasques ) { %>
        <li>
            <p>Títol: <%= tasca.titol %></p>
            <p>Completada: <%= tasca.completada %></p>
            <p>Creada: <%= tasca.creada %></p>
        </li>
    <% } %>
    </ul>
  </body>
</html>

El motor ejs ens permet utilitzar javascript (més o menys) directament al nostre fitxer html, en aquest cas, si volem mostrar el contingut d'una variable utilitzem:

<%= variable %>

Per fer control de flux (for, if, etc)

<% for(var x in y) { %>
    <%= x %>
<% } %>

Pots aprendre més aquí: ejs.co

Si encenem el servidor amb npm start i anem a http://localhost:3000/todos podrem veure la llista de tasques.

Afegir una tasca

Per poder afegir una tasca hem de crear una ruta POST on subministrar les dades d'aquesta nova tasca.

L'argument req.body conté les dades proveïdes pel formulari.

El nostre req.body conté la propietat titol perquè hem creat un formulari amb un input el qual té "titol" a l'atribut "name".

Per exemple:

<input name="titol" type="text">

Aquí el codi de la ruta:

// todos.js

router.post("/", function (req, res, next) {
  // Verifiquem que el títol sigui vàlid.
  // També es pot utilitzar una llibreria com 
  // Yup (https://github.com/jquense/yup) que facilita la validació de dades.
  if (
    !req.body.titol ||
    typeof req.body.titol !== "string" ||
    req.body.titol === "" ||
    req.body.titol.length > 150
  ) {
    return res.render("todos", {
      title: "Les meves tasques",
      todos: todos,
      msg: "El títol es invàlid.",
    });
  }

  let tasca = {
    completada: false,
    crada: new Date(),
    titol: req.body.titol,
  };

  tasques.push(tasca);
  res.render("todos", { title: "Les meves tasques", tasques: tasques, msg: "Tasca afegida.", });
});

Després hem d'afegir el formulari al fitxer todos.ejs (just després del ul)

<form action="/todos" method="post">
  <input name="titol" type="text" placeholder="Títol">
  <input type="submit" value="Afegir">
</form>
<% if (locals.msg) { %>
  <p><%= msg %></p>
<% } %>

També hem afegit l'habilitat de mostrar un missatge en afegir una tasca.

Eliminar i actualitzar una tasca

Per fer això, utilitzarem l'índex de l'array com a id per identificar la tasca.

Primer, hem d'actualitzar el codi que mostra les tasques afegint dos formularis, un per actualitzar i l'altre per eliminar:

<ul>
  <% for(var index in tasques ) { %>
  <% let tasca = tasques[index]; %>
  <li>
    <p>Títol: <%= tasca.titol %></p>
    <p>Completada: <%= tasca.completada %></p>
    <p>Creada: <%= tasca.creada %></p>
    <% if (!tasca.completada) { %>
      <form action=<%= `/todos/${index}/completada` %> method="post">
        <input type="submit" value="Cambiar a fet">
      </form>
    <% } %>
    <form action=<%= `/todos/${index}/eliminar` %> method="post">
      <input type="submit" value="Eliminar">
    </form>
  </li>
  <% } %>
</ul>

En la nostra aplicació hem d'afegir dues rutes més, per actualitzar i per eliminar.

Aquestes rutes hauran de comprovar que l'índex és vàlid.

// Ruta per actualitzar una tasca com a completada.
router.post("/:index/completada", function (req, res, next) {
  var index = parseInt(req.params.index);

  // Comprovem que l'index sigui vàlid.
  if(isNaN(index) || index < 0 || index >= tasques.length) {
    return new Error("L'index no és válid.")
  }

  tasques[index].completada = true;

  res.render("todos", { title: "Les meves tasques", tasques: tasques, msg: "Tasca actualitzada.", });
});

// Ruta per eliminar una tasca .
router.post("/:index/eliminar", function (req, res, next) {
  var index = parseInt(req.params.index);

  // Comprovem que l'index sigui vàlid.
  if(isNaN(index) || index < 0 || index >= tasques.length) {
    return new Error("L'index no és válid.")
  }

  tasques.splice(index, 1);

  res.render("todos", { title: "Les meves tasques", tasques: tasques, msg: "Tasca eliminada.", });
});

El resultat:

Página web

Conclusió

Fins aquí arriba aquesta introducció, pots trobar el repositori amb tots els arxius aquí:

git.struct.cat/intro-express.git