Exemple de WAD adaptative avec Bootstrap/React et Node

11.3 Exemple de WAD adaptative avec Bootstrap/React et Node

Objectifs d'apprentissage

À la fin de cette section, vous serez capable de :

  • Créer une application web de gestion de tâches (Todo) avec Bootstrap, React et Node
  • Créer un dorsal (back end) Node
  • Construire le contrôleur
  • Mettre en place l'API REST
  • Créer le frontal (front end) React
  • Connecter le frontal (front end) React au dorsal (back end) Node

Dans la section précédente, vous avez créé une simple application de gestion de tâches (Todo) en utilisant Bootstrap et Django. Dans cette section, vous continuerez à utiliser Bootstrap pour créer une autre simple application de gestion de tâches. Mais au lieu de travailler avec Django, vous utiliserez React et Node. React, ou React.js, est une bibliothèque JavaScript populaire pour construire des interfaces utilisateur. Node, ou Node.js, est un environnement d'exécution JavaScript qui fournit aux utilisateurs les outils pour développer des applications web, ainsi que des serveurs, des scripts et des outils en ligne de commande.

Création d'une application web de gestion de tâches (Todo) avec Bootstrap, React et Node

Lors de la création d'une application web de gestion de tâches (Todo) utilisant React et Node, React sert de frontal, gérant l'interface utilisateur, ainsi que l'obtention et la définition des données via des requêtes HTTP en utilisant Axios. Node sert de dorsal, utilisant une API REST construite avec ExpressJS et la modélisation de données objet (ODM) de MongooseJS pour interagir avec une base de données MongoDB.

Prérequis

Pour construire l'application de gestion de tâches (Todo) en utilisant Bootstrap, React et Node, vous aurez besoin des composants logiciels suivants : React v17.0.2, Bootstrap v4.5.0, Node v14.17.5, ExpressJS v4.17.2, MongooseJS v6.1.9 et Axios v0.21.0. Pour commencer, téléchargez et installez Node.

Création du dorsal (back end) Node

Plusieurs étapes sont nécessaires pour construire le dorsal (back end) de l'application Node requis pour l'application de gestion de tâches (Todo). Cette section expliquera chacune de ces étapes.

Création de l'application Node

Avant de pouvoir créer le dorsal (back end) de l'application de gestion de tâches (Todo), vous devez créer une application Node. Pour ce faire, créez le répertoire nodebackend/ et naviguez-y. Ensuite, exécutez la commande suivante pour initialiser l'application Node :

$ npm init

Après avoir exécuté cette commande, suivez l'invite, qui est mise en évidence dans la Figure 11.30.

Capture d'écran des invites pour NodeJS.
Figure 11.30 Cette invite apparaît lorsque l'application Node est initialisée. (source : Copyright Rice University, OpenStax, sous licence CC BY 4.0)

Une fois l'initialisation de l'application Node terminée, un fichier package.json est généré, comme le montre le code suivant.

{
  "name": "nodebackend",
  "version": "1.0.0",
  "description": "Todo Web application with Bootstrap, ReactJS and NodeJS",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "bootstrap",
    "reactjs",
    "nodejs",
    "express",
    "mongodb",
    "rest",
    "api"
  ],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.1",
    "cors": "^2.8.5",
    "express": "^4.17.2",
    "mongoose": "^6.1.9"
  }
}

Mise en place du serveur web Express

