1 applications avec node.js angular et mean

69
1 Applications avec Node.js, Angular et MEAN Table des matières I. Prérequis............................................................................................................................. 4 1. Les « indispensables ».................................................................................................... 4 a. NPM ............................................................................................................................. 4 b. Task runner : GruntJS ................................................................................................ 5 c. MongoDB.................................................................................................................... 6 2. Les « utiles »..................................................................................................................... 7 a. Bower .......................................................................................................................... 7 b. Git ................................................................................................................................ 8 c. Une console plus agréable ...................................................................................... 9 d. Pour tester ses services Web .................................................................................. 10 II. EDI et éditeurs de texte ................................................................................................. 10 III. Mêmento Node.js ....................................................................................................... 11 1. Création du projet et installation des modules ...................................................... 11 2. Organisation de projet ............................................................................................... 12 a. Serveur ...................................................................................................................... 13 b. Configuration ........................................................................................................... 13 c. Contrôleurs ............................................................................................................... 16 d. Mongoose ................................................................................................................ 16 e. Moteurs de vues ...................................................................................................... 17 IV. Module « core » ........................................................................................................... 22 1. Partie « client » ............................................................................................................. 22 a. « config.js »................................................................................................................ 22 b. Configuration du module « core » ........................................................................ 22 c. Contrôleurs Angular ................................................................................................ 23 d. Message flash .......................................................................................................... 23 e. Vue « header » ......................................................................................................... 25 2. Partie « server » ............................................................................................................ 26 a. Routes côté server .................................................................................................. 26 b. Le layout ................................................................................................................... 26 V. Module « users » .............................................................................................................. 28

Upload: others

Post on 16-Oct-2021

7 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 1 Applications avec Node.js Angular et MEAN

1

Applications avec Node.js,

Angular et MEAN

Table des matières I. Prérequis ............................................................................................................................. 4

1. Les « indispensables ».................................................................................................... 4

a. NPM ............................................................................................................................. 4

b. Task runner : GruntJS ................................................................................................ 5

c. MongoDB.................................................................................................................... 6

2. Les « utiles »..................................................................................................................... 7

a. Bower .......................................................................................................................... 7

b. Git ................................................................................................................................ 8

c. Une console plus agréable ...................................................................................... 9

d. Pour tester ses services Web .................................................................................. 10

II. EDI et éditeurs de texte ................................................................................................. 10

III. Mêmento Node.js ....................................................................................................... 11

1. Création du projet et installation des modules ...................................................... 11

2. Organisation de projet ............................................................................................... 12

a. Serveur ...................................................................................................................... 13

b. Configuration ........................................................................................................... 13

c. Contrôleurs ............................................................................................................... 16

d. Mongoose ................................................................................................................ 16

e. Moteurs de vues ...................................................................................................... 17

IV. Module « core » ........................................................................................................... 22

1. Partie « client » ............................................................................................................. 22

a. « config.js » ................................................................................................................ 22

b. Configuration du module « core » ........................................................................ 22

c. Contrôleurs Angular ................................................................................................ 23

d. Message flash .......................................................................................................... 23

e. Vue « header » ......................................................................................................... 25

2. Partie « server » ............................................................................................................ 26

a. Routes côté server .................................................................................................. 26

b. Le layout ................................................................................................................... 26

V. Module « users » .............................................................................................................. 28

Page 2: 1 Applications avec Node.js Angular et MEAN

2

1. Partie « server » ............................................................................................................ 28

a. Configuration de Passport ..................................................................................... 28

b. Création du modèle « User » .................................................................................. 32

c. Contrôleur d’’authentification .............................................................................. 33

d. « ErrorHandler » ........................................................................................................ 36

2. Partie « client » ............................................................................................................. 37

a. Module ...................................................................................................................... 37

b. Contrôleur Angular pour l’authentification ......................................................... 38

c. Service Authentication ........................................................................................... 38

d. Formulaires ............................................................................................................... 39

VI. Module quelconque .................................................................................................. 42

1. Partie client (Angular) ................................................................................................ 43

a. Module ...................................................................................................................... 43

b. Contrôleur(s) Angular ............................................................................................. 45

c. Services ..................................................................................................................... 46

d. Vues ........................................................................................................................... 47

2. Partie Serveur ............................................................................................................... 51

a. Routes côté serveur ................................................................................................ 51

b. Modèle Mongoose ................................................................................................. 51

c. Contrôleur « Express » .............................................................................................. 53

VII. Plus … ............................................................................................................................ 55

1. Express .......................................................................................................................... 55

a. Variables partagées ............................................................................................... 55

b. Charger un fichier JSON ......................................................................................... 55

c. Changer le répertoire des vues ............................................................................ 55

d. Ressources (dossier « public ») ............................................................................... 55

e. Express Generator ................................................................................................... 56

2. “Sans Express” .............................................................................................................. 57

a. Serveur ...................................................................................................................... 57

c. URL et paramètres .................................................................................................. 57

d. Créer un index listant les contrôleurs .................................................................... 58

e. Evénements.............................................................................................................. 58

3. Bases de données ....................................................................................................... 59

a. SQL Server ................................................................................................................. 59

b. MongoDB.................................................................................................................. 60

VIII. MEAN.JS ........................................................................................................................ 62

Page 3: 1 Applications avec Node.js Angular et MEAN

3

Génération de code ......................................................................................................... 65

IX. Publication ................................................................................................................... 67

Page 4: 1 Applications avec Node.js Angular et MEAN

4

I. Prérequis

1. Les « indispensables »

a. NPM

Documentation

NPM (Node Package Manager) permet d’installer et publier des modules.

Générer « package.json » de l’application.

Depuis une invite de commande, naviguer jusqu’au dossier du projet (cd …) du

projet puis

npm init

Installation de modules

On peut soit :

Installer des modules un par un

Installation globale

npm install nommodule –g

Installation locale au projet

npm install nommodule

Installation avec ajout de la dépendance à « package.json »

npm install nommodule --save

Autres commandes utiles :

Désinstaller un module (suppression dossier et dépendance dans « package.json »)

npm rm nomdule Mettre à jour

npm update

Soit définir les dépendances dans « package.json » et les installer toutes avec

la commande. Cette méthode est utile pour alléger le projet, on pourra

supprimer tous les modules et les installer ensuite par cette simple commande.

npm install

Exemple « package.json »

{

"name": "AngNodeDemo",

"version": "1.0.0",

"description": "Node Angular application demo.",

"main": "server.js",

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1"

},

Page 5: 1 Applications avec Node.js Angular et MEAN

5

"author": "ROMAGNY13",

"license": "ISC",

"dependencies": {

"body-parser": "^1.14.2",

"client-sessions": "^0.7.0",

"consolidate": "^0.14.0",

"express": "^4.13.4",

"mongoose": "^4.4.2",

"passport": "^0.3.2",

"passport-facebook": "^2.1.0",

"passport-google-oauth": "^1.0.0",

"passport-local": "^1.0.0",

"swig": "^1.4.2"

},

"devDependencies": {

"grunt": "^0.4.5",

"grunt-nodemon": "^0.4.1"

}

}

b. Task runner : GruntJS

Documentation

Permet d’éviter d’avoir à relancer le serveur à chaque modification du code.

Installer Grunt en global

npm install -g grunt-cli

Installer Grunt-Nodemon en local au projet

npm install grunt-nodemon --save-dev

Créer un fichier de configuration pour Grunt « gruntfile.js »

module.exports = function (grunt){

grunt.initConfig({

nodemon: {

all: {

script: 'app.js',

options: {

watchedExtensions: ['js']

}

}

}

});

grunt.loadNpmTasks('grunt-nodemon');

grunt.registerTask('default', ['nodemon']);

};

Pour lancer le serveur il suffit désormais de taper la commande « grunt » à la place

de « node server.js »

grunt

Dépendances qui seront installées

(« ^ » pour version minimum)

Page 6: 1 Applications avec Node.js Angular et MEAN

6

c. MongoDB

Installation de MongoDB

Par défaut la base est installée dans « C:\Program Files\MongoDB »

La base de données est stockée par défaut dans « C:\data\db »

Les bases de données sont sur le port 27017. Exemple de chaine de connexion

« mongodb://localhost:27017/local-dev ». Les collections seront ajoutées à la

base de données « local-dev ».

Pour lancer , depuis une invite de commande, naviguer jusqu’au dossier

« bin » de MongoDB puis

mongod

Il est possible de changer la base de données de répertoire et définir ce nouveau

dossier :

mongod –dbpath ./data

Robomongo permet de gérer facilement ses bases de données MongoDB

Autres outils d’administration

Page 7: 1 Applications avec Node.js Angular et MEAN

7

2. Les « utiles »

a. Bower

Documentation et packages disponibles

Bower est un gestionnaire de dépendances. Indispensable avec « Angular ». Il

permet d’installer des packages, les dépendances seront automatiquement

installées. Par exemple jQuery sera automatiquement ajouté avec Bootstrap. Bower

nécessite que Node.js et Git soient installés.

Installation de Bower (en global)

npm install bower -g

Installation de Bower localement au projet

npm install bower --save

Avec dépendance de développement (« devDependencies » de « package.json »)

npm install bower --save-dev

