NodeAtlas ne se contente pas que de faciliter la génération de page web en fonction de variable dans les fichiers de variation. NodeAtlas vous permet également d'intéragir avec le contenu des fichiers variations ou avec le DOM généré en fonction ;
Pour cela, il vous est possible d'intéragir à divers endroit (Hooks) du cycle de vie de création d'une page grâce à un contrôleur commun (commonController
) et à un controlleur spécifique à chaque page (routes[<route>].controller
).
Voici à quoi peut ressembler un webconfig.json
permettant d'atteindre tous les points du cycle de vie d'une page.
{
"controllersRelativePath": "controllers",
"commonController": "common.js",
"routes": {
"/": {
"template": "index.htm",
"variation": "index.json",
"controller": "index.json"
}
}
}
Note : Si controllersRelativePath n'est pas présent dans « webconfig.json », par défaut le dossier des controlleurs est bien controllers. controllersRelativePath est donc utile seulement pour changer le nom/chemin du répertoire.
et voici le détail des endroits ou vous pouvez intervenir :
Démarrage de NodeAtlas
Initialisation des modules
- loadModules --> à manipuler depuis le fichier
commonController
(common.js
dans l'exemple).Initialisation des Sessions
- setSessions --> à manipuler depuis le fichier
commonController
(common.js
dans l'exemple).Initialisation de la configuration du serveur
- setConfigurations --> à manipuler depuis le fichier
commonController
(common.js
dans l'exemple).Initialisation des routes
- setRoutes --> à manipuler depuis le fichier
commonController
(common.js
dans l'exemple).Lancement du serveur web
Requête/Réponse HTTP de NodeAtlas
Traitement de la Request du Client
changeVariation --> à manipuler depuis le fichier
commonController
(common.js
dans l'exemple).changeVariation --> à manipuler depuis le fichier
routes[<route>].controller
(index.js
dans l'exemple).Assemblage des Templates et Compilation des Variations => DOM complet de la Réponse.
changeDom --> à manipuler depuis le fichier
commonController
(common.js
dans l'exemple).changeDom --> à manipuler depuis le fichier
routes[<route>].controller
(index.js
dans l'exemple).Envoi de la Response au Client
Pour intercepter les variations, vous pouvez soit utiliser le contrôleur commun pour tout le site et/ou également le contrôleur par page.
Voici un exemple utilisant les deux interceptions, d'abord la commune au deux pages, puis celle de chaque page :
{
"commonController": "common.js",
"commonVariation": "common.json",
"routes": {
"/": {
"template": "index.htm",
"variation": "index.json",
"controller": "index.js"
}
}
}
avec cet ensemble de fichier :
├─ components/
│ ├─ head.htm
│ └─ foot.htm
├─ variations/
│ ├─ common.json
│ └─ index.json
├─ controllers/
│ ├─ common.js
│ └─ index.js
├─ templates/
│ └─ index.htm
└─ webconfig.json
En demandant la page http://localhost/?title=Haeresis
en POST avec une variable example=Ceci+est+un+test
dans le corp de requête, les fichiers suivants (entre autre) seront utilisés :
variations/common.json
{
"titleWebsite": "Titre du site"
}
variations/index.json
{
"titlePage": "Bienvenue",
"content": "<p>C'est la page d'accueil.</p>"
}
templates/index.htm
<%- include('head.htm') %>
<div class="title"><%- common.titleWebsite %></div>
<div>
<h1><%- specific.titlePage %></h1>
<%- specific.content %>
</div>
<%- include('foot.htm') %>
controllers/common.js
// On intervient avant que les variables soient injectées dans le système de template.
// Ce code sera exécuté pour toute request HTTP, toute page confondue.
exports.changeVariation = function (params, next) {
var variation = params.variation,
request = params.request,
response = params.response;
// Ici on modifie les variables de variations.
console.log(variation.common.titleWebsite); // "Titre du site"
console.log(variation.specific.titlePage); // "Bienvenue"
console.log(variation.specific.content); // "C'est la page d'accueil."
if (request.query["title"]) {
variation.specific.titlePage = variation.specific.titlePage + " " + request.query.title;
}
if (request.body["example"]) {
variation.specific.content = request.body.example;
}
console.log(variation.common.titleWebsite); // "Titre du site"
console.log(variation.specific.titlePage); // "Bienvenue Haeresis"
console.log(variation.specific.content); // "Ceci est un test"
// On ré-injecte les modifications.
next(variation);
};
controllers/index.js
// On intervient avant que les variables soient injectées dans le système de template.
// Ce code sera exécuté uniquement lors de la demande de la page « / ».
exports.changeVariation = function (params, next) {
var variation = params.variation,
request = params.request,
response = params.response;
// Ici on modifie les variables de variations.
console.log(variation.common.titleWebsite); // "Titre du site"
console.log(variation.specific.titlePage); // "Bienvenue Haeresis"
console.log(variation.specific.content); // "Ceci est un test"
variation.common.titleWebsite = "C'est l'accueil, c'est tout.";
variation.specific.content = "C'est l'accueil, c'est tout.";
console.log(variation.common.titleWebsite); // "C'est l'accueil, c'est tout."
console.log(variation.specific.titlePage); // "Bienvenue Haeresis"
console.log(variation.specific.content); // "C'est l'accueil, c'est tout."
// On ré-injecte les modifications.
next(variation);
};
ce qui produit la sortie suivante :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>C'est l'accueil, c'est tout.</title>
</head>
<body>
<div class="title">C'est l'accueil, c'est tout.</div>
<div>
<h1>Bienvenue Haeresis</h1>
C'est l'accueil, c'est tout.
</div>
</body>
</html>
Si vous décidez de désabonner la variation spécifique avec le webconfig suivant :
{
"commonController": "common.js",
"commonVariation": "common.json",
"routes": {
"/": {
"template": "index.htm",
"variation": "index.json"
}
}
}
alors la sortie sera :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>Titre du site</title>
</head>
<body>
<div class="title">Titre du site</div>
<div>
<h1>Bienvenue Haeresis</h1>
Ceci est un test
</div>
</body>
</html>
Pour intercepter le DOM avant qu'il ne soit renvoyé, vous pouvez soit utiliser le contrôleur commun pour tout le site et/ou également le contrôleur par page.
Voici un exemple utilisant les deux interceptions, d'abord la commune au deux pages, puis celle de chaque page :
{
"commonController": "common.js",
"commonVariation": "common.json",
"routes": {
"/": {
"template": "index.htm",
"variation": "index.json",
"controller": "index.js"
}
}
}
avec cet ensemble de fichier :
├─ variations/
│ └─ index.json
├─ controllers/
│ ├─ common.js
│ └─ index.js
├─ templates/
│ └─ index.htm
└─ webconfig.json
En demandant la page http://localhost/
les fichiers suivants (entre autre) seront utilisés :
variations/common.json
{
"titleWebsite": "Titre du site"
}
variations/index.json
{
"titlePage": "Bienvenue",
"content": "<p>C'est la page d'accueil.</p>"
}
templates/index.htm
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title><%- common.titleWebsite %></title>
</head>
<body>
<div class="title"><%- common.titleWebsite %></div>
<div>
<h1><%- specific.titlePage %></h1>
<%- specific.content %>
</div>
</body>
</html>
controllers/common.js
// On intervient avant que le DOM ne soit renvoyé au Client.
// Ce code sera exécuté pour toute request HTTP, toute page confondue.
exports.changeDom = function (params, next) {
var NA = this,
dom = params.dom,
request = params.request,
response = params.response,
cheerio = NA.modules.cheerio, // Récupération de jsdom pour parcourir le DOM avec jQuery.
$ = cheerio.load(dom, { decodeEntities: false }); // On charge les données pour les manipuler comme un DOM.
// Après tous les h1 de la sortie HTML « dom »,
$("h1").each(function () {
var $this = $(this);
// ...on créé une div,
$this.after(
// ... on injecte le contenu du h1 dans la div,
$("<div>").html($this.html())
);
// ...et supprime le h1.
$this.remove();
});
// On recrée une nouvelle sortie HTML avec nos modifications.
dom = $.html();
// On réinjecte les modifications.
next(dom);
};
controllers/index.js
// On intervient avant que le DOM ne soit renvoyé au Client.
// Ce code sera exécuté uniquement lors de la demande de la page « / ».
exports.changeDom = function (params, next) {
var NA = this,
dom = params.dom,
request = params.request,
response = params.response,
cheerio = NA.modules.cheerio, // Récupération de jsdom pour parcourir le DOM avec jQuery.
$ = cheerio.load(dom, { decodeEntities: false }); // On charge les données pour les manipuler comme un DOM.
// On modifie tous les contenu des noeuds avec la classe `.title`.
$(".title").text("Modification de Contenu");
// On recrée une nouvelle sortie HTML avec nos modifications.
dom = $.html();
// On réinjecte les modifications.
next(dom);
};
ce qui produit la sortie suivante :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8">
<title>Titre du site</title>
</head>
<body>
<div class="title">Modification de Contenu</div>
<div>
<div>Bienvenue</div>
<p>C'est la page d'accueil.</p>
</div>
</body>
</html>
Pour charger d'autres modules qui ne sont pas fournis avec NodeAtlas vous pouvez utiliser le contrôleur commun pour tout le site afin de les charger une seule fois et de les rendres disponible dans tous vos controlleurs.
Voici un exemple utilisant un module externe à NodeAtlas :
{
"commonController": "common.js",
"routes": {
"/": {
"template": "index.htm",
"controller": "index.js"
}
}
}
avec cet ensemble de fichier :
├─ controllers/
│ ├─ common.js
│ └─ index.js
├─ templates/
│ └─ index.htm
└─ webconfig.json
En demandant la page http://localhost/
les fichiers suivants (entre autre) seront utilisés :
templates/index.htm
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>Test Module</title>
</head>
<body>
<div class="title">Test Module</div>
<div>
<h1>Test Module</h1>
<%- example %>
</div>
</body>
</html>
controllers/common.js
// On intervient avant que la phase de chargement des modules ne soit achevée.
// Ce code sera exécuté au lancement de NodeAtlas.
exports.loadModules = function () {
// Récupérer l'instance « NodeAtlas » du moteur.
var NA = this;
// Associations de chaque module pour y avoir accès partout.
NA.modules.marked = require('marked');
};
controllers/index.js
// On intervient avant que les variables soient injectées dans le système de template.
// Ce code sera exécuté uniquement lors de la demande de la page « / ».
exports.changeVariation = function (params, next) {
// Récupérer l'instance « NodeAtlas » du moteur.
var NA = this,
variation = params.variation,
marked = NA.modules.marked;
variation.example = marked("I am using __markdown__.");
// On ré-injecte les modifications.
next(variation);
};
ce qui produit la sortie suivante :
<!DOCTYPE html>
<html lang="fr-fr">
<head>
<meta charset="utf-8" />
<title>Test Module</title>
</head>
<body>
<div class="title">Test Module</div>
<div>
<h1>Test Module</h1>
<p>I am using <strong>markdown</strong>.</p>
</div>
</body>
</html>
Pour configurer le serveur web de NodeAtlas (ExpressJs) vous pouvez utiliser le contrôleur commun pour tout le site afin de les charger une seule fois et de les rendres disponible dans tous vos controlleurs.
Voici un exemple utilisant un middleware pour ExpressJs :
{
"commonController": "common.js",
"routes": {
"/": {
"template": "index.htm",
"controller": "index.js"
}
}
}
avec cet ensemble de fichier :
├─ controllers/
│ └─ common.js
├─ templates/
│ └─ index.htm
└─ webconfig.json
En demandant la page http://localhost/
les fichiers suivants (entre autre) seront utilisés :
templates/index.htm
<%- content %>
controllers/common.js
// On intervient au niveau du serveur avant que celui-ci ne soit démarré.
// Ce code sera exécuté au lancement de NodeAtlas.
exports.setConfigurations = function (next) {
// Récupérer l'instance « NodeAtlas » du moteur.
var NA = this;
// Middleware utilisé lors de chaque requête.
NA.httpServer.use(function (request, response, next) {
response.setHeader("X-Frame-Options", "ALLOW-FROM http://www.lesieur.name/");
next();
});
// On ré-injecte les modifications.
next();
};
controllers/index.js
// On intervient avant que les variables soient injectées dans le système de template.
// Ce code sera exécuté uniquement lors de la demande de la page « / ».
exports.changeVariation = function (params, next) {
var variation = params.variation;
// On prépare le fichier pour un affichage JSON.
variation.currentRouteParameters.headers = {
"Content-Type": "application/json; charset=utf-8"
};
variation.content = JSON.stringify(variation, null, " ");
// On ré-injecte les modifications.
next(variation);
};
ce qui produit la sortie suivante :
{
"urlBasePathSlice": "http://localhost",
"urlBasePath": "http://localhost/",
"urlPath": "http://localhost/",
"pathname": /* ... */,
"filename": /* ... */,
"params": {},
"currentRouteParameters": { /* ... */ },
"currentRoute": "/",
"webconfig": { /* ... */ }
}
Pour configurer les sessions client-serveur de NodeAtlas vous pouvez utiliser le contrôleur commun pour tout le site afin de les charger une seule fois et de les rendres disponible dans tous vos controlleurs, voici un exemple de management de Session avec Redis.
Voici l'ensemble de fichier suivant :
├─ controllers/
│ └─ common.js
├─ templates/
│ └─ index.htm
├─ variations/
│ ├─ common.json
│ └─ index.json
└─ webconfig.json
Avec le webconfig.json
:
{
"commonController": "common.js",
"commonVariation": "common.json",
"routes": {
"/": {
"template": "index.htm",
"variation": "index.json"
}
}
}
et avec le fichier « common.js » contenant par exemple :
// On intervient avant que la phase de chargement des modules ne soit achevée.
// Ce code sera exécuté au lancement de NodeAtlas.
exports.loadModules = function () {
// Récupérer l'instance « NodeAtlas » du moteur.
var NA = this;
// Associations de chaque module pour y avoir accès partout.
NA.modules.RedisStore = require('connect-redis');
};
// On intervient au niveau du serveur pendant la configuration des Sessions.
// Ce code sera exécuté au lancement de NodeAtlas.
exports.setSessions = function (next) {
var NA = this,
session = NA.modules.session,
RedisStore = NA.modules.RedisStore(session);
// On remplace la session par default.
NA.sessionStore = new RedisStore();
// On redonne la main à NodeAtlas pour la suite.
next();
};
Pour configurer les routes de NodeAtlas dynamiquement vous pouvez utiliser le contrôleur commun pour tout le site afin de les charger une seule fois et de les rendres disponible dans tous vos controlleurs.
Voici l'ensemble de fichier suivant :
├─ controllers/
│ └─ common.js
├─ templates/
│ ├─ content.htm
│ └─ index.htm
├─ variations/
│ └─ common.json
└─ webconfig.json
Avec le webconfig.json
:
{
"commonController": "common.js",
"commonVariation": "common.json",
"routes": {
"/index.html": {
"template": "index.htm"
}
}
}
et avec le fichier « common.js » contenant par exemple :
// On intervient au niveau des routes pendant qu'elles sont ajoutées.
// Ce code sera exécuté au lancement de NodeAtlas.
exports.setConfigurations = function (next) {
// On récupère l'instance de NodeAtlas en cours.
var NA = this,
// Et nous récupérons les routes en provenance du webconfig...
route = NA.webconfig.routes;
// ...pour ajouter la route "/content.html" à la liste de nos routes.
route["/content.html"] = {
"template": "content.htm"
};
// On redonne la main à NodeAtlas pour la suite.
next();
};
Afin de conserver une liaison ouverte entre la partie Frontale et la partie Serveur de vos applications, NodeAtlas est à même d'utiliser Socket.IO dont vous trouverez plus de détail sur le site officiel.
Grâce à cela, vous pourrez changer des informations en temps réel sur votre page, mais également sur toutes les autres page ouvertes à travers tous les autres navigateurs.
Avec l'ensemble de fichier suivant :
├─ assets/
│ └─ javascript/
│ ├─ common.js
│ └─ index.js
├─ components/
│ ├─ foot.htm
│ ├─ head.htm
│ └─ index.htm
├─ controllers/
│ ├─ common.js
│ └─ index.js
├─ variations/
│ ├─ common.json
│ └─ index.json
├─ templates/
│ └─ index.htm
└─ webconfig.json
Contenant le webconfig.json
suivant :
{
"commonController": "common.js",
"commonVariation": "common.json",
"routes": {
"/": {
"template": "index.htm",
"variation": "index.json",
"controller": "index.js"
}
}
}
et contenant les fichiers de template suivant :
components/head.htm
<!DOCTYPE html>
<html lang="<%= languageCode %>">
<head>
<meta charset="utf-8" />
<title><%- common.titleWebsite %></title>
</head>
<body data-hostname="<%= webconfig.urlWithoutFileName %>" data-subpath="<%= webconfig.urlRelativeSubPath.slice(1) %>" data-variation="<%= currentRouteParameters.variation.replace(/\.json/,'') %>">
Note : data-hostname
et data-subpath
va nous aider à paramètrer Socket.io côté Front.
components/foot.htm
<script type="text/javascript" src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="javascript/common.js"></script>
</body>
</html>
Note : Le fichier frontal de Socket.IO s'injecte ici en global.
components/index.htm
<div class="title"><%- common.titleWebsite %></div>
<div>
<h1><%- specific.titlePage %></h1>
<%- specific.content %>
<div><%- new Date() %></div>
</div>
<button>Update</button>
Note : Chaque clique sur button
raffraichira le contenu de components/index.htm
.
templates/index.htm
<%- include('head.htm') %>
<div class="layout">
<%- include('index.htm') %>
</div>
<script src="javascript/index.js"></script>
<%- include('foot.htm') %>
Note : On construit ici la page d'accueil /
.
ainsi que les fichiers de variations suivant :
variations/common.json
{
"titleWebsite": "Socket.IO Exemple"
}
variations/index.json
{
"titlePage": "Date",
"content": "<p>La date actuelle est :</p>"
}
Jusque là, rien d'inhabituel et tout fonctionnerait sans partie contrôleur. Mais nous allons mettre en place la communication via Socket.IO côté Serveur puis côté Client.
Côté serveur, nous utiliserons les fichiers suivant :
controllers/common.js
var privates = {};
// Chargement des modules pour ce site dans l'objet NodeAtlas.
exports.loadModules = function () {
// Récupérer l'instance « NodeAtlas » du moteur.
var NA = this;
// Associations de chaque module pour y avoir accès partout.
NA.modules.socketio = require('socket.io');
NA.modules.cookie = require('cookie');
};
// Exemple d'utilisation de Socket.IO.
privates.socketIoInitialisation = function (socketio, NA, next) {
var optionIo = (NA.webconfig.urlRelativeSubPath) ? { path: NA.webconfig.urlRelativeSubPath + '/socket.io', secure: ((NA.webconfig.httpSecure) ? true : false) } : undefined,
io = socketio(NA.server, optionIo),
cookie = NA.modules.cookie,
cookieParser = NA.modules.cookieParser;
// Synchronisation des Sessions avec Socket.IO.
io.use(function(socket, next) {
var handshakeData = socket.request;
// Fallback si les cookies ne sont pas gérés.
if (!handshakeData.headers.cookie) {
return next(new Error('Cookie de session requis.'));
}
// Transformation de la String cookie en Objet JSON.
handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
// Vérification de la signature du cookie.
handshakeData.cookie = cookieParser.signedCookies(handshakeData.cookie, NA.webconfig.session.secret);
// Garder à portée l'ID de Session.
handshakeData.sessionID = handshakeData.cookie[NA.webconfig.session.key];
// Accepter le cookie.
NA.sessionStore.load(handshakeData.sessionID, function (error, session) {
if (error || !session) {
return next(new Error('Aucune session récupérée.'));
} else {
handshakeData.session = session;
next();
}
});
});
// Suite.
next(io);
};
// Ajout d'évènements d'écoute pour un controller spécifique « index.js » (voir exemple dans le fichier d'après).
privates.socketIoEvents = function (io, NA) {
var params = {};
params.io = io;
// Evènements pour la page index (voir exemple dans le fichier d'après).
require('./index').asynchrone.call(NA, params);
};
// Configuration de tous les modules.
exports.setConfigurations = function (next) {
var NA = this,
socketio = NA.modules.socketio;
// Initialisation de Socket IO.
privates.socketIoInitialisation(socketio, NA, function (io) {
// Écoute d'action Socket IO.
privates.socketIoEvents(io, NA);
// Étapes suivante du moteur.
next();
});
};
Note : Ceci est la configuration global de Socket.IO côté serveur.
controllers/index.js
// Intégralité des actions Websocket possible pour ce template.
// Utilisé non pas par « NodeAtlas » mais par « common.js » (voir fichier précédent).
exports.asynchrone = function (params) {
var NA = this,
io = params.io;
// Dès qu'on a un lien valide entre le client et notre back...
io.sockets.on("connection", function (socket) {
// ...rester à l'écoute de la demande « create-article-button »...
socket.on("server-render", function (data) {
var sessionID = socket.request.sessionID,
session = socket.request.session,
variation = {};
// On récupère les variations spécifiques dans la bonne langue.
variation = NA.addSpecificVariation("index.json", data.lang, variation);
// On récupère les variations communes dans la bonne langue.
variation = NA.addCommonVariation(data.lang, variation);
// On récupère le fragment HTML depuis le dossier `componentsRelativePath` et on applique les variations.
data.render = NA.newRender("index.htm", variation);
// Et on répond à tous les clients avec un jeu de donnée dans data.
io.sockets.emit('server-render', data);
});
});
};
Quand au côté client, nous utiliserons les fichiers suivant :
assets/javascript/common.js
window.website = window.website || {};
(function (publics) {
"use strict";
var privates = {},
optionsSocket,
body = document.getElementsByTagName("body")[0];
// On configure Socket.IO côté Client.
optionsSocket = (body.getAttribute("data-subpath") !== "") ? { path: "/" + body.getAttribute("data-subpath") + ((body.getAttribute("data-subpath")) ? "/" : "") + "socket.io" } : undefined;
publics.socket = io.connect((body.getAttribute("data-subpath") !== "") ? body.getAttribute("data-hostname") : undefined, optionsSocket);
}(website));
// On exécute le JavaScript Spécifique à la page en cours, ici ["index"].
website[document.getElementsByTagName("body")[0].getAttribute("data-variation")].init();
Note : Ceci est la configuration global de Socket.IO côté client en ce basant sur data-subpath
et data-hostname
.
assets/javascript/index.js
window.website = window.website || {};
(function (publics) {
"use strict";
var html = document.getElementsByTagName("html")[0],
body = document.getElementsByTagName("body")[0],
layout = document.getElementsByClassName("layout")[0];
// On associe sur le bouton l'action de communiquer avec le serveur en cliquant dessus.
function setServerRender() {
var button = document.getElementsByTagName("button")[0];
button.addEventListener("click", function () {
website.socket.emit("server-render", {
lang: html.getAttribute("lang"),
variation: body.getAttribute("data-variation")
});
});
}
// On créer le code qui s'exécutera au lancement de la page.
publics.init = function () {
// On affecte l'action au bouton.
setServerRender();
// Quand le serveur répond après notre demande auprès de lui...
website.socket.on("server-render", function (data) {
// ...on met à jour le contenu...
layout.innerHTML = data.render;
// ...et ré-affectons l'action au bouton du nouveau contenu.
setServerRender();
});
};
}(website.index = {}));
Lancer votre projet et rendez-vous à l'adresse http://localhost/
dans deux onglets différent, voir même, dans deux navigateurs différent. Vous constaterez alors qu'à chaque clique sur « Update », la page se remettra à jour (comme le montre la date courante) sur tous les onglets ouvert.
Grâce à NA.addSpecificVariation
, NA.addCommonVariation
et NA.newRender
, il est possible de générer une nouvelle compilation d'un template (composant) et d'une variation commune et spécifique.
Si data.lang
dans notre exemple est de type undefined
, alors les fichiers seront cherchés à la racine. Si variation
est de type undefined
alors un objet contenant uniquement le scope demandé sera renvoyé.
Nous allons voir à présent comment utiliser des informations venant d'une base de donnée. Pour cela nous allons utiliser le module npm mysql
. Il va également nous falloir installer un serveur MySQL.
Tout d'abord, nous allons alimenter la base de données avec la base demo
:
CREATE DATABASE demo;
et la sélectionner :
USE demo
puis créer la table user
:
CREATE TABLE user
(
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
lastname VARCHAR(100),
firstname VARCHAR(100),
email VARCHAR(255),
birthdate DATE,
gender TINYINT(1),
country VARCHAR(255),
town VARCHAR(255),
zipcode VARCHAR(5),
address VARCHAR(255)
);
et la remplir avec un jeu de données :
INSERT INTO user (
lastname,
firstname,
email,
birthdate,
gender,
country,
town,
zipcode,
address
) VALUES (
"Lesieur",
"Bruno",
"bruno.lesieur@gmail.com",
"1988/07/18",
true,
"France",
"Annecy",
74000,
"66 avenue de Genève"
);
Avec le jeu de fichier suivant :
├─ assets/
│ └─ javascript/
│ └─ models/
│ └─ user.js
├─ controllers/
│ ├─ common.js
│ └─ index.js
├─ models/
│ └─ user.js
├─ templates/
│ └─ index.htm
├─ variations/
│ ├─ common.json
│ └─ index.json
└─ webconfig.json
Nous allons utiliser le webconfig.json
suivant avec une variable custom _mysqlConfig
qui contiendra toutes les informations pour se connecter à la base de données :
{
"commonController": "common.js",
"commonVariation": "common.json",
"routes": {
"/": {
"template": "index.htm",
"variation": "index.json",
"controller": "index.js"
}
},
"_mysqlConfig": {
"host": "localhost",
"user": "root",
"password": "root",
"database": "demo"
}
}
Avec les fichiers suivant pour afficher la page :
templates/index.htm
<!DOCTYPE html>
<html lang="<%- languageCode %>">
<head>
<meta charset="utf-8" />
<title><%- common.titleWebsite %></title>
</head>
<body>
<div class="title"><%- common.titleWebsite %></div>
<div>
<h1><%- specific.titlePage %></h1>
<%- specific.content %>
<ul>
<li>Id: <strong><%- id %></strong></li>
<li>Lastname: <strong><%- lastname %></strong></li>
<li>Firstname: <strong><%- firstname %></strong></li>
<li>Email: <strong><%- email %></strong></li>
<li>Birthdate: <strong><%- birthdate %></strong></li>
<li>Gender: <strong><%- gender %></strong></li>
<li>Country: <strong><%- country %></strong></li>
<li>Town: <strong><%- town %></strong></li>
<li>Zipcode: <strong><%- zipcode %></strong></li>
<li>Address: <strong><%- address %></strong></li>
</ul>
</div>
</body>
</html>
variations/common.json
{
"titleWebsite": "Exemple MySql",
"male": "Homme",
"female": "Femme"
}
variations/index.json
{
"titlePage": "Table User",
"content": "<p>Détail de l'entrée `bruno`.</p>"
}
Enfin nous allons nous connecter à la base de données avec le controlleur globale controllers/common.js
:
exports.loadModules = function () {
var NA = this;
NA.modules.mysql = require('mysql');
NA.models = {};
NA.models.User = require('../models/user.js');
};
exports.setConfigurations = function (next) {
var NA = this,
mysql = NA.modules.mysql;
NA.mySql = mysql.createPool(NA.webconfig._mysqlConfig);
next();
};
Et afficher les résultats via le controlleur spécifique controllers/index.js
:
exports.changeVariation = function (params, next) {
var NA = this,
variation = params.variation,
User = NA.models.User,
bruno = User();
NA.mySql.getConnection(function(err, connection) {
if (err) {
console.log(err);
return false;
}
bruno
.setConnection(connection)
.firstname("bruno")
.readFirst(function () {
variation.id = bruno.id();
variation.lastname = bruno.lastname();
variation.firstname = bruno.firstname();
variation.email = bruno.email();
variation.birthdate = bruno.birthdate();
variation.gender = (bruno.gender()) ? variation.common.male : variation.common.female;
variation.country = bruno.country();
variation.town = bruno.town();
variation.zipcode = bruno.zipcode();
variation.address = bruno.address();
next(variation);
});
});
};
en utilisant le model user
via le fichier de connexion à la base de données models/user.js
:
/* jslint esversion: 6 */
var user = require('../assets/javascript/models/user.js');
function User(connection) {
var privates = {},
publics = this;
privates.connection = connection;
if (!(publics instanceof User)) {
return new User();
}
publics.setConnection = function (connection) {
privates.connection = connection;
return publics;
};
user.call(publics);
publics.readFirst = function (callback) {
var select = `SELECT
id,
lastname,
firstname,
email,
birthdate,
gender,
country,
town,
zipcode,
address
FROM user`,
where = "",
limit = " LIMIT 0,1 ",
addWhere = " WHERE ";
if (publics.id()) { where += addWhere + "`id` = '" + publics.id().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.lastname()) { where += addWhere + "`lastname` = '" + publics.lastname().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.firstname()) { where += addWhere + "`firstname` = '" + publics.firstname().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.email()) { where += addWhere + "`email` = '" + publics.email().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.birthdate()) { where += addWhere + "`birthdate` = '" + publics.birthdate().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.gender()) { where += addWhere + "`gender` = '" + publics.gender().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.country()) { where += addWhere + "`country` = '" + publics.country().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.town()) { where += addWhere + "`town` = '" + publics.town().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.zipcode()) { where += addWhere + "`zipcode` = '" + publics.zipcode().replace(/'/g, "''") + "'"; addWhere = ' && '; }
if (publics.address()) { where += addWhere + "`address` = '" + publics.address().replace(/'/g, "''") + "'"; addWhere = ' && '; }
privates.connection.query(select + where + limit, function(err, rows, fields) {
if (err) console.log(err);
if (rows[0]) {
publics.id(rows[0].id);
publics.lastname(rows[0].lastname);
publics.firstname(rows[0].firstname);
publics.email(rows[0].email);
publics.birthdate(rows[0].birthdate);
publics.gender((rows[0].gender) ? true : false);
publics.country(rows[0].country);
publics.town(rows[0].town);
publics.zipcode(rows[0].zipcode);
publics.address(rows[0].address);
}
callback();
});
};
}
User.prototype = Object.create(user.prototype);
User.prototype.constructor = User;
module.exports = User;
basé sur une classe user
partagé entre le Front et le Back assets/javascript/models/user.js
:
(function (expose, factory) {
if (typeof module !== 'undefined' && module.exports) {
module.exports = factory;
} else {
expose.User = factory;
}
}(this, function User() {
var privates = {},
publics = this;
if (!(publics instanceof User)) {
return new User();
}
publics.id = function (id) {
if (typeof id === 'undefined') {
return privates.id;
} else {
privates.id = id;
return publics;
}
};
publics.lastname = function (lastname) {
if (typeof lastname === 'undefined') {
return privates.lastname;
} else {
privates.lastname = lastname;
return publics;
}
};
publics.firstname = function (firstname) {
if (typeof firstname === 'undefined') {
return privates.firstname;
} else {
privates.firstname = firstname;
return publics;
}
};
publics.email = function (email) {
if (typeof email === 'undefined') {
return privates.email;
} else {
privates.email = email;
return publics;
}
};
publics.birthdate = function (birthdate) {
if (typeof birthdate === 'undefined') {
return privates.birthdate;
} else {
privates.birthdate = birthdate;
return publics;
}
};
publics.gender = function (gender) {
if (typeof gender === 'undefined') {
return privates.gender;
} else {
privates.gender = gender;
return publics;
}
};
publics.country = function (country) {
if (typeof country === 'undefined') {
return privates.country;
} else {
privates.country = country;
return publics;
}
};
publics.town = function (town) {
if (typeof town === 'undefined') {
return privates.town;
} else {
privates.town = town;
return publics;
}
};
publics.zipcode = function (zipcode) {
if (typeof zipcode === 'undefined') {
return privates.zipcode;
} else {
privates.zipcode = zipcode;
return publics;
}
};
publics.address = function (address) {
if (typeof address === 'undefined') {
return privates.address;
} else {
privates.address = address;
return publics;
}
};
}));
Vous obtiendrez la sortie suivante :
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<title>Exemple MySql</title>
</head>
<body>
<div class="title">Exemple MySql</div>
<div>
<h1>Table User</h1>
<p>Détail de l'entrée `bruno`.</p>
<ul>
<li>Id: <strong>1</strong></li>
<li>Lastname: <strong>Lesieur</strong></li>
<li>Firstname: <strong>Bruno</strong></li>
<li>Email: <strong>bruno.lesieur@gmail.com</strong></li>
<li>Birthdate: <strong>Mon Jul 18 1988 00:00:00 GMT+0200 (Paris, Madrid (heure d’été))</strong></li>
<li>Gender: <strong>Homme</strong></li>
<li>Country: <strong>France</strong></li>
<li>Town: <strong>Annecy</strong></li>
<li>Zipcode: <strong>74000</strong></li>
<li>Address: <strong>66 avenue de Genève</strong></li>
</ul>
</div>
</body>
</html>
Nous allons voir à présent comment utiliser des informations venant d'une base de données non sql. Pour cela nous allons utiliser le module npm mongoose
. Il va également nous falloir installer un serveur MongoDB.
Tout d'abord, nous allons alimenter la base de données avec la base demo
et la sélectionner :
use demo
puis créer la collection user
:
db.createCollection("user")
et la remplir avec un document :
db.user.insert({
email: "bruno.lesieur@gmail.com",
identity: {
lastname: "Lesieur",
firstname: "Bruno",
gender: true,
birthdate : new Date("1988/07/18")
},
location: {
country: "France",
town: "Annecy",
zipcode: "74000",
address: "66 avenue de Genève"
}
})
Avec le jeu de fichier suivant :
├─ assets/
│ └─ javascript/
│ └─ models/
│ └─ user.js
├─ controllers/
│ ├─ common.js
│ └─ index.js
├─ templates/
│ └─ index.htm
├─ variations/
│ ├─ common.json
│ └─ index.json
└─ webconfig.json
Nous allons utiliser le webconfig.json
suivant avec une variable custom _mongodbConfig
qui contiendra toutes les informations pour se connecter à la base de données :
{
"commonController": "common.js",
"commonVariation": "common.json",
"routes": {
"/": {
"template": "index.htm",
"variation": "index.json",
"controller": "index.js"
}
},
"_mongodbConfig": {
"host": "localhost",
"port": "27017",
"database": "demo"
}
}
Avec les fichiers suivant pour afficher la page :
templates/index.htm
<!DOCTYPE html>
<html lang="<%- languageCode %>">
<head>
<meta charset="utf-8" />
<title><%- common.titleWebsite %></title>
</head>
<body>
<div class="title"><%- common.titleWebsite %></div>
<div>
<h1><%- specific.titlePage %></h1>
<%- specific.content %>
<ul>
<li>Id: <strong><%- id %></strong></li>
<li>Lastname: <strong><%- lastname %></strong></li>
<li>Firstname: <strong><%- firstname %></strong></li>
<li>Email: <strong><%- email %></strong></li>
<li>Birthdate: <strong><%- birthdate %></strong></li>
<li>Gender: <strong><%- gender %></strong></li>
<li>Country: <strong><%- country %></strong></li>
<li>Town: <strong><%- town %></strong></li>
<li>Zipcode: <strong><%- zipcode %></strong></li>
<li>Address: <strong><%- address %></strong></li>
</ul>
</div>
</body>
</html>
variations/common.json
{
"titleWebsite": "MongoDB Exemple",
"male": "Homme",
"female": "Femme"
}
variations/index.json
{
"titlePage": "Collection User",
"content": "<p>Détail du document `{ \"identity.firstname\": \"Bruno\" }`.</p>"
}
Enfin nous allons nous connecter à la base de données avec le controlleur globale controllers/common.js
:
exports.loadModules = function () {
var NA = this,
path = NA.modules.path;
NA.modules.mongoose = require('mongoose');
NA.models = {};
NA.models.User = require('../assets/javascript/models/user.js');
};
exports.setConfigurations = function (next) {
var NA = this,
mongoose = NA.modules.mongoose,
config = NA.webconfig._mongodbConfig;
mongoose.Promise = global.Promise;
mongoose.model("user", NA.models.User, "user");
mongoose.connect("mongodb://" + config.host + ":" + config.port + "/" + config.database, function (error) {
next();
});
};
Et afficher les résultats via le controlleur spécifique controllers/index.js
:
exports.changeVariation = function (params, next) {
var NA = this,
variation = params.variation,
mongoose = NA.modules.mongoose,
User = mongoose.model('user');
User
.findOne({ "identity.firstname": "Bruno" })
.exec(function (err, bruno) {
variation.id = bruno._id;
variation.lastname = bruno.identity.lastname;
variation.firstname = bruno.identity.firstname;
variation.birthdate = bruno.identity.birthdate;
variation.email = bruno.email;
variation.gender = (bruno.identity.gender) ? variation.common.male : variation.common.female;
variation.country = bruno.location.country;
variation.town = bruno.location.town;
variation.zipcode = bruno.location.zipcode;
variation.address = bruno.location.address;
next(variation);
});
};
en utilisant sur une classe user
partagé entre le Front et le Back assets/javascript/models/user.js
:
var mongoose;
if (typeof module !== 'undefined' && module.exports) {
mongoose = require('mongoose');
}
(function (expose, factory) {
if (mongoose) {
module.exports = factory;
} else {
expose.User = factory;
}
}(this, new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
email: { type : String, match: /^\S+@\S+$/ },
identity: {
lastname: String,
firstname: String,
gender: Boolean,
birthdate : { type : Date, default : Date.now }
},
location: {
country: String,
town: String,
zipcode: String,
address: String
}
})));
Vous obtiendrez la sortie suivante :
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<title>Exemple MongoDB</title>
</head>
<body>
<div class="title">Exemple MongoDB</div>
<div>
<h1>Collection User</h1>
<p>Détail de l'entrée `{ "identity.firstname": "Bruno" }`.</p>
<ul>
<li>Id: <strong>5804d4d530788ee2e52ea1c7</strong></li>
<li>Lastname: <strong>Lesieur</strong></li>
<li>Firstname: <strong>Bruno</strong></li>
<li>Email: <strong>bruno.lesieur@gmail.com</strong></li>
<li>Birthdate: <strong>Mon Jul 18 1988 00:00:00 GMT+0200 (Paris, Madrid (heure d’été))</strong></li>
<li>Gender: <strong>Homme</strong></li>
<li>Country: <strong>France</strong></li>
<li>Town: <strong>Annecy</strong></li>
<li>Zipcode: <strong>74000</strong></li>
<li>Address: <strong>66 avenue de Genève</strong></li>
</ul>
</div>
</body>
</html>