Une fois l'application Node initialisée, l'étape suivante consiste à mettre en place le serveur web (web server) Express. Pour ce faire, utilisez la commande suivante pour installer Express, Mongoose (Mongoose est une bibliothèque pour MongoDB utilisée pour interagir avec MongoDB en facilitant la modélisation des données en tant qu'objets), et d'autres paquets dépendants dans le répertoire nodebackend/ :

$ npm install express mongoose body-parser cors –save

Ensuite, créez le serveur web Express en vous rendant dans le répertoire nodebackend/, créez le fichier server.js et ajoutez le code suivant.

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");

const app = express();

var corsOptions = {
  origin: "http://localhost:8081"
};

app.use(cors(corsOptions));

// parse requests of content-type - application/json
app.use(bodyParser.json());

// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));

// routes
app.get("/", (req, res) => {
  res.json({ message: "Welcome to the Todo Web App." });
});

require("./routes/todo.routes.js")(app);

// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

Une fois que vous utilisez le code pour importer Express, vous pouvez construire les API REST. Le paquet body-parser est utilisé pour créer et analyser l'objet de requête. Le paquet cors est utilisé comme intergiciel (middleware) pour Express qui active CORS. Le serveur web Express fonctionnera sur le port 8080 comme port par défaut. Utilisez la commande suivante pour démarrer le serveur :

$ node server.js

Dans un navigateur, naviguez vers http://localhost:8080/. La page suivante, illustrée dans la Figure 11.31, s'affiche.

Capture d'écran de la page de démarrage du serveur web Express.
Figure 11.31 Une fois le serveur web Express démarré, cette page devrait apparaître à l'adresse http://localhost:8080/. (rendu dans Node; source : Copyright Rice University, OpenStax, sous licence CC BY 4.0)

Configuration de la base de données MongoDB et de Mongoose

Après la création du serveur web Express, l'étape suivante consiste à configurer la base de données MongoDB et Mongoose. Pour ce faire, dans le répertoire nodebackend/, créez le répertoire config/. Ensuite, dans le répertoire config/, créez le fichier db.config.js et ajoutez le code suivant. Cela spécifie l'URL de connexion à la base de données MongoDB.

module.exports = {
    url: "mongodb://localhost:27017/todo_db"
  };

Une fois l'URL de connexion MongoDB configurée, l'étape suivante consiste à ajouter du code pour se connecter à la base de données en utilisant Mongoose. Pour ce faire, dans le répertoire nodebackend/, créez le répertoire models/. Dans le répertoire models/, créez le fichier index.js et ajoutez ce qui suit.

const dbConfig = require("../config/db.config.js");

const mongoose = require("mongoose");
mongoose.Promise = global.Promise;

const db = {};
db.mongoose = mongoose;
db.url = dbConfig.url;
db.todos = require("./todo.model.js")(mongoose);

module.exports = db;

Ensuite, le fichier nodebackend/server.js doit être mis à jour pour permettre au serveur web Express d'établir une connexion avec MongoDB. Le code correspondant est présenté dans l'extrait suivant.

const db = require("./models");
db.mongoose.connect(db.url, {
  useNewUrlParser: true
}).then(() => {
  console.log("Connected to the database successfully!")
}).catch(err => {
  console.log("Cannot connect to the database: " , err);
  process.exit();
});

Une fois le code de connexion à la base de données terminé, l'étape suivante consiste à créer le modèle Mongoose. Pour ce faire, dans le répertoire nodebackend/models/, créez le fichier todo.model.js et ajoutez le code suivant. Ce code définit un schéma Mongoose pour le modèle todos, ce qui entraîne la création d'une collection todos dans la base de données MongoDB.

module.exports = mongoose => {
  var schema = mongoose.Schema({
    title: {
      type: String,
      required: true,
    },
    content: {
      type: String,
      required: false,
    },
    created: {
      type: String,
      default: Date.now(),
      required: true,
    },
    due_date: {
      type: String,
      required: true,
    },
    category: {
      type: String,
      required: true,
    },
  });

  schema.method("toJSON", function() {
    const { __v, _id, ...object } = this.toObject();
    object.id = _id;
    return object;
  });
  const Todos = mongoose.model("todos", schema);
  return Todos;
};

Construction du contrôleur

L'étape suivante consiste à construire le contrôleur. Le contrôleur contient le code qui appelle les fonctions CRUD de Mongoose pour interagir avec la base de données MongoDB. Pour construire le contrôleur, dans le répertoire nodebackend/, créez le répertoire controllers/. Ensuite, dans le répertoire controllers/, créez le fichier todo.controller.js et ajoutez le code tel qu'illustré. Le code implémente les fonctions CRUD create, findAll, findOne, update, delete et deleteAll.

const  db = require("../models");
const Todos = db.todos;

// Create and Save a new Todo item
exports.create = (req, res) => {
  // Validate requred data
  if (!req.body.title) {
    res.status(400).send({ message: "Title can not be empty!" });
    return;
  }
  if (!req.body.category) {
    res.status(400).send({ message: "Category can not be empty!" });
    return;
  }
  if (!req.body.due_date) {
    res.status(400).send({ message: "Due date can not be empty!" });
    return;
  }

  // Create a todo item
  const todo = new Todos({
    title: req.body.title,
    content: req.body.content,
    category: req.body.category,
    due_date: req.body.due_date
  });

  // Save the todo item in the database
  todo
    .save(todo)
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "An error occurred while creating the todo item."
      });
    });
};

// Retrieve all todo items
exports.findAll = (req, res) => {
  const title = req.query.title;
  var condition = title ? { title: { $regex: new RegExp(title), $options: "i" } } : {};

  Todos.find(condition)
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "An error occurred while retrieving todo items."
      });
    });
};