Pour définir le dossier où seront installées les librairies … Créer un fichier « .bowerrc »à

la racine du projet

{

"directory": "public/lib"

}

Le fonctionnement est un peu le même qu’avec NPM. On peut créer un fichier

« bower.json » qui contiendra la liste des dépendances

bower init

… et utiliser la commande pour installer tous les packages

bower install

On peut également installer les packages un par un

bower install bootstrap --save

Mettre à jour tous les packages

bower update

Mettre à jour un package

bower update bootstrap

Les fichiers seront installés

dans le dossier « public/lib »

Page 8: 1 Applications avec Node.js Angular et MEAN

8

b. Git

Documentation

Installation

Télécharger et installer la version de Git (Windows, Mac, Linux, etc.)

On peut configurer si on veut (avec « Git Bash » )

git config --global user.name "Jerome Romagny"

git config --global user.email "romagny13@....."

git config --list

Définir le dossier du projet :

Avec git bash, naviguer jusqu’au dossier du projet (cd … ) puis

git init

Commit

Valider les changements dans le dossier (ajout/ suppression de fichiers, etc.)

git commit -a -m "initial commit"

On peut également définir les fichiers à ajouter puis « commit »

git add .

git commit -m "inital commit"

Commandes utiles :

- « git help »

- « git status » permet de lister les changements validés avec « git add » avant

un « commit »

- « git log » permet de lister tous les « commit »

Créer un fichier « .gitignore » à la racine du projet et indiquer les dossiers et fichiers à

ignorer. Exemple

/.idea

/vendor

composer.lock

Github

Cloner un projet github (copié dans le dossier défini auparavant) git clone http://github.com/...git

Remote

git remote add origin https ://github.com/…git

git push

(Demande les identifiants avec « https », on peut également utiliser « ssh »)

…Puis seulement « git push » les fois suivantes.

Message décrivant la build

Exemple ajout de variables globales

puis listing

Page 9: 1 Applications avec Node.js Angular et MEAN

9

c. Une console plus agréable

Pour avoir une console plus agréable, on peut utiliser cmder.

Autre possibilité console 2 combiné avec gitbash (installée avec Git) pour avoir une

console moins austère.

Lancer console 2 puis menu « Edit » … « Settings »… onglet « Tabs »… « Add »

Title : « git bash » par exemple

Shell : C:\Windows\SysWOW64\cmd.exe /c ""C:\Program Files\Git\bin\sh.exe" --

login -i"

Startup uri : ce que l’on veut

On peut désormais ouvrir une nouvelle tab git bash

Le chemin vers git peut varier

Page 10: 1 Applications avec Node.js Angular et MEAN

10

Commandes utiles

« cd » pour naviguer dans les dossiers

Glisser les répertoires dans l’invite de commande

« dir » lister les dossiers et fichiers du répertoire courant

« md » créer un dossier

touche « espace » et « flèches » permet de répéter la dernière commande entrée.

« cls » pour nettoyer la console

d. Pour tester ses services Web

DHC ,extension pour Chrome

Fiddler de Telerik (free)

II. EDI et éditeurs de texte WebStorm (ou PHPStorm) : chercher des plugins pour Node et Angular depuis

les settings

Visual Studio avec l’extension NodeJS tools for Visual Studio (ajout d’un onglet

de Templates « nodejs » + ouverture de la console depuis le menu contextuel

du projet et fenêtre « Node.js interactive window »)

Brackets, Visual Studio Code, Sublime Text, Atom, etc.

Page 11: 1 Applications avec Node.js Angular et MEAN

11

III. Mêmento Node.js

1. Création du projet et installation des modules a. Créer le dossier du projet

b. Ajout de « package.json »

npm init

c. Modules

Express

npm install express --save

Moteur de vues

o Pour vash

npm install vash --save

o Pour ejs

npm install ejs --save

npm install ejs-locals --save

o Pour jade

npm install jade --save

o Pour swig

npm install consolidate --save

npm install swig --save

Mongoose

npm install mongoose --save

Body parser pour récupérer les valeurs de formulaires

npm install body-parser --save

Session

npm install client-sessions --save

Passport pour l’authentification

npm install passport --save

Stratégies (local, facebook, google,etc.)

npm install passport-local --save

npm install passport-facebook --save

npm install passport-google-oauth --save

Page 12: 1 Applications avec Node.js Angular et MEAN

12

2. Organisation de projet Partie « server » Partie « client »

« node_modules » : librairies

installées par NPM pour node.js

« public » : dossier contenant la

partie « visible » du site (feuilles de

styles, images, modules Angular,

etc.)

« server » : On peut ranger dans

un dossier « server » si on veut

organiser son code

« config » : configuration des

différents modules Node.js

« controllers » Contrôleurs

« Express »

« models » : Modèles Mongoose

« views » : le layout et la page

index du site

A la racine « server.js », le serveur

Node.js

Dans le dossier « public » :

« css » : feuille de styles du site

« lib » : dossier où sont

installées les librairies par

Bower

« modules » dossier contenant

tous les modules Angular

Page 13: 1 Applications avec Node.js Angular et MEAN

13

a. Serveur

Nommé « server.js » plutôt que « app.js » pour éviter la confusion avec Angular.

var express = require('express');

var env = process.env.NODE_ENV = process.env.NODE_ENV || 'development';

var app = express();

var config = require('./server/config/config')[env];

require('./server/config/express')(app, config);

require('./server/config/mongoose')(config);

require('./server/config/passport')(app,config);

require('./server/config/routes')(app);

app.listen(config.port);

console.log('Listening on port ' + config.port + '...');

b. Configuration

config.js var path = require('path');

var rootPath = path.normalize(__dirname + '/../');

module.exports = {

development: {

db: 'mongodb://localhost:27017/local-dev',

rootPath: rootPath,

port: process.env.PORT || 3000,

facebook: {

clientID: 'your client id',

clientSecret: 'your client secret',

callbackURL: '/api/auth/facebook/callback'

},

google: {

clientID: 'your client id',

clientSecret: 'your client secret',

callbackURL: 'http://localhost:3000/api/auth/google/callback'

}

},

production: {

rootPath: rootPath,

db: 'mongodb://....', // à configurer

port: process.env.PORT || 80,

facebook: {

clientID: 'your client id',

clientSecret: 'your client secret',

callbackURL: '/api/auth/facebook/callback'

},

google: {

clientID: 'your client id',

clientSecret: 'your client secret',

callbackURL: 'http://localhost:3000/api/auth/google/callback'

}

}

};

Page 14: 1 Applications avec Node.js Angular et MEAN

14

Express var express = require('express'),

bodyParser = require('body-parser'),

session = require('client-sessions'),

consolidate = require('consolidate');

module.exports = function(app, config) {

app.set('views', config.rootPath + '/views');

app.engine('html', consolidate['swig']);

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

app.use(bodyParser.json());

app.use(bodyParser.urlencoded({ extended: true }));

app.use(session({

cookieName: 'session',

secret: 'secret'

}));

app.use(express.static(config.rootPath + '/public'));

};

Mongoose var mongoose = require('mongoose'),

userModel = require('../models/user'),

articleModel = require('../models/article');

module.exports = function(config) {

mongoose.connect(config.db);

var db = mongoose.connection;

db.on('error', console.error.bind(console, 'connection error...'));

db.once('open', function callback() {

console.log('Db opened');

});

//articleModel.createDefaultArticles();

};

Passport

Voir le module “users”

Page 15: 1 Applications avec Node.js Angular et MEAN

15

Routes var articleController = require('../controllers/articleController'),

auth = require('../controllers/authController');

module.exports = function (app) {

app.get('/api/articles/', articleController.list);

app.get('/api/articles/:id', articleController.read);

app.post('/api/articles/', articleController.create);

app.put('/api/articles/', articleController.update);

app.delete('/api/articles/:id', articleController.delete);

app.get('/', function (req,res) {

if (req.session && req.session.passport && req.session.passport.user) {

res.render('index', { 'user' : req.session.passport.user });

}

else {

res.render('index');

}

});

// local

app.route('/api/auth/register').post(auth.register);

app.route('/api/auth/login').post(auth.login);

// facebook

app.route('/api/auth/facebook').get(auth.oauthCall('facebook',{ scope: ['email'] }));

app.route('/api/auth/facebook/callback').get(auth.oauthCallback('facebook'));

// google

app.route('/api/auth/google').get(auth.oauthCall('google', {

scope: [

'https://www.googleapis.com/auth/userinfo.profile',

'https://www.googleapis.com/auth/userinfo.email'

]

}));

app.route('/api/auth/google/callback').get(auth.oauthCallback('google'));

// log out

app.get('/api/auth/logout', auth.logout);

};

Si on ne veut pas passer « app » on peut utiliser « router »

var articleController = require('../controllers/articleController'),

express = require('express'),

router = express.router();

module.exports = function () {

router.get('/api/articles/', articleController.list);

// etc.

};

Utilise les contrôleurs

On passe express en argument

Route et appel de fonction du

contrôleur

Page 16: 1 Applications avec Node.js Angular et MEAN

16

c. Contrôleurs

Exemple de contrôleur

var mongoose = require('mongoose'),

Article = mongoose.model('Article'),

errorHandler = require('./errorHandler');

