Fonctionnement
angular.module('demo', ['ngResource'])
.factory('user',function($resource){
return $resource('/user/:id', {id:'@_id'});
});
L'objet (optionnel) passé en paramètre permet
d'attribuer des valeurs par defaut aux paramètres de la
route.
Prefixer d'un @ : $resource ira chercher la valeur de
l'attribut correspondant dans l'objet sur laquelle
l'action est appelée
Manipulations de base
$resource nous renvoie un objet disposant de 5 methodes :
get : récupère un objet
save : persiste un objet
query : retourne toute la collection
remove : supprime un enregistrement
delete : alias de remove
angular.module('demo', ['ngResource'])
.factory('user', function($resource) {
return $resource('http://my-web-service.org/user/:id', {id:'@_id'});
});
.controller('UserController', function(user) {
this.save = function(user) {
user
.save(this)
.$promise
.then(function(user) {
//do something
})
.catch(function(err) {
//do something
})
}
this.remove = function() {
user
.remove()
.$promise
.then(function() {
//do something
})
}
this.getOne = function() {
user
.get()
.$promise
.then(function(user) {
//do something
})
}
this.getAll = function() {
user
.query()
.$promise
.then(function(users) {
//do something
})
}
});
Méthodes personnalisées
Au moment de la création de la ressource, il est possible de
rajouter des méthodes personnalisées, comme par exemple la
methode update qui est absente.
angular.module('demo',[
'ngResource'
])
.factory('userModel',function($resource){
return $resource('http://my-web-service.org/user/:id', {id:'@_id'},{
'update': { method:'PUT' },
'follow' : {
method : 'Post',
url : 'http://my-web-service.org/user/:id/like'
}
});
})
.controller('UserController',function(){
var self = this;
this.save = function(user){
if(user._id){
user
.save()
.$promise
.then(function(me){
self.me = me;
})
}else{
user
.update()
.$promise.then(function(me){
self.me = me
})
}
}
});
js-data
"Respect your data"
"Give your data the treatment it deserves
with a data store built for AngularJS."
bower install --save js-data js-data-angular
Dernier arrivé et inspiré par Ember.js, angular-data corrige
les fautes de ses prédécesseurs.
Très bonne gestion du cache, particulièrement adapté aux
fonctionnalités en temps réel (WebSocket, Server push...)
Permet avec une API unique de persister tout aussi bien à
distance (http), que localement (indexedDB). Il suffira de
préciser un "adapter".
Compatible avec Node.js (en utilisant l'adapter MongoDB par
exemple)
Data-store Memory and persistent
A la base simple Tout ce qui est récupéré via l'adapter
service de stockage spécifié (http par défaut) et correspondant
en mémoire, la au model est stocké en mémoire cache.
librairie a évolué et
propose desormais
bien plus.
L'api expose deux méthodes pour chaque operations CRUD,
une pour la mémoire cache, l'autre via l'adapter
Définir une resource
name définit le nom de la resource
idAttribute fait office de clef primaire
endpoint fait reference aux point d'entrée dans l'API REST
baseUrl est l'url de base de notre API REST
var app = angular.module('myApp', ['js-data']);
app.factory('User', function (DS) {
return DS.defineResource({
name: 'user',
idAttribute: '_id',
endpoint: 'users',
baseUrl: 'https://example.com/api'
});
});
Définir un adapter
Nous aurons la possiblité plus tard
de changer d'adapter
var app = angular.module('myApp', ['js-data']);
app.factory('User', function (DS) {
return DS.defineResource({
name: 'user',
idAttribute: '_id',
endpoint: 'users',
baseUrl: 'https://example.com/api',
defaultAdapter: 'DSLocalForageAdapter'
});
});
Ici on indique que toutes les données seront persistées par
défaut localement en utilisant localForage, une librairie
développée par Mozilla qui standardise le localStorage
2 types de méthodes :
synchrone
asynchrone (retourne une promise)
Les méthodes synchrones
vont récupérer les données
depuis le cache
Les méthodes asynchrones vont quant à
elles recupérer les données en utilisant
l'adapter (http par default)
Création via l'adapter Le C de CRUD
angular.module('app',[])
.controller('UserFormController', function(User) {
// this.submit est juste la pour l'exemple
this.submit = function(userData){
// une requête HTTP POST est faite (en fonction du paramétrage de User)
User.create(userData).then(function(createdUser) {
})
}
})
Ou dans le "memory store"
angular.module('app')
.controller('UserController', function(User){
var user = {
id: 10,
name: 'MrFoo'
};
// l'objet n'est pas persisté via l'adapter, mais uniquement stocké en cache
User.inject(user);
})
Récupérer des données via l'adapter Le R de CRUD
Ou dans le "memory store"
angular.module('app',[])
.controller('UsersController', function(User){
User.getAll(); //retourne undefined
User.findAll().then(function(users) {
// users est un array d'instances de la ressource user
User.getAll(); // retourne un array d'instances de la ressource user
})
})
angular.module('app')
.controller('UserController', function(User) {
User.get(5); // retourne undefined
User.find(5).then(function(users) {
// users est une instance de la ressource user
User.get(5); // retourne une instance de la ressource user
})
})
Mettre à jour des données via l'adapter
Le U de CRUD
angular.module('app',[])
.controller('UsersController', function(User) {
var myUser = User.get(5);
myUser.name = 'MrBar';
User.save().then(function(user) {
//user est l'objet mis à jour
});
})
Supprimer des données via l'adapter
Le D de CRUD
angular.module('app',[])
.controller('UsersController', function(User) {
User.destroy(5).then(function() {
User.get(5) // retourne undefined
});
})
Specifier un adapter differend de celui par default
angular.module('app',[])
.controller('UsersController',function(User){
User.findAll( { adapter: 'DSLocalForageAdapter' } ).then(function(){
});
})
L'attribut validate Validation
permet de définir
une fonction de var app = angular.module('myApp', ['js-data']);
validation. app.factory('User', function (DS) {
beforeValidate et return DS.defineResource({
afterValidate name: 'user',
seront executé idAttribute: '_id',
avant et aprés la endpoint: 'users',
validation. baseUrl: 'https://example.com/api',
beforeValidate: function (resourceName, attrs, cb) {
//faites quelque chose avant la validation
},
validate: function (resourceName, attrs, cb) {
if (!attrs.firstName) {
cb('Title is required');
} else {
cb(null, attrs);
}
},
afterValidate: function (resourceName, attrs, cb) {
//faites quelque chose aprés la validation
}
});
Intercepteurs
De la même manière var app = angular.module('myApp', ['js-data']);
nous allons pouvoir app.factory('User', function (DS) {
spécifier des callback
qui seront appelés à return DS.defineResource({
des moments clef de name: 'user',
la vie nos objets. idAttribute: '_id',
endpoint: 'users',
baseUrl: 'https://example.com/api',
beforeCreate: function (resourceName, attrs, cb) {
//faites quelque chose avant la validation
},
beforeInject: function (resourceName, attrs, cb) {
//faites quelque chose avant la validation
},
beforeUpdate: function (resourceName, attrs, cb) {
//faites quelque chose aprés la validation
}
beforeDestroy: function (resourceName, attrs, cb) {
//faites quelque chose aprés la validation
}
});
Intercepteurs
La même chose mais var app = angular.module('myApp', ['js-data']);
après l'action. app.factory('User', function (DS) {
return DS.defineResource({
name: 'user',
idAttribute: '_id',
endpoint: 'users',
baseUrl: 'https://example.com/api',
afterCreate: function (resourceName, attrs, cb) {
//faites quelque chose aprés la validation
},
afterInject: function (resourceName, attrs, cb) {
//faites quelque chose aprés la validation
},
afterUpdate: function (resourceName, attrs, cb) {
//faites quelque chose aprés la validation
}
afterDestroy: function (resourceName, attrs, cb) {
//faites quelque chose aprés la validation
}
});
Sérialisation/Désérialisation
serialize sera var app = angular.module('myApp', ['js-data']);
appelée juste app.factory('User', function (DS) {
avant que les
données soient return DS.defineResource({
envoyées à name: 'user',
l'adapter idAttribute: '_id',
endpoint: 'users',
deserialize est baseUrl: 'https://example.com/api',
appelée après serialize: function serialize(resourceName, data) {
que des données return {
aient été reçues payload: data
de l'adapter }; },
deserialize: function deserialize(resourceName, data) {
return {
payload: data
};
}
});
Pratique si le web service renvoie les données
dans un format non standard
Méthodes personnalisées
Toute méthode définie var app = angular.module('myApp', ['js-data']);
dans l'attribut app.factory('User', function (DS) {
methods sera ajoutée
au prototype des return DS.defineResource({
instances de notre name: 'user',
ressource idAttribute: '_id',
endpoint: 'users',
baseUrl: 'https://example.com/api',
methods: {
fullName: function () {
return this.firstName + ' ' + this.lastName;
}
}
}
});
Méthodes statiques
var app = angular.module('myApp', ['js-data']);
app.factory('User', function (DS) {
var User = DS.defineResource({
name: 'user',
idAttribute: '_id',
endpoint: 'users',
baseUrl: 'https://example.com/api',
methods: {
fullName: function () {
return this.firstName + ' ' + this.lastName;
}
}
}
User.aStaticMethod = function(){
}
return User;
});
Relations entre entités
var app = angular.module('myApp', ['js-data']);
app.factory('User', function (DS) {
return DS.defineResource({
name: 'user',
idAttribute: '_id',
endpoint: 'users',
baseUrl: 'https://example.com/api',
relations: {
hasMany: {
comment: {
localField: 'comments',
foreignKey: 'userId'
}
},
hasOne: {
profile: {
localField: 'profile',
foreignKey: 'userId'
}
},
belongsTo: {
organization: {
localKey: 'organizationId',
localField: 'organization',
// impacte la structure de l'url /organization/15/user/4
parent: true
}
}
});
Récupérer les données des relations
angular.module('app')
.controller('UserController',function(User){
User.find(5).then(function(user){
//user est une instance de la resource user
DS.loadRelations( user, ['comment', 'profile']).then(function (user) {
user.comments;
user.profile;
});
})
})
Bind dans un controlleur avec $scope
angular.module('app')
.controller('UserController',function(User,$scope){
User.bindOne($scope, 'me', 1);
User.bindAll($scope, 'friends');
})
Bind dans un controlleur sans $scope
angular.module('app')
.controller('UserController',function(User,$scope){
var self = this;
$scope.$watch(function () {
return User.lastModified($stateParams.id);
}, function () {
self.me = User.get($stateParams.id);
});
$scope.$watch(function () {
// Changes when anything in the User collection is modified
return User.lastModified();
}, function () {
self.friends = User.filter(query);
});
})