// Retrieve a todo item by id
exports.findOne = (req, res) => {
  const id = req.params.id;

  Todos.findById(id)
    .then(data => {
      if (!data)
        res.status(404).send({ message: "Error finding todo item with id " + id });
      else res.send(data);
    })
    .catch(err => {
      res
        .status(500)
        .send({ message: "Error retrieving todo item with id=" + id });
    });
};

// Update a todo item by id
exports.update = (req, res) => {
  if (!req.body) {
    return res.status(400).send({
      message: "Specify a todo item to update. Todo item cannot be empty!"
    });
  }

  const id = req.params.id;

  Todos.findByIdAndUpdate(id, req.body, { useFindAndModify: false })
    .then(data => {
        if (!data) {
        res.status(404).send({
            message: `Cannot update todo item with id=${id}. Todo item was not found!`
        });
        } else res.send({ message: "The todo item was updated successfully." });
    })
    .catch(err => {
        res.status(500).send({
        message: "Error updating todo item with id=" + id
        });
    });
};

// Delete a todo item by id
exports.delete = (req, res) => {
  const id = req.params.id;

  Todos.findByIdAndRemove(id, { useFindAndModify: false })
    .then(data => {
        if (!data) {
            res.status(404).send({
            message: `Cannot delete todo item with id=${id}. Todo item was not found!`
            });
        } else {
            res.send({
            message: "The todo item was deleted successfully!"
            });
        }
        })
        .catch(err => {
        res.status(500).send({
            message: "Error deleting todo item with id=" + id
        });
    });
};

// Delete all todo items from the database
exports.deleteAll = (req, res) => {
    Todos.deleteMany({})
        .then(data => {
        res.send({
            message: `${data.deletedCount} All todo items were deleted successfully!`
        });
        })
        .catch(err => {
        res.status(500).send({
            message:
            err.message || "An error occurred while deleting all todo items."
        });
        });
};

Mise en place de l'API REST

Une fois le contrôleur construit, l'étape suivante consiste à mettre en place l'API REST. Un client enverra des requêtes HTTP (par exemple, GET, POST, PUT et DELETE) aux points d'accès de l'API REST qui déterminent comment le serveur gérera ces requêtes et fournira une réponse. Pour ce faire, les routes de l'API REST sont d'abord définies. Dans le répertoire nodebackend/, créez le répertoire routes/. Dans le répertoire routes/, créez le fichier todo.routes.js et ajoutez le code suivant. Dans ce code, les routes nécessitent un accès aux fonctions CRUD déclarées dans le contrôleur.

module .exports = app => {
  const todos = require("../controllers/todo.controller.js");
  var router = require("express").Router();

  // Create a new todo item
  router.post("/", todos.create);

  // Retrieve all todo items
  router.get("/", todos.findAll);

  // Retrieve a todo item by id
  router.get("/:id", todos.findOne);

  // Update a todo item by id
  router.put("/:id", todos.update);

  // Delete a todo item by id
  router.delete("/:id", todos.delete);

  // Delete all todo items
  router.delete("/", todos.deleteAll);

  app.use("/api/todos", router);

};

L'étape suivante consiste à mettre à jour nodebackend/server.js pour importer les routes présentées dans le code suivant.

//  routes
app.get("/", (req, res) => {
  res.json({ message: "Welcome to the Todo Web App." });
});

require("./routes/todo.routes.js")(app);

// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

L'étape suivante consiste à exécuter le serveur web Express pour tester les fonctions CRUD et interagir avec la base de données MongoDB. Pour ce faire, exécutez la commande suivante :

$ node server.js

Pour tester l'API REST, utilisez Postman, qui est un outil de test de plateforme API pouvant être utilisé comme client. Pour ce faire, suivez ces étapes :

  • Installez et lancez Postman.
  • Sélectionnez POST comme méthode de requête.
  • Entrez l'URL http://localhost:8080/api/todos pour tester la création d'un élément de tâche.
  • Dans le cadre de saisie des données, sélectionnez « Body » (corps).
  • Sélectionnez « raw » (brut) et « JSON » pour le format des données.
  • Entrez un objet JSON représentant un élément de tâche à créer, comme indiqué.
  • Cliquez sur Envoyer.

Après avoir suivi ces étapes, le cadre inférieur devrait recevoir une réponse avec le statut « 200 OK » indiquant que la requête a été traitée avec succès. Le corps de l'élément de tâche créé s'affichera également avec un champ d'identifiant généré. Vous pouvez utiliser cette méthode pour tester toutes les fonctions CRUD.