exports.list = function (req, res) {

Article.find().sort('-created').populate('user').exec(function (err, articles) {

if (err) {

return res.status(400).send({

message: errorHandler.getErrorMessage(err)

});

} else {

res.json(articles);

}

});

};

d. Mongoose

Documentation

Création des modèles dans un dossier « models »

var mongoose = require('mongoose');

var schema = new mongoose.Schema({

title: {type:String, required:'{PATH} is required!'},

content: {type:String, required:'{PATH} is required!'}

});

var Article = mongoose.model('Article', schema);

Types de données :

String

Number

Date

Boolean

Buffer

ObjectId

Mixed

Array

Ne pas oublier « require » dans ce cas dans « mongoose.js »

var mongoose = require('mongoose'),

articleModel = require('../models/article');

Dans les contrôleurs on utilisera

var mongoose = require('mongoose'),

Article = mongoose.model('Article');

Page 17: 1 Applications avec Node.js Angular et MEAN

17

Une variante

var mongoose = require('mongoose'),

Schema = mongoose.Schema;

var ArticleSchema = new Schema({

title: {type:String, required:'{PATH} is required!'},

content: {type:String, required:'{PATH} is required!'}

});

module.exports = mongoose.model('Article', ArticleSchema, 'articles');

Pas besoin de require dans « mongoose.js » mais require dans les contrôleurs

var mongoose = require('mongoose'),

Article = require('../models/article');

e. Moteurs de vues

Choix du moteur de template (Express)

app.set("view engine", "ejs");

Définir le chemin du dossier content les vues

app.set('views', __dirname + '/views');

Passage de paramètres à la vue

res.render('articles-list',{title:'Mon titre', articles:articles});

Swig

Documentation

Fichiers avec extension « *.html »

Pour pouvoir utiliser Swig, installer avec NPM

npm install consolidate --save

npm install swig --save

Puis dans la configuration d’Express

var express = require('express'),

// etc.

consolidate = require('consolidate');

module.exports = function(app, config) {

app.set('views', config.rootPath + '/views');

app.engine('html', consolidate['swig']);

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

// etc.

app.use(express.static(__dirname + "/../../public"));

};

Les variables entre accolades {{ }}

Page 18: 1 Applications avec Node.js Angular et MEAN

18

{{ title }}

Exemple de boucle

{% for article in articles %}

<h1>{{ article.title }}</h1>

<p>{{ article.content }}</p>

{% endfor %}

Note : avec Angular on utilisera plutôt les directives

Page 19: 1 Applications avec Node.js Angular et MEAN

19

VASH

Documentation

Fichiers avec extension « *.vash »

Master Page « layout.vash »

<!doctype html>

<html>

<head>

<title>@model.title</title>

</head>

<body>

@html.block("body")

@html.block("scripts")

</body>

</html>

Vue

@html.extend('layout', function(model){

@html.block("body", function (model) {

<p>Le contenu</p>

});

});

Variable avec @model

@model.title

Exemple de boucle

@model.articles.forEach(function(article){ <h2>@article.title</h2> <p>@article.content</p> });

Page 20: 1 Applications avec Node.js Angular et MEAN

20

EJS

Documentation

Fichiers avec extension « *.ejs »

Installer ejs-locals pour le support des master pages

npm install ejs –save

npm install ejs-locals --save

configuration

var engine = require('ejs-locals');

app.engine('ejs', engine);

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

Master page “layout.ejs”

<!doctype html>

<html>

<head>

<title><%- title %></title>

</head>

<body>

<%- body %>

</body>

</html>

Vue

<% layout('layout.ejs') -%>

<p>Le contenu</p>

Variables entre <%= %>

<h1><%= title %></h1>

Exemple de boucle

<% articles.forEach(function(article){ %>

<h2><%= article.title %></h2>

<p><%= article.content %></p>

<% }) %>

Vues partielles (dans un dossier “partials” de “views”)

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta charset="utf-8" />

<title></title>

<link href="/css/default.css" rel="stylesheet" />

</head>

<body>

<%= include partials/footer.ejs %>

</body>

</html>

Exemple de vue partielle

<footer>

<div>Copyright &copy; J. ROMAGNY</div>

Page 21: 1 Applications avec Node.js Angular et MEAN

21

</footer>

Jade

Documentation

Fichiers avec extension « *.jade »

Master page “layout.jade”

doctype

html

head

title= title

body

block content

Vue

extends layout

block content

p Le contenu

Variables

h1= title

Exemple de boucle

each article in articles

h2=article.title

p=article.content

Page 22: 1 Applications avec Node.js Angular et MEAN

22

IV. Module « core »

1. Partie « client »

a. « config.js »

Enregistrement de tous les modules de l’application + dépendances

'use strict';

var app = angular.module('app',['core','articles','users','ngRoute']);

b. Configuration du module « core »

Contient essentiellement les routes vers la page d’accueil et vers la page « 404 »

'use strict';

var core = angular.module('core',['ngRoute']);

core.config(function ($routeProvider) {

$routeProvider

.when('/',{

controller:'HomeController',

templateUrl:'modules/core/views/home.html'

})

.otherwise({ redirectTo: "/" })

}

);

Enregistrement de tous les modules de

l’application

Contrôleurs Angular pour la vue

« header » insérée dans le layout et

pour la vue d’accueil du site.

Vues

Configuration du module

« core » et routes

Directives et services

partagés

Page 23: 1 Applications avec Node.js Angular et MEAN

23

c. Contrôleurs Angular

« HeaderController » On injecte « Authentication » afin de pouvoir mettre en forme le

menu du header selon que l’utilisateur est connecté ou non.

angular.module('core').controller('HeaderController',function($scope,Auth

entication, flash, $rootScope){

$scope.authentication = Authentication;

function flashMessage() {

if(flash.hasMessage()){

$scope.message = flash.getMessage();

}

else{

$scope.message = undefined;

}

}

$rootScope.$on('$routeChangeStart',

function () { flashMessage(); }

);

});

« HomeController » est très classique voir quasiment vide, selon ce que l’on affiche

en page d’accueil. Il peut avoir un « title ».

d. Message flash

On peut se créer un service très simple de message. Le message n’est affiché

qu’une fois (supprimé après un changement de route)

angular.module('core').service("flash", function($rootScope) {

var message = undefined;

$rootScope.$on("$routeChangeSuccess", function() {

message = undefined;

});

this.setMessage = function(text,type){

message ={};

message.text = text;

message.type = typeof type !== 'undefined' ? 'success' : type;

message.cssclass = typeof message.type !== 'success' ? 'alert-

success' : 'alert-error';

};

this.getMessage = function() {

return message;

};

this.hasMessage = function() {

return message;

};

});

Il suffit ensuite de l’injecter dans les contrôleurs devant l’utiliser. On indique un

message avec « setMessage »

flash.setMessage('Article modifié.');

Page 24: 1 Applications avec Node.js Angular et MEAN

24

On ajoute dans une vue partielle que l’on inclut dans le layout par exemple.

<div ng-show="message" class="alert {{ message.cssclass }}">

<span dynamic="message.text"></span>

</div>

Et dans le contrôleur de cette vue partielle, on injecte le service « flash » puis on teste

à chaque changement de route si un message est présent.

function flashMessage() {

if(flash.hasMessage()){

$scope.message = flash.getMessage();

}

else{

$scope.message = undefined;

}

}

$rootScope.$on('$routeChangeStart',

function () { flashMessage(); }

);

La directive « dynamic » permet d’insérer du contenu au format HTML dans le

message.

angular.module('core').directive('dynamic', ['$compile', function

($compile) {

return {

restrict: 'A',

replace: true,

link: function link(scope, ele, attrs) {

return scope.$watch(attrs.dynamic, function (html) {

ele.html(html);

return $compile(ele.contents())(scope);

});

}

};

}]);

Exemple de contenu au format HTML

