Bonjour à tous, nous avons précédemment fait une petite introduction a AngularJS, avec certains concepts permettant de commencer à l’utiliser. Pour continuer sur ce schéma, je vous propose de découvrir ensemble comment créer et se servir des services dans une application AngularJS.
Cinq méthodes: provider
, factory
, service
, value
et constant
. Ce sont ces méthodes de module qui permettent de créer un service:
provider
est la manière la plus configurable de créer un servicefactory
, service
et value
délèguent à la méthode provider
avec une configuration standardconstant
se comporte de manière plus spécifique puisque chaque service constant
est son propre providervalue
et constant
sont des services plutôt destinés à contenir des valeurs/objets comme leur nom l’indique, tandis que factory
et service
sont juste des raccourcis de la méthode provider
et permettent donc de créer des services sans se soucier de leur configuration.
La différence entre service
et factory
? Voici ce que l’on trouve sur la documentation d’AngularJS:
Factory and Service are the most commonly used recipes. The only difference between them is that Service recipe works better for objects of custom type, while Factory can produce JavaScript primitives and functions.
Puisque la méthode factory
se comporte comme une fonction JavaScript habituelle, on peut simuler des variables privées qui permettent de ne pas exposer toute la logique si ce n’est pas nécessaire. C’est donc généralement cette méthode qui est préférée pour la création de service.
J’ai pensé, pour illustrer ce billet, a un service de génération de logs. Créer des logs avec JavaScript est déjà possible, alors nous allons englober le système existant pour l’enrichir. Commençons par poser la base de ce dont nous avons besoin :
<!DOCTYPE html>
<html ng-app="serviceApp">
<head>
</head>
<body>
<div ng-controller="indexCtrl">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>
<script src="app.js"></script>
<script src="services.js"></script>
</body>
<html>
Un fichier index.html
dans lequel nous chargeons AngularJS bien sûr, un fichier JavaScript pour l’application et le fichier qui va contenir notre service.
Le code du fichier app.js
:
var serviceApp = angular.module('serviceApp', []);
serviceApp.controller('indexCtrl', ['$scope', function($scope) {
}]);
Et enfin notre service:
var services = angular.module('services', []);
services.factory('Logger', function() {
var logger = {};
var active = false; // par défaut le service est désactivé
// Retourne la date et l'heure courante
var currentDateTime = function() {
var currentdate = new Date();
var datetime = currentdate.getDate() + '/' +
(currentdate.getMonth() + 1) + '/' +
currentdate.getFullYear() + ' ' +
currentdate.getHours() + ':' +
currentdate.getMinutes() + ':' +
currentdate.getSeconds();
return datetime;
}
logger.turnOn = function() {
active = true;
};
logger.turnOff = function() {
active = false;
};
// Retourne le message reçu précédé de la date et de l'heure,
// avec le niveau d'alerte voulu
logger.log = function(msg, type) {
var type = type || '';
if(console && active) { // si la console de JavaScript existe et que le service est actif
var message = currentDateTime() + ' - ' + msg;
switch (type) {
case 'e':
console.error(message);
break;
case 'w':
console.warn(message);
break;
case 'd':
console.debug(message);
break;
default:
console.log(message);
break;
}
}
};
return logger;
});
Ce service contient un paramètre et une méthode privée, et expose les méthodes: turnOn
, turnOff
et log
.
Il faut ajouter en dépendance de l’application le module services
et en dépendance du contrôleur notre service:
var serviceApp = angular.module('serviceApp', ['services']);
serviceApp.controller('indexCtrl', ['$scope', 'Logger', function($scope, Logger) {
}]);
Le contrôleur accède maintenant à toutes les fonctions visibles de notre Logger
:
serviceApp.controller('indexCtrl', ['$scope', 'Logger', function($scope, Logger) {
Logger.turnOn(); // On active le logger
Logger.log('Page chargée !'); // Log au chargement de la page
}]);
Le service en l’état est très basique. Que pensez vous de l’enrichir en récupérant des informations sur l’utilisation de l’application ou sur l’utilisateur ? Pour cela, nous allons avoir besoin d’un service qui s’occupera de contenir les données, et puisque ce service sera exclusivement destiné à cette tâche, alors la méthode de module value
est toute indiquée:
services.value('Current', {
user: {
firstname: 'Numa',
lastname: 'Claudel',
},
collectedData: {},
usedActions: []
});
Complétons notre Logger
:
services.factory('Logger', ['Current', function(Current) {
.
.
.
// Enregistre quelques informations sur l'utilisateur
logger.storeUserInfos = function() {
Current.collectedData.screen = { width: screen.width, height: screen.height, screenColor: screen.colorDepth };
};
// Enregistre l'action utilisée par l'utilisateur et à quelle heure
logger.storeUserAction = function(name) {
Current.usedActions.push({ action: name, at: currentDateTime() });
};
return logger;
}]);
Vous remarquerez que notre Logger
dépend maintenant du service Current
que nous avons fraîchement créé. Maintenant complétons le contrôleur pour qu’il utilise les nouvelles fonctions de notre Logger
:
serviceApp.controller('indexCtrl', ['$scope', 'Logger', 'Current',
function($scope, Logger, Current) {
Logger.turnOn(); // On active le logger
Logger.log('Page chargée !'); // Log au chargement de la page
$scope.user = Current.user;
Logger.storeUserInfos(); // récupère et stocke quelques données sur l'utilisateur
$scope.actionExemple = function() {
Logger.storeUserAction('actionExemple'); // stocke l'action avec l'heure courante
// vider les données de temps en temps
if(Current.usedActions.length >= 10) {
// => envoyer les données au serveur
Current.usedActions = [];
}
}
}
]);
Et enfin ajoutons le bouton de l’actionExemple
à notre vue:
<div ng-controller="indexCtrl">
peux tu cliquer sur ce bouton ? :
<button ng-click="actionExemple()">Action</button>
</div>
Vous noterez la présence de la directive ng-click
fournie par AngularJS, qui nous permet d’appeler une fonction du contrôleur au clic sur l’élément.
Pour résumer un peu, on peut observer que pour créer un service et l’utiliser il faut:
angular.module('unNomDeModule', [])."leType"
ou “leType” sera une des 5 méthodes de définition de serviceOn a très souvent besoin d’aller récupérer ou envoyer des données sur le serveur en Ajax, alors pourquoi ne pas centraliser ça dans un module de services Ajax.
ajax.factory('GetSomeData', ['$http', function($http) {
return {
get: function(params, callback) {
$http.get('/resource/url?' + params).
success(function(data, status) {
callback(data, status);
}).
error(function(error, status) {
callback(error, status);
});
}
};
}]);
Une requête Ajax spécifique en GET construite grâce au service $http
fournit par AngularJS, dans laquelle je me suis servis d’un des raccourcis de définition de méthode disponible. Utiliser cette requête dans un contrôleur se fera de la sorte: GetSomeData.get('color=blue', function() {})
, mais ce n’est qu’un exemple.
Et dans le cas de l’utilisation d’une ressource serveur, faut-il définir toutes les requêtes d’un CRUD ? Heureusement, pour interagir avec un serveur RESTful comme dans le cas de Rails, AngularJS fournit un autre service qui apporte les méthodes nécessaires à ce type d’interactions. Il se nomme $resource du module ngResource
:
ajax.factory('User', ['$resource', function($resource) {
return $resource('/users/:id', { id: '@id' }, { update: { method: 'PUT' } });
}]);
Ce service nous permet de définir une ressource, dans ce cas User
, que l’on va pouvoir réutiliser dans n’importe quel contrôleur. A savoir que $resource
fournit de base les méthodes: get
, save
, query
, remove
et delete
, auxquelles nous avons ajouté update
.
Pour utiliser cette ressource, il faut charger angular-resource
et le fichier ajax.js
dans notre index.html
, ajouter le module ngResource
en dépendance de l’application et ajouter User
en dépendance de notre contrôleur :
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>
<script src="https://code.angularjs.org/1.2.16/angular-resource.min.js"></script>
<script src="app.js"></script>
<script src="services.js"></script>
<script src="ajax.js"></script>
</body>
<html>
var serviceApp = angular.module('serviceApp', ['services', 'ajax', 'ngResource']);
serviceApp.controller('indexCtrl', ['$scope', 'Logger', 'Current', 'User',
function($scope, Logger, Current, User) {
Logger.turnOn(); // On active le logger
Logger.log('Page chargée !'); // Log au chargement de la page
// récupère l'utilisateur voulu
User.get({ id: 1 }, function(data) {
Current.user = data;
});
// un update des informations de l'utilisateur se fera sous cette forme:
// User.update(Current.user, function() {});
.
.
.
}
]);
Créer des services avec AngularJS permet de garder les contrôleurs éloignés de la logique d’accès aux données, ou de fonctionnements généraux de l’application. Le code d’un contrôleur peut ainsi se concentrer sur la fonctionnalité propre à la portion de vue qui lui est allouée. J’espère vous avoir aidé à y voir un peu plus clair au sujet des services avec AngularJS.
L’équipe Synbioz.
Libres d’être ensemble.