Création du frontal (front end) React

Une fois ces étapes terminées et le dorsal (back end) de l'application web de gestion de tâches (Todo) opérationnel, l'étape suivante consiste à implémenter le frontal (front end) en utilisant React. Cette section décrira les étapes pour y parvenir.

Création de l'application React

Pour créer l'application React, la première étape consiste à exécuter la commande suivante. Cela doit être fait dans un répertoire qui est extérieur et séparé du répertoire nodebackend/.

$ npx create-react-app reactfrontend

Une fois la commande exécutée, elle générera les fichiers de l'application React dans le répertoire reactfrontend/, comme le montre la Figure 11.32.

Liste des fichiers de l'application ReactJS dans reactfrontend.
Figure 11.32 Ceci montre les fichiers de l'application React dans le répertoire reactfrontend/. (rendu dans React par Meta Open Source; source : Copyright Rice University, OpenStax, sous licence CC BY 4.0)

L'étape suivante consiste à naviguer dans le répertoire reactfrontend/ et à lancer l'application React pour confirmer que l'application frontale (front end) React a été créée avec succès. À ce stade, cette application ne se connecte pas au dorsal (back end) Node, elle lance donc une page React par défaut. Exécutez la commande, qui lancera automatiquement une page de navigateur vers http://localhost:3000.

$ npm start

Vous verrez ce qui suit, comme illustré dans la Figure 11.33.

Capture d'écran de la page de démarrage de l'application frontale ReactJS.
Figure 11.33 Lorsque l'application frontale React est créée avec succès, cette page se lance à l'adresse http://localhost:3000. (source : React par Meta Open Source)

Installation de Bootstrap et d'autres dépendances

L'étape suivante consiste à installer les paquets pour Bootstrap et reactstrap, qui sont nécessaires pour utiliser Bootstrap dans une application React. Pour ce faire, dans le répertoire reactfrontend/, exécutez les commandes suivantes :

$ npm install [email protected] [email protected] --legacy-peer-deps
$ npm install react-bootstrap

Ensuite, importez le fichier CSS de Bootstrap4 dans l'application React. Pour ce faire, ouvrez reactfrontend/src/index.js et ajoutez l'importation Bootstrap suivante.

import  React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Création des composants React

L'étape suivante consiste à créer les composants React. L'interface utilisateur de React est rendue à partir du composant reactfrontend/src/App.js. Le fichier reactfrontend/src/App.js est généré lors de la création de l'application React. C'est le composant principal de l'application React, et tous les autres composants sont ajoutés au composant reactfrontend/src/App.js. Quelques importations sont ajoutées au fichier reactfrontend/src/App.js, comme le montre le code suivant. Cela inclut une importation pour App.css. Des règles CSS personnalisées sont ajoutées à reactfrontend/src/App.css.

//import logo from './logo.svg';
import './App.css';
import React, { Component } from "react";
import Modal from "./components/Modal";
import Nav from "./components/NavComponent";
import axios from "axios";
import { Button, Form, FormGroup, Label, Input } from 'reactstrap';

Pour comparer avec l'application web Django dans 11.2 Exemple de WAD adaptative avec Bootstrap et Django, des règles CSS personnalisées ont été ajoutées au fichier style.css dans le répertoire static/, comme décrit dans Création des modèles.

Ensuite, examinez le code suivant, qui montre que App est un composant de classe qui étend la classe Component de React. Toutes les données d'état sont ajoutées à la variable this.state dans le constructeur.

class  App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      todoChecked: false,
      todoList: [],
      categories: [],
      description: "",
      modal: false,
      activeItem: {
        title: "",
        description: "",
        completed: false,
      },
    };
  }

L'extrait de code suivant montre que chaque composant de classe doit inclure une render() fonction. Cette fonction renvoie les composants qui construisent l'interface utilisateur pour l'application web de gestion de tâches (Todo).