flash.setMessage('<strong>Article ajouté!</strong> vous pouvez le

<i>modifier</i> si vous le désirez.');

C’est un exemple simple. On peut aussi utiliser angular-flash

Exemple de message flash

Page 25: 1 Applications avec Node.js Angular et MEAN

25

e. Vue « header »

(Mise en forme avec Bootstrap)

<div class="navbar-inverse">

<div class="container">

<div class="navbar-header">

<button type="button" class="navbar-toggle" data-toggle="collapse" data-

target=".navbar-collapse">

<span class="icon-bar"></span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

</button>

<a href="#/" class="navbar-brand">Angular Node Demo</a>

</div>

<div class="navbar-collapse collapse">

<ul class="nav navbar-nav">

<li ng-class="{ active: isActive('/')}"><a href="#/">Accueil</a></li>

<li ng-class="{ active: isActive('/articles/')}"><a

href="#/articles/">Blog</a></li>

</ul>

<ul class="nav navbar-nav navbar-right" ng-hide="authentication.user">

<li><a href="#/register">S'inscrire</a></li>

<li class="divider-vertical"></li>

<li><a href="#/login">Se connecter</a></li>

</ul>

<ul class="nav navbar-nav navbar-right" ng-show="authentication.user">

<li><a href="#">Bonjour {{ authentication.user.username }}</a></li>

<li class="divider-vertical"></li>

<li><a href="/api/auth/logout">Se déconnecter</a></li>

</ul>

</div>

</div>

</div>

<div class="separator-20"></div>

<div class="container">

<div ng-show="message" class="alert {{ message.cssclass }}">

<span dynamic="message.text"></span>

</div>

</div>

Menu hamburger

visible sur mobile

Menu à gauche avec

« Accueil » ,etc.

Menu à droite pour

utilisateur anonyme et

connecté

Pour l’exemple

message flash

Menu

« déconnecté »

Menu

« connecté »

Page 26: 1 Applications avec Node.js Angular et MEAN

26

2. Partie « server »

a. Routes côté server

Dans « routes.js » du dossier « config » du « server ». Essentiellement la route vers la

page d’accueil

var articleController = require('../controllers/articleController'),

auth = require('../controllers/authController');

module.exports = function (app) {

// etc.

app.get('/', function (req,res) {

if (req.session && req.session.passport && req.session.passport.user) {

res.render('index', { 'user' : req.session.passport.user });

}

else {

res.render('index');

}

});

};

b. Le layout <!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">

<title>Titre</title>

<link type="text/css" rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.css" />

<link type="text/css" rel="stylesheet" href="/css/style.css" />

</head>

<body ng-app="app">

<header ng-include="'/modules/core/views/header.html'" ng-

controller="HeaderController"></header>

<section class="content">

<section class="container">

{% block content %}{% endblock %}

</section>

</section>

<script type="text/javascript">

window.user = {{ user | json | safe }}

</script>

<!-- vendors -->

<script type="text/javascript" src="/lib/angular/angular.min.js"></script>

<script type="text/javascript" src="/lib/angular/angular-locale_fr-fr.js"></script>

<script type="text/javascript" src="/lib/angular-route/angular-route.min.js"></script>

<script type="text/javascript" src="/lib/angular-resource/angular-

resource.min.js"></script>

<!-- modules -->

<script type="text/javascript" src="/modules/core/core.module.js"></script>

<script type="text/javascript" src="/modules/users/users.module.js"></script>

<script type="text/javascript" src="/modules/articles/articles.module.js"></script>

Page 27: 1 Applications avec Node.js Angular et MEAN

27

<script type="text/javascript" src="/modules/core/config/config.js"></script>

<!-- controllers -->

<script type="text/javascript"

src="/modules/articles/controllers/articleController.js"></script>

<script type="text/javascript" src="/modules/users/controllers/authController.js"></script>

<script type="text/javascript"

src="/modules/core/controllers/headerController.js"></script>

<script type="text/javascript" src="/modules/core/controllers/homeController.js"></script>

<!-- Directives -->

<script type="text/javascript" src="/modules/core/directives/dynamic.js"></script>

<script type="text/javascript"

src="/modules/articles/directives/contenteditable.js"></script>

<!-- Services -->

<script type="text/javascript"

src="/modules/articles/services/articles.service.js"></script>

<script type="text/javascript" src="/modules/core/services/flash.js"></script>

<script type="text/javascript" src="/modules/users/services/authentication.js"></script>

</body>

</html>

« Index.html »

C’est la page principale du site qui va afficher les vues avec la directive ng-view (ou

ui-view avec ui-router). Elle utilise le layout.

{% extends 'layout.html' %}

{% block content %}

<div ng-view></div>

{% endblock %}

Page 28: 1 Applications avec Node.js Angular et MEAN

28

V. Module « users »

1. Partie « server » Passport permet de simplifier l’authentification. Il faut définir les stratégies utilisées :

local, facebook, google, … Stratégies disponibles

a. Configuration de Passport var passport = require('passport'),

User = require('mongoose').model('User');

module.exports = function (app, config) {

passport.serializeUser(function (user, done) {

done(null, user);

});

passport.deserializeUser(function (id, done) {

User.findOne({

_id: id

}, '-salt -password', function (err, user) {

done(err, user);

});

});

require('./strategies/facebook')(config);

require('./strategies/google')(config);

require('./strategies/local')();

app.use(passport.initialize());

app.use(passport.session());

};

Stratégie « local »

npm install passport-local --save

var passport = require('passport'),

LocalStrategy = require('passport-local').Strategy,

User = require('mongoose').model('User');

module.exports = function () {

passport.use(new LocalStrategy({

usernameField: 'username',

passwordField: 'password'

},

function (username, password, done) {

User.findOne({

username: username

}, function (err, user) {

if (err) {

return done(err);

}

if (!user || !user.authenticate(password)) {

return done(null, false, {

message: 'Utilisateur ou mot de passe incorrect.'

});

}

return done(null, user);

});

}

));

};

Page 29: 1 Applications avec Node.js Angular et MEAN

29

Stratégie « Facebook »

npm install passport-facebook --save

var passport = require('passport'),

FacebookStrategy = require('passport-facebook').Strategy,

auth = require('../../controllers/authController');

module.exports = function (config) {

passport.use(new FacebookStrategy({

clientID: config.facebook.clientID,

clientSecret: config.facebook.clientSecret,

callbackURL: config.facebook.callbackURL,

profileFields: ['id', 'name', 'displayName', 'emails',

'photos'],

passReqToCallback: true

},

function (req, accessToken, refreshToken, profile, done) {

var providerData = profile._json;

providerData.accessToken = accessToken;

providerData.refreshToken = refreshToken;

var providerUserProfile = {

firstName: profile.name.givenName,

lastName: profile.name.familyName,

displayName: profile.displayName,

email: profile.emails[0].value,

profileImageURL: (profile.id) ? '//graph.facebook.com/' +

profile.id + '/picture?type=large' : undefined,

provider: 'facebook',

providerIdentifierField: 'id',

providerData: providerData

};

auth.saveOAuthUserProfile(req, providerUserProfile, done);

}

));

};

Création d’une application Facebook

Page 30: 1 Applications avec Node.js Angular et MEAN

30

Récupérer l’App ID et l’APP Secret et les indiquer dans « config.js »

Stratégie « Google »

On utilise ici « passport-google-oauth », il en existe d’autres pour Google.

npm install passport-google-oauth --save

var passport = require('passport'),

GoogleStrategy = require('passport-google-oauth').OAuth2Strategy,

auth = require('../../controllers/authController');

module.exports = function (config) {

passport.use(new GoogleStrategy({

clientID: config.google.clientID,

clientSecret: config.google.clientSecret,

callbackURL: config.google.callbackURL,

passReqToCallback: true

},

function (req, accessToken, refreshToken, profile, done) {

var providerData = profile._json;

providerData.accessToken = accessToken;

providerData.refreshToken = refreshToken;

var providerUserProfile = {

firstName: profile.name.givenName,

lastName: profile.name.familyName,

displayName: profile.displayName,

email: profile.emails[0].value,

username: profile.username,

profileImageURL: (providerData.picture) ?

providerData.picture : undefined,

provider: 'google',

providerIdentifierField: 'id',

providerData: providerData

};

auth.saveOAuthUserProfile(req, providerUserProfile, done);

}

));

};

Page 31: 1 Applications avec Node.js Angular et MEAN

31

Création d’une application Google avec la console Google Developers

Avec creation d’une clé et URI de redirection (exemple

« http://localhost:3000/api/auth/google/callback » en local)

Récupérer le Client ID et le code secret et les indiquer dans « config.js »

Page 32: 1 Applications avec Node.js Angular et MEAN

32

b. Création du modèle « User » var mongoose = require('mongoose'),

Schema = mongoose.Schema,

crypto = require("crypto");

var UserSchema = new Schema({

firstName: {type: String, trim: true, default: ''},

lastName: {type: String, trim: true, default: ''},

displayName: {type: String, trim: true},

email: { type: String, trim: true, unique: true, default: ''},

username: {type: String, unique: 'Un utilisateur avec cet identifiant

est déja enregistré.', required: 'Vous devez indiquer un nom

d\'utilisateur', trim: true},

password: {type: String, default: ''},

salt: {type: String},

profileImageURL: {type: String},

roles: {

type: [{

type: String,

enum: ['user', 'admin']

}],

default: ['user']

},

provider: {type: String, required: 'Un provider est requis'},

providerData: {},

created: {type: Date, default: Date.now}

});

UserSchema.pre('save', function (next) {

this.salt = createSalt();

this.password = hashPassword(this.password, this.salt);

next();

});

var createSalt = function () {

return crypto.randomBytes(128).toString('base64');

};

var hashPassword = function (password, salt) {

var hmac = crypto.createHmac("sha1", salt);

return hmac.update(password).digest("hex");

};

UserSchema.methods.authenticate = function (password) {

return this.password === hashPassword(password,this.salt);

};

var User = mongoose.model('User', UserSchema);

Page 33: 1 Applications avec Node.js Angular et MEAN

33

c. Contrôleur d’’authentification

Partie pour la stratégie « local »

var passport = require('passport'),

mongoose = require('mongoose'),

User = mongoose.model('User'),

errorHandler = require('./errorHandler');

exports.register = function (req, res) {

var user = new User(req.body);

var message = null;

user.provider = 'local';

user.displayName = user.firstName + ' ' + user.lastName;

user.save(function (err) {

if (err) {

return res.status(400).send({

message: errorHandler.getErrorMessage(err)

});

} else {

// sensitive data

user.password = undefined;

user.salt = undefined;

req.login(user, function (err) {

if (err) {

res.status(400).send(err);

} else {

res.json(user);

}

});

}

});

};

exports.login = function (req, res) {

passport.authenticate('local', function (err, user, info) {

if (err || !user) {

res.status(400).send(info);

} else {

user.password = undefined;

user.salt = undefined;

req.login(user, function (err) {

if (err) {

res.status(400).send(err);

} else {

res.json(user);

}

});

}

})(req, res);

};

exports.logout = function(req,res){

req.logout();

req.session.destroy();

res.redirect('/'); //

};

Page 34: 1 Applications avec Node.js Angular et MEAN

34

Stratégies Facebook, Google, etc.

var noReturnUrls = [

'/login',

'/register'

];

exports.oauthCall = function(strategy,scope){

return passport.authenticate(strategy, scope)

};

exports.oauthCall = function (strategy, scope) {

return function (req, res, next) {

if (noReturnUrls.indexOf(req.query.redirect_to) === -1) {

req.session.redirect_to = req.query.redirect_to;

}

passport.authenticate(strategy, scope)(req, res, next);

};

};

exports.oauthCallback = function (strategy) {

return function (req, res, next) {

var sessionRedirectURL = req.session.redirect_to;

delete req.session.redirect_to;

passport.authenticate(strategy, function (err, user, redirectURL) {

if (err) {

return res.redirect('/login');

}

if (!user) {

return res.redirect('/login');

}

req.login(user, function (err) {

if (err) {

return res.redirect('/login');

}

else{

return res.redirect(redirectURL || '/#' + sessionRedirectURL || '/');

}

});

})(req, res, next);

};

};

Page 35: 1 Applications avec Node.js Angular et MEAN

35

La fonction servant à connecter/enregistrer l’utilisateur avec stratégie Facebook,

Google, etc.

exports.saveOAuthUserProfile = function (req, providerUserProfile, done) {

var identifierField = 'providerData.' + providerUserProfile.providerIdentifierField;

var searchQuery = {};

searchQuery.provider = providerUserProfile.provider; // facebook, google, etc.

searchQuery[identifierField] =

providerUserProfile.providerData[providerUserProfile.providerIdentifierField]; // id

User.findOne(searchQuery, function (err, user) {

if (err) {

return done(err);

} else {

if (!user) {

var availableUsername = providerUserProfile.username ||

((providerUserProfile.email) ? providerUserProfile.email.split('@')[0] : '');

user = new User({

firstName: providerUserProfile.firstName,

lastName: providerUserProfile.lastName,

username: availableUsername,

displayName: providerUserProfile.displayName,

email: providerUserProfile.email,

profileImageURL: providerUserProfile.profileImageURL,

provider: providerUserProfile.provider,

providerData: providerUserProfile.providerData

});

user.save(function (err) {

return done(err, user);

});

}

else {

return done(err, user);

}

}

});

};

Page 36: 1 Applications avec Node.js Angular et MEAN

36

d. « ErrorHandler »

Pour afficher des messages d’erreurs lisibles pour les visiteurs.

exports.getErrorMessage = function (err) {

var message = '';

if (err.code) {

switch (err.code) {

case 11000:

case 11001:

message = getUniqueErrorMessage(err);

break;

default:

message = 'Une erreur est survenue';

}

} else {

for (var errName in err.errors) {

if (err.errors[errName].message) {

message = err.errors[errName].message;

}

}

}

return message;

};

var getUniqueErrorMessage = function (err) {

var output;

try {

var fieldName = err.errmsg.substring(err.errmsg.lastIndexOf('.$') + 2,

err.errmsg.lastIndexOf('_1'));

output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' existe

déja';

} catch (ex) {

output = 'Ce champ existe déja';

}

return output;

};

Page 37: 1 Applications avec Node.js Angular et MEAN

37

2. Partie « client »

a. Module

Routes vers les vues « login » et « register » et check à changement de route pour voir

si l’utilisateur est autorisé à aller à la route suivante

'use strict';

var users = angular.module('users',['ngRoute']);

users.config(function ($routeProvider) {

$routeProvider

.when('/login',{

controller:'AuthController',

templateUrl:'modules/users/views/login.html'

})

.when('/register',{

controller:'AuthController',

templateUrl:'modules/users/views/register.html'

})

}

);

users.run(function ($rootScope, Authentication,$location) {

$rootScope.$on('$routeChangeStart', function (event,next, current) {

if (next && next.$$route && next.$$route.secure) {

if (!Authentication.user) {

$rootScope.$evalAsync(function () {

$rootScope.previousUrl = next.$$route.originalPath;

$location.path('/login');

});

}

}

});

});

Contrôleur gérant

l’authentification

Service permettant de

stocker l’utilisateur

Vues pour se connecter et

s’inscrire

Configuration et routes du

module

Page 38: 1 Applications avec Node.js Angular et MEAN

38

b. Contrôleur Angular pour l’authentification 'use strict';

angular.module('users').controller('AuthController',

function ($rootScope, $scope, $http, $location, $window, Authentication) {

$scope.authentication = Authentication;

// erreur passée dans l’url

$scope.error = $location.search().err;

var redirect_to = $rootScope.previousUrl || '/';

if ($scope.authentication.user) {

$location.path('/');

}

$scope.register = function () {

$http.post('/api/auth/register',$scope.credentials).then(function(response){

$scope.authentication.user = response.data;

$location.path('/');

},function(response){

$scope.error = response.data.message;

});

};

$scope.login = function () {

$http.post('/api/auth/login',$scope.credentials).then(function(response){

$scope.authentication.user = response.data;

$location.path(redirect_to);

},function(response){

$scope.error = response.data.message;

});

};

$scope.callOauthProvider = function (url) {

$window.location.href = url + (redirect_to ? '?redirect_to=' +

encodeURIComponent(redirect_to) : '');

};

});

c. Service Authentication

Permettant de stocker l’utilisateur connecté

angular.module('users').service('Authentication', ['$window',

function ($window) {

var auth = {

user: $window.user

};

return auth;

}

]);

Page 39: 1 Applications avec Node.js Angular et MEAN

39

Dans le layout

<script type="text/javascript">

window.user = {{ user | json | safe }}

</script>

d. Formulaires

Connexion

<div class="col-md-offset-4 col-md-4">

<h3>Se connecter avec</h3>

<form ng-submit="login()" class="form-horizontal" autocomplete="off">

<fieldset>

<div class="form-group">

<a class="btn btn-block btn-social btn-facebook" href ng-

click="callOauthProvider('/api/auth/facebook')">

<span class="fa fa-facebook"></span>

Facebook

</a>

<a class="btn btn-block btn-social btn-google" href ng-

click="callOauthProvider('/api/auth/google')">

<span class="fa fa-google"></span>

Google+

</a>

</div>

<hr class="hrOr">

<div class="form-group">

<label class="sr-only" for="email">Email</label>

<input type="email" id="email" name="email" placeholder="Email"

class="form-control" ng-model="credentials.email"/>

</div>

<div class="form-group">

<label class="sr-only" for="password">Password</label>

<input type="password" id="password" name="password" class="form-control"

placeholder="Mot de passe" ng-model="credentials.password"/>

</div>

<div class="text-center form-group">

<button type="submit" class="btn btn-primary">Se connecter</button>

&nbsp; ou &nbsp;

<a href="#/register">S'inscrire</a>

</div>

</fieldset>

</form>

<alert type="danger" ng-show="error" class="text-center text-danger">

<span ng-bind="error"></span>

</alert>

</div>

Utilisation de Bootstrap social pour les boutons Facebook et Google+

bower install bootstrap-social --save

Référencer bootstrap social et font awesome dans le layout

<link type="text/css" rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.css" />

<link type="text/css" rel="stylesheet" href="/lib/bootstrap-social/bootstrap-social.css"

/>

<link type="text/css" rel="stylesheet" href="/lib/font-awesome/css/font-awesome.css" />

Page 40: 1 Applications avec Node.js Angular et MEAN

40

Inscription

<div class="col-md-offset-4 col-md-4">

<h3>Inscription</h3>

<alert type="danger" ng-show="error" class="text-center text-danger">

<span ng-bind="error"></span>

</alert>

<form name="userForm" ng-submit="register()" class="form-horizontal" novalidate

autocomplete="off">

<fieldset>

<div class="form-group">

<label for="firstName">Prénom</label>

<input type="text" required id="firstName" name="firstName" class="form-

control"

ng-model="credentials.firstName" placeholder="Prénom">

</div>

<div class="form-group">

<label for="lastName">Nom</label>

<input type="text" id="lastName" name="lastName" class="form-control"

ng-model="credentials.lastName" placeholder="Nom">

</div>

<div class="form-group">

<label for="username">Username</label>

<input type="text" id="username" name="username" class="form-control"

ng-model="credentials.username" placeholder="Username">

</div>

<div class="form-group">

<label for="email">Email</label>

<input type="email" id="email" name="email" placeholder="Email"

class="form-control" ng-model="credentials.email"/>

</div>

<div class="form-group">

<label for="password">Mot de passe</label>

<input type="password" id="password" name="password" class="form-control"

placeholder="Mot de passe" ng-model="credentials.password"/>

</div>

<div class="form-group">

<label for="confirmpassword">Confirmer le mot de passe</label>

<input type="password" id="confirmpassword" name="confirmpassword"

class="form-control" ng-model="credentials.confirmpassword" placeholder="Confirmer le mot

de passe" required>

</div>

<div class="text-center form-group">

<button type="submit" class="btn btn-primary">S'inscrire</button>

&nbsp; ou&nbsp;

<a href="#/login">Se connecter</a>

</div>

</fieldset>

</form>

</div>

Page 41: 1 Applications avec Node.js Angular et MEAN

41

Ce qui donne …

Connexion

Inscription

Page 42: 1 Applications avec Node.js Angular et MEAN

42

VI. Module quelconque Exemple avec « articles » que l’on pourrait retrouver avec un CMS.

Détails Liste

Edition

Boutons édition,

suppression visibles

que si l’utilisateur

est l’auteur de

l’article

Affichage du contenu

au format HTML

Content éditable

et sauvegarde au

format HTML vers

MongoDB. L’ajout

et la modification

d’articles

réclament des

utilisateurs

authentifiés

Page 43: 1 Applications avec Node.js Angular et MEAN

43

1. Partie client (Angular)

a. Module

Avec une organisation du projet par modules.

Pour chaque module on crée un fichier dans lequel on définit le nom du

module, ses dépendances, routes pour ce module uniquement (on peut aussi

créer un fichier de routes à part dans un dossier « routes »)

'use strict';

var articles = angular.module('articles',['ngRoute','ngResource']);

articles.config(function ($routeProvider) {

$routeProvider

.when('/articles/',{

controller:'ArticleController',

templateUrl:'modules/articles/views/list.html'

})

.when('/articles/view/:id',{

controller:'ArticleController',

templateUrl:'modules/articles/views/view.html'

})

.when('/articles/create',{

controller:'ArticleController',

templateUrl:'modules/articles/views/create.html',

secure:true

})

.when('/articles/edit/:id',{

controller:'ArticleController',

templateUrl:'modules/articles/views/edit.html',

secure:true

})

}

);

Routes : On peut utiliser soit $routeProvider, soit $stateProvider

On enregistre le module dans un module principal (dans un fichier « config.js »

dans un dossier « config » du module « core »)

var app = angular.module('app',['core','articles','users','ngRoute']);

Configuration du

module

Vues

Services du module

Contrôleurs Angular

Directives

Page 44: 1 Applications avec Node.js Angular et MEAN

44

Sécuriser une route

Dans l’exemple la création et modification d’articles demandent d’être authentifié.

De plus, chaque utilisateur ne pourra modifier que les articles dont il est l’auteur.

Indiquer « secure » dans la route

$routeProvider

.when('/articles/create',{

controller:'ArticleController',

templateUrl:'modules/articles/views/create.html',

secure:true

})

Dans le module « users » on vérifie à chaque changement de route si l’utilisateur est

autorisé à accéder à la route suivante. Si ce n’est pas le cas on le redirige vers la vue

de login.

'use strict';

var users = angular.module('users',['ngRoute']);

// routing du module

users.run(function ($rootScope, Authentication,$location) {

$rootScope.$on('$routeChangeStart', function (event,next, current) {

if (next && next.$$route && next.$$route.secure) {

if (!Authentication.user) {

$rootScope.$evalAsync(function () {

$rootScope.previousUrl = next.$$route.originalPath;

$location.path('/login');

});

}

}

});

});

Pour rendre un article éditable seulement par son auteur, on n’affiche les boutons

édition et suppression que si l’identifiant de l’utilisateur actuel correspond à celui de

l’auteur de l’article.

<div class="pull-right" ng-show="authentication.user._id == article.user._id">

<a href="#/articles/edit/{{ article._id}}" class="btn btn-default">Modifier</a>

<a class="btn btn-primary" ng-click="delete();">

Supprimer

</a>

</div>

Page 45: 1 Applications avec Node.js Angular et MEAN

45

b. Contrôleur(s) Angular

Contiennent les crud. Un contrôleur peut servir pour plusieurs vues. Exemple de

contrôleur utilisant un service avec $resource

'use strict';

angular.module('articles').controller('ArticleController', ['$scope', '$routeParams',

'$location', 'Authentication', 'Articles', 'flash',

function ($scope, $routeParams, $location, Authentication, Articles, flash) {

$scope.authentication = Authentication;

$scope.getAll = function () {

$scope.articles = Articles.query();

};

$scope.findOne = function () {

$scope.article = Articles.get({

id: $routeParams.id

});

};

$scope.create = function () {

var article = new Articles({

title: this.title,

content: this.content

});

article.$save(function (response) {

flash.setMessage('Article ajouté.');

$location.path('/articles/view/' + response._id);

$scope.title = '';

$scope.content = '';

}, function (errorResponse) {

$scope.error = errorResponse.data.message;

});

};

$scope.update = function () {

var article = $scope.article;

article.$update(function () {

flash.setMessage('Article modifié.');

$location.path('/articles/view/' + article._id);

}, function (errorResponse) {

$scope.error = errorResponse.data.message;

});

};

$scope.delete = function () {

$scope.article.$delete({id: this.article._id},function () {

flash.setMessage('Article supprimé.');

$location.path('/articles/');

});

};

}

]);

Page 46: 1 Applications avec Node.js Angular et MEAN

46

c. Services

Service avec $http

Documentation

'use strict';

angular.module('articles').service('Articles',function($http){

var baseUrl = "/api/articles/";

this.getAll = function(){

return $http.get(baseUrl);

};

this.findOne = function (id) {

return $http.get(baseUrl + id);

};

this.create = function (article) {

return $http.post(baseUrl,article);

};

this.update = function (article) {

return $http.put(baseUrl,article);

};

this.delete = function (id) {

return $http.delete( baseUrl + id);

}

});

Service avec $resource

Documentation

Installer avec Bower angular-resource

bower install angular-resource --save

Le référencer dans le layout

<script type="text/javascript" src="/lib/angular-resource/angular-

resource.min.js"></script>

Création du service utilisé par le contrôleur

'use strict';

angular.module('articles').factory('Articles', ['$resource',

function ($resource) {

return $resource('api/articles/:id', {

id: '@id'

}, {

update: {

method: 'PUT'

}

},

{

delete:{

method:'DELETE'

}

});

}

]);

Création d’une fonction

delete avec passage du

paramètre id dans l’url

Création d’une fonction

update avec utilisation de la

méthode PUT (non présente

de base)

Page 47: 1 Applications avec Node.js Angular et MEAN

47

d. Vues

Vue liste

<section ng-init="getAll()">

<div>

<h1>Articles</h1>

<hr>

</div>

<div ng-show="articles.length > 0">

<a href="#/articles/create" class="btn btn-primary">Ajouter un nouvel

article</a>

<div ng-repeat="article in articles">

<article>

<header class="entry-header">

<h2><a href="#/articles/view/{{ article._id }}" >{{ article.title

}}</a></h2>

<div class="entry-meta">

<small>

<em class="text-muted">

Posté le <span class="created">{{ article.created |

date:'mediumDate' }}</span>

par <span class="author">{{ article.user.username

}}</span>

</em>

</small>

</div>

</header>

<div class="entry-content">

<div dynamic="article.content" ></div>

</div>

</article>

</div>

</div>

<div class="alert alert-warning text-center" ng-show="articles.length==0">

Voulez-vous ajouter <a href="#/articles/create">le premier article</a>?

</div>

</section>

ng-init : chargement des données à partir du contrôleur Angular, pour la vue détails

on utilse « findOne() »

<section ng-init="getAll()">

ng-repeat pour faire une boucle avec utilisation soit de directive « ng-bind » sur une

balise soit de variable avec accolades {{ }}

Liens

Pour afficher directement une vue

<a href="#/articles/create" class="btn btn-primary">Ajouter un nouvel

article</a>

Avec un paramètre

<a href="#/articles/view/{{ article._id }}" >{{ article.title }}</a>

Lien avec appel d’une fonction du contrôleur

<a class="btn btn-primary" ng-click="delete();">Supprimer</a>

Note ici Bootstrap met en forme le lien, mais on peut afficher le lien correctement

sans style en indiquant simplement « href » sans valeur. Exemple

<a href ng-click="delete();">Supprimer</a>

Page 48: 1 Applications avec Node.js Angular et MEAN

48

Localisation des dates

Documentation avec Angular pour les dates

Copier le fichier dans la langue désirée et le référencer. Les dates avec filtres par

exemple seront automatiquement localisées.

{{ article.created | date:'mediumDate' }}

Affichage du contenu au format HTML

Créer une directive dans un dossier « directives »

angular.module('core').directive('dynamic', ['$compile', function

($compile) {

return {

restrict: 'A',

replace: true,

link: function link(scope, ele, attrs) {

return scope.$watch(attrs.dynamic, function (html) {

ele.html(html);

return $compile(ele.contents())(scope);

});

}

};

}]);

Utilisation

<div dynamic="article.content" ></div>

Page 49: 1 Applications avec Node.js Angular et MEAN

49

Formulaire <section>

<div class="page-header">

<h1>Ajouter un nouvel article</h1>

</div>

<div class="col-md-12">

<form name="articleForm" class="form-horizontal" ng-submit="create()" novalidate>

<fieldset>

<div class="form-group">

<label class="control-label" for="title">Titre</label>

<div class="controls">

<input name="title" type="text" ng-model="title" id="title"

class="form-control" placeholder="Saisissez votre titre ici" required>

</div>

</div>

<div class="form-group">

<div class="controls">

<div contenteditable ng-model="content" class="form-control

content-editable"></div>

</div>

</div>

<div class="form-group">

<input type="submit" class="btn btn-default" value="Publier">

</div>

<div ng-show="error" class="text-danger">

<strong ng-bind="error"></strong>

</div>

</fieldset>

</form>

</div>

</section>

ng-submit sur la balise form pointe la fonction du contrôleur permettant de valider

l’ajout, la modification du formulaire.

Une variante consiste à utiliser ng-click sur le bouton submit

ng-model pour binder les valeurs en édition du formulaire

Afficher un message d’erreur

<div ng-show="error" class="text-danger">

<strong ng-bind="error"></strong>

</div>

Page 50: 1 Applications avec Node.js Angular et MEAN

50

Permettre de sauvegarder le contenu formaté en html dans une base MongoDB

Création d’une directive

angular.module('core').directive("contenteditable", function() {

return {

restrict: "A",

require: "ngModel",

link: function(scope, element, attrs, ngModel) {

function read() {

ngModel.$setViewValue(element.html());

}

ngModel.$render = function() {

element.html(ngModel.$viewValue || "");

};

element.bind("blur keyup change", function() {

scope.$apply(read);

});

}

};

});

Utilisation

<div contenteditable ng-model="article.content"></div>

On peut mettre en forme ce bloc avec Bootstrap par exemple

<div contenteditable ng-model="article.content" class="form-control

content-editable"></div>

Et ajouter une classe CSS simple

.content-editable{ height: 250px; overflow: auto;}

On peut imaginer créer une barre permettant de mettre en forme, insérer des

images, etc. un peu comme un « tinyMCE »

Une div éditable avec

l’illusion que c’est un

textarea

Page 51: 1 Applications avec Node.js Angular et MEAN

51

2. Partie Serveur

a. Routes côté serveur

Il faut renseigner les routes dans « routes.js » du dossier « config » du « server »

var articleController = require('../controllers/articleController'),

auth = require('../controllers/authController');

module.exports = function (app) {

app.get('/api/articles/', articleController.list);

app.get('/api/articles/:id', articleController.read);

app.post('/api/articles/', articleController.create);

app.put('/api/articles/', articleController.update);

app.delete('/api/articles/:id', articleController.delete);

// etc.

}

b. Modèle Mongoose

Dans un dossier « models » du « server »

var mongoose = require('mongoose'),

Schema = mongoose.Schema;

var articleSchema = new Schema({

title: {type:String, required:'Vous devez renseigner un titre'},

content: {type:String, required:'Votre article n\'a aucun contenu.'},

user : { type: Schema.ObjectId, ref: 'User' },

created: {type: Date, default: Date.now }

});

var Article = mongoose.model('Article', articleSchema);

Ajouter les routes

Créer un contrôleur

pour mettre à jour la

base de données

Créer un modèle

Mongoose

Page 52: 1 Applications avec Node.js Angular et MEAN

52

Modèle avec relation

Pour “user” on indique le nom du schéma en relation

user : { type: Schema.ObjectId, ref: 'User' },

L’élément “user” sera chargé depuis le contrôleur grâce à la function “populate”

exports.list = function (req, res) {

Article.find().sort('-created').populate('user').exec(function (err, articles) {

if (err) {

return res.status(400).send({

message: errorHandler.getErrorMessage(err)

});

} else {

res.json(articles);

}

});

};

Page 53: 1 Applications avec Node.js Angular et MEAN

53

c. Contrôleur « Express »

C’est lui qui va recevoir des requêtes depuis le service Angular et renvoyer les

réponses au format JSON

var mongoose = require('mongoose'),

Article = mongoose.model('Article'),

errorHandler = require('./errorHandler');

exports.list = function (req, res) {

Article.find().sort('-created').populate('user').exec(function (err, articles) {

if (err) {

return res.status(400).send({

message: errorHandler.getErrorMessage(err)

});

} else {

res.json(articles);

}

});

};

exports.read = function (req,res){

Article.findOne({ _id: req.params.id }).populate('user').exec(function (err,

article) {

if (err) {

return res.status(400).send({

message: errorHandler.getErrorMessage(err)

});

} else {

res.json(article);

}

});

};

exports.create = function (req, res) {

var article = new Article(req.body);

article.user = req.user || req.session.passport.user;

article.save(function(err){

if (err) {

return res.status(400).send({

message: errorHandler.getErrorMessage(err)

});

} else {

res.json(article);

}

});

};

exports.update = function (req, res) {

Article.update({ _id: req.body._id }, { $set: { title: req.body.title, content:

req.body.content } },

function (err) {

if (err) {

return res.status(400).send({

message: errorHandler.getErrorMessage(err)

});

} else {

res.json(req.body);

}

});

Page 54: 1 Applications avec Node.js Angular et MEAN

54

};

exports.delete = function (req, res) {

Article.remove({ _id: req.params.id }, function (err) {

if (err) {

return res.status(400).send({

message: errorHandler.getErrorMessage(err)

});

} else {

res.send('Ok')

}

});

};

Page 55: 1 Applications avec Node.js Angular et MEAN

55

VII. Plus …

1. Express

a. Variables partagées var express = require('express'),

app = express();

app.locals.pageTitle="Ma variable";

Pas besoin de passer la variable pour pouvoir l’afficher dans la vue. Exemple avec

ejs

<h1><%= pageTitle %></h1>

b. Charger un fichier JSON

On charge simplement un fichier json

app.locals.customers = require("./customers.json");

Le contenu du fichier “customers.json”

[

{"name":"M. PILLON","email":"[email protected]"},

{"name":"M. BELLIN","email":"[email protected]"}

]

Exemple avec ejs

<ul>

<% customers.forEach(function(customer){ %>

<li><%= customers.name %></li>

<% }) %>

</ul>

c. Changer le répertoire des vues app.set('views', __dirname + '/views');

d. Ressources (dossier « public »)

app.use(express.static(__dirname + "/public"));

Utilisation d’une ressource

<link href="/css/default.css" rel="stylesheet" />

Référencer le répertoire « public » de

l’application

Dans un dossier « public »

Inutile d’indiquer le répertoire « public »

Page 56: 1 Applications avec Node.js Angular et MEAN

56

e. Express Generator

Créer un projet avec Express generator

Installer express generator en global

npm install express-generator -g

Naviguer jusqu’au dossier parent qui contiendra le du projet puis

express nomprojet

Installation des dépendances du projet généré : Naviguer jusqu’au dossier du projet

généré puis

npm install

Lancer le serveur

node bin/www

On peut se rendre à la page « http://localhost:3000/ »

Page 57: 1 Applications avec Node.js Angular et MEAN

57

2. “Sans Express”

a. Serveur

b. Créer un serveur (fichier « server.js » ou « app.js » par exemple)

var http = require('http'); var html = "<!DOCTYPE html><html><head><meta charset=\"utf-8\" /><title>Demo NodeJS</title></head><body><h1>D&eacute;monstration NodeJS</h1></body></html>"; var server = http.createServer(function (req, res) { res.writeHead(200, { "Content-Type": "text/html" }); res.write(html); res.end(); }); server.listen(8080); console.log("listen on port 8080");

b. Lancer un serveur :

Depuis une invite de commande, naviguer jusqu’au dossier

du projet puis

node server.js

c. Aller à « http://localhost:8080/ » pour tester

(CTRL + C pour arrêter le serveur)

c. URL et paramètres

Paramètres d’url (querystring)

var http = require('http'); var url = require('url'); var querystring = require('querystring'); var server = http.createServer(function (req, res) { var params = querystring.parse(url.parse(req.url).query); res.writeHead(200, { "Content-Type": "text/plain" }); if ('firstname' in params && 'lastname' in params) { res.write('Hello ' + params['firstname'] + ' !'); } else { } res.end(); }); server.listen(8080);

Réponse. Types de contenu :

Texte : text/plain

HTML : text/html

CSS : text/css

Image : image/jpeg

Vidéo : video/mp4

Zip : application/zip

Etc.

Création du serveur

« req » pour request,

« res » pour response

Chargement du module « http »

Port 3000 ou 8080 en local

Besoin du module

« querystring » pour récupérer

les paramètres dans l’url

Récupération des paramètres

passés dans un tableau

Accès aux membres du tableau

de paramètres

Page 58: 1 Applications avec Node.js Angular et MEAN

58

d. Créer un index listant les contrôleurs (function (controllers) { var homeController = require('./homeController.js'); var contactController = require('./contactController.js'); controllers.init = function (app) { homeController.init(app); contactController.init(app); } })(module.exports);

Utilisation

var express = require('express'), app = express(), controllers = require('./controllers'); app.set("view engine", "vash"); controllers.init(app); app.listen(8080);

e. Evénements

Ecouter et émettre des événements

var eventEmitter = require('events').EventEmitter; var myEvent = new eventEmitter(); myEvent.on('myEventEmittted', function (message) { console.log(message); }); myEvent.emit('myEventEmittted', 'Ev&eacute;nement d&eacute;clench&eacute;');

On indique le dossier

contenant les contrôleurs

Initialisation des contrôleurs en

appelant la méthode

Besoin du module « events »

On écoute l’événement, avec fonction callback

Page 59: 1 Applications avec Node.js Angular et MEAN

59

3. Bases de données

a. SQL Server

Plusieurs Drivers existent… Exemple avec Tedious (documentation)

npm install tedious

Note : il faut lancer SQL Browser

var Connection = require('tedious').Connection; var Request = require('tedious').Request; exports.getAll = function (req, res) { var config = { server: 'DonJR', userName: 'sa', password: 'password123456' , database: 'peopledb', options : { instanceName: 'SqlExpress', rowCollectonOnRequestCompletion: 'true' } } var connection = new Connection(config); connection.on('connect', function (err) { if (err) { console.log("Erreur"); } else { var request = new Request("use peopledb; select * from Person", function (err, rowCount, columns) { var result = []; columns.forEach(function (column) { var person = new Person(column[1].value, column[2].value); result.push(person); }); res.render('index.ejs', { people: result }); }); connection.execSql(request); } }); };

Autre exemple avec paramètres

var TYPES = require('tedious').TYPES; var sql = 'use peopledb; insert into Person(name,twitter) values(@name,@twitter);'; var request = new Request(sql, function (err) { console.log("Erreur"); }); request.addParameter('name', TYPES.VarChar, 'M. Bellin'); request.addParameter('twitter', TYPES.VarChar, '@mbellin'); connection.execSql(request);

Nom du serveur, indiquer le

nom de l’instance

(instanceName) dans

« options ».

« rowCompletionOnRequestCompletion »

permet de récupérer le résultat de la

requête en callback

Exécution de la

requête

Paramètres

Page 60: 1 Applications avec Node.js Angular et MEAN

60

b. MongoDB

Autres bases NoSQL : RavenDB, Cassandra, Neo4j, Redis, CouchDB

Installation locale au projet avec Windows

a. Télécharger le zip de MongoDB (ou le msi et choisir le dossier du projet

pourl’installation)

b. Créer un dossier « mongodb » dans le projet et y copier l’ensemble de

l’archive

c. Créer un dossier « data » dans « mongodb »

d. Ouvrir une invite de commande, naviguer jusqu’au dossier « mongodb » du

projet et indiquer le dossier dans lequel la base est installée

mongod –dbpath ./data

(Un message « waiting for connections on port 27017 » apparait si tout se passe bien)

Pour avoir accès aux bases depuis « http://localhost:28017 »

mongod –dbpath ./data --rest

Page 61: 1 Applications avec Node.js Angular et MEAN

61

Créer une base en invite de commandes

Une base de données NoSQL a des collections. Chaque collection a des documents

(JSON), « schemaless » (par exemple un document peut avoir seulement un nom et

un autre avoir un nom et un email)

Créer ou utiliser une base

use peopledb

Insertion d’un document dans une collection

db.people.insert({name:"J. Romagny", twitter:"@romagny13"})

Un identifiant unique est ajouté à chaque document (_id)

Obtenir un document

Utilisation de la méthode « find »

Retourner tous les documents de la collection

db.people.find()

Filtrer les résultats

$gt : plus grand

$lt : plus petit

$gte : plus grand ou égal

$lte: plus petit ou égal

$or : ou

$and : et

$in, $all, $exist, $type ,$regex

db.people.find({name : "J. Romagny"})

db.people.find({age : {$gt :18}})

Trier

Avec la méthode « sort »

db.people.find().sort({name :1})

Modification de document

db.people.update({name : "J. Romagny"}, {$set :{twitter : "@jerome_romagny13"}})

Supprimer un document

db.people.remove ({name : "J. Romagny"})

$unset permet de supprimer une propriété du document

Collection Document

Modification de la propriété « twitter » Filtre des documents recherchés Méthode « update »

Tri de la collection de personnes sur le nom en ordre

croissant

Pour un ordre décroissant utiliser « -1 »

Page 62: 1 Applications avec Node.js Angular et MEAN

62

VIII. MEAN.JS MEAN = MongoDB + Express + AngularJS + Node.js

Documentation

Installation

a. Pré-requis

Node.js et NPM

MongoDB

Bower

Grunt

b. Installation avec Yo

Installation de Yeoman en global

npm install -g yo

Installation du generator meanjs

npm install -g generator-meanjs

Puis création d’un projet MEAN …Naviguer jusqu’au dossier où créer le projet avec la

commande puis

yo meanjs

Répondre aux questions… donner un nom à l’application …

c. Une fois le projet généré. Installer tous les modules avec la commande

npm install

Lancer MongoDB. Depuis une invite de commande, naviguer jusqu’au dossier

d’installation puis

mongod

Et lancer le serveur. Depuis une invite de commande naviguer jusqu’au projet mean

et

grunt

Ou simplement

node server.js

Page 63: 1 Applications avec Node.js Angular et MEAN

63

Le projet généré

Configuration

Serveur

Package avec informations du

projet et dépendances

Chaque module est divisé avec une partie

client (Angular) et une partie serveur (Node)

Librairies installées avec NPM

Page 64: 1 Applications avec Node.js Angular et MEAN

64

Exemple de module (« users » pour l’authentification, utilise Passport)

Moteur de vues utilisé Swig

Les vues sont dans la partie « client » des modules

Le layout, la page index, pages « complètes » sont dans la partie « server »

Le module core

Client

Contient le fichier de config permettant d’enregistrer des modules pour

l’application

Fichier init qui permet également d’intercepter au changement d’état et

vérifier l’autorisation. Des rôles sont définis pour les états et c’est par rapport à

ceux-ci que l’on vérifie si l’utilisateur a les droits pour accéder au prochain

état. S’il n’est pas autorisé, l’utilisateur est redirigé vers la page

d’authentification.

Routes avec $stateProvider avec les vues home, 404, un fichier pour les routes

admin

Contient également le header avec le menu principale inclus dans le layout

du site

Un contrôleur pour le header (avec le service « Authentication » pour switcher

entre utilisateur connecté et anonyme) et un contrôleur pour la vue home

Feuille de style core.css, images (favicon,logo)

Un service pour le menu

Server

Routes pour page index (‘/’) et pages not found,etc.

Le contrôleur ne sert qu’à afficher les pages index, not found,etc.

Le layout, la page index, pages « complètes » (404,500)

Page 65: 1 Applications avec Node.js Angular et MEAN

65

Génération de code Documentation

Création d’un module MEAN

yo meanjs:mean-module

Server … « Express »

Création d’un contrôleur côté serveur avec crud (create, read, update,

delete, list)

yo meanjs:express-controller

Choisir le module (exemple « voyages ») puis donner un nom au contrôleur

Modèle pour Mongoose

yo meanjs:express-model

Choix du module et nom du modèle

Routes pour le module

yo meanjs:express-route

Choix du module et nom du fichier de routes

Client … « Angular »

Contrôleur Angular

yo meanjs:angular-controller

Choix du module et nom du contrôleur

Routes Angular avec $stateProvider

yo meanjs:angular-route

Choix du module, nom, …

Vue

yo meanjs:angular-view

Choix du module, nom de la vue, du contrôleur, routes

Exemple de module généré nommé « voyage »

Page 66: 1 Applications avec Node.js Angular et MEAN

66

Angular

« Express »

Page 67: 1 Applications avec Node.js Angular et MEAN

67

IX. Publication Heroku

1. Créer un compte

2. Créer une nouvelle application

3. Donner un nom d’application

Page 68: 1 Applications avec Node.js Angular et MEAN

68

4. Plusieurs méthodes de déploiement disponibles

Publication du projet

1. « package.json »

{

"name": "NodejsAuthDemo",

"version": "0.0.0",

"description": "NodejsAuthDemo",

"main": "server.js",

"author": {

"name": "romagny13",

"email": ""

},

"engines": {

"node": "0.12.x",

"npm": "2.7.x"

},

"dependencies": {

"body-parser": "^1.12.3",

"ejs": "^2.3.1",

"express": "^4.12.3",

"mongoose": "^4.0.2",

"passport": "^0.2.1",

"passport-local": "^1.0.0"

}

}

2. « procfile.js »

Créer un fichier « procfile.js » à la racine du projet et y ajouter le nom du serveur

web: node app.js

Ajouter une section « engines » avec les

versions de node et npm

… que l’on peut obtenir avec « node --

version » et « npm --version »).

Page 69: 1 Applications avec Node.js Angular et MEAN

69

3. Dans le serveur « server.js » ou « app.js » selon le nom donné, modifier le port

var port = process.env.PORT || 3000;

app.listen(port);

console.log("Listen on " + port + "...");

4. MongoDB addons (option « free » mais demande quand même de rentrer des

informations de paiement)

Permet de créer une base. On obtient également les informations de connexion

(Créer un utilisateur minimum) et modifier la chaine de connexion dans le projet.

mongoose.connect('mongodb://………);

Port de test