render () {
    return(
      <main>
        <Nav />
        <div className="container mt-5 pl-3">
          <h1>Todo List</h1>
          <Form>
            <div className="inputContainer">
                <FormGroup>
                  <Label htmlFor="description">Description</Label>
                  <Input type="text" id="description" name="description"
                      placeholder="Description"
                      value={this.state.description}
                      />
                </FormGroup>
            </div>
            <div className="inputContainer half last">
                <FormGroup>
                  <Label htmlFor="category">Category</Label>
                  <Input id="category" className="taskCategory" type="select" name="category_select"
                    value={this.state.contactType}
                    >
                    <option className="disabled" value="">Choose a category</option>
                    <option>Work</option>
                    <option>Personal</option>
                  </Input>
                </FormGroup>
            </div>
            <div className="inputContainer half last right">
              <FormGroup>
                  <Label htmlFor="description">Due Date</Label>
                  <Input type="text" id="description" name="description"
                      placeholder="Due Date (mm/dd/yyyy)"
                      value={this.state.description}
                      />
                </FormGroup>
            </div>
            <div className="row">
                <Button
                  className="taskAdd"
                  name="taskAdd" type="submit"
                >
                  Edit
                </Button>
                <Button
                  className="taskDelete ml-1"
                  name="taskDelete"
                  type="submit"
                  onclick="$('input#sublist').click();"
                >
                  Delete
                </Button>
            </div>
          </Form>
          <ul className="taskList">{this.renderItems()}</ul>
        </div>
      </main>
    )
  }

Connexion du frontal (front end) React au dorsal (back end) Node

L'étape finale pour créer l'application web de gestion de tâches (Todo) consiste à configurer l'application React afin qu'elle puisse effectuer des requêtes vers les points d'accès de l'API de l'application Node. L'application React utilise Axios pour récupérer des données en effectuant des requêtes vers un point d'accès donné. Pour installer Axios, exécutez la commande suivante dans le répertoire reactfrontend/ :

  $ npm install axios@0.21.1
  

Ensuite, ajoutez un proxy à l'application Node. Le proxy aidera à acheminer les requêtes API de l'application React vers http://localhost:8080 où l'application Node recevra et traitera les requêtes. Pour ce faire, ouvrez le fichier reactfrontend/package.json et ajoutez le proxy suivant.

  { 
    "name": "reactfrontend",
    "version": "0.1.0",
    "private": true,
    "proxy": "http://localhost:8080",
    "dependencies": {
  

L'étape suivante consiste à créer un service sur le frontal pour envoyer des requêtes HTTP au dorsal. Ce processus utilise Axios et est similaire à la façon dont les routes ont été créées côté dorsal. Le service exportera des fonctions CRUD et une méthode de recherche pour interagir avec la base de données MongoDB. Pour ce faire, dans le répertoire reactfrontend/, créez le répertoire services/. Ensuite, dans le répertoire services/, créez le fichier TodoService.js et ajoutez le code suivant.

import axios from "axios";

const getAll = () => {
  return axios.get("/api/todos/");
};

const get = id => {
  return axios.get(`/api/todos/${id}`);
};

const create = data => {
  return axios.post("/api/todos", data);
};

const update = (id, data) => {
  return axios.put(`/api/todos/${id}`, data);
};

const remove = id => {
  return axios.delete(`/api/todos/${id}`);
};

const removeAll = () => {
  return axios.delete(`/api/todos`);
};

export default {
  getAll,
  get,
  create,
  update,
  remove,
  removeAll
};

Enfin, pour compléter cette étape, mettez à jour reactfrontend/App.js comme suit pour appeler les services.

handleSubmit = (item) => {
  var data = {
    id: item.id,
    title: item.title,
    content: item.content,
    due_date: item.due_date,
    category: item.category
  };

  TodoService.create(data)
    .then((res) => this.refreshList())
    .catch((err) => console.log(err));
};
handleUpdate = (item) => {
  var data = {
    id: item.id,
    title: item.title,
    content: item.content,
    due_date: item.due_date,
    category: item.category
  };
  TodoService.update(item.id, data)
    .then((res) => this.refreshList())
    .catch((err) => console.log(err));
};
handleDelete = (item) => {
  TodoService.remove(item.id)
    .then((res) => this.refreshList())
    .catch((err) => console.log(err));
};
refreshList = () => {
  TodoService.getAll()
  .then((res) => this.setState({ todoList: res.data }))
  .catch((err) => console.log(err));
}

Une fois cette opération terminée, exécutez le serveur web (web server) Express et l'application React en utilisant les commandes suivantes :

$ node server.js
$ npm start

Une fois cela fait, utilisez le formulaire présenté dans la Figure 11.34 pour créer un nouvel élément de tâche.

Capture d'écran du formulaire de liste de tâches pour créer des éléments.
Figure 11.34 Une fois l'application web de gestion de tâches (Todo) créée en utilisant Bootstrap avec React et Node, ce formulaire peut être utilisé pour créer des éléments de tâche. (rendu avec Bootstrap, sous licence MIT copyright 2018 Twitter, avec React par Meta Open Source et Node; source : Copyright Rice University, OpenStax, sous licence CC BY 4.0)