Comment utiliser les services $resource avec AngularJS

Comme je ne vous l’ai jamais dit, j’ai commencé à apprendre à utiliser AngularJS pour faire des applications Web. Et c’est pas mal du tout. J’apprécie particulièrement le fait de construire les vues (la partie HTML) en … HTML, ou presque. Et construire une application Web en HTML ce n’est pas aussi courant que cela puisse paraître.

Enfin bref, voilà ce que j’ai appris à propos de la communication entre AngularJS et un serveur REST, en utilisant les services $resource.

AngularJS ?

Comment utiliser les services $resource avec AngularJS : le logo d'AngularJS

Voyons ce qu’est AngularJS, comme il s’agit de mon premier article sur le sujet.

AngularJS est un framework JavaScript développé par Google, qui offre, comme son nom l’indique un cadre pour travailler avec JavaScript et produire des applications Web (voire des sites Web).

Il est basé sur le principe Modèle/Vue/Contrôleur (MVC).

Le principe : le contrôleur en JavaScript gère les données et les actions disponibles dans la vue en HTML.

Je vous propose un exemple pour vous montrer la simplicité d’écriture offerte par ce framework.

Dans le contrôleur, on définit les données :

$scope.users = [
  {"userId": 123, "name": "Utilisateur 1"}, 
  {"userId": 456, "name": "Utilisateur 2"}, 
  {"userId": 789, "name": "Utilisateur 3"}
];

Dans la vue, il ne reste qu’à les afficher, au beau milieu des autres balises HTML :

<ul>
  <li ng-repeat="for user in users">{{user.name}}</li>
</ul>

L’attribut ng-repeat n’est pas standard à HTML, mais vient d’AngularJS. Ceci est possible grâce à HTML5.

Cette directive permet simplement de répéter la balise <li> autant de fois qu’il y a d’utilisateurs, pour y afficher leur nom.

Si vous voulez avoir un bon aperçu des possibilités pour débuter, je vous conseille de suivre le tutoriel directement sur le site du framework.

Aussi, Le format des données dans le contrôleur est en JSON et peut très bien venir d’un service REST. C’est d’ailleurs le sujet de cet article.

Créer une ressource

Une ressource, appelée $resource dans AngularJS, permet de traiter les informations provenant d’un serveur REST sans trop de peine.

Il est possible de créer une ressource au moment où on en a besoin, dans un contrôleur par exemple :

var User = $resource('/users/:userId', {userId:'@id'});

Cette ressource ne sera disponible qu’à l’endroit où elle a été instanciée.

Il est également possible de les inclure dans des services, et les rendre disponibles depuis toute l’application :

angular.module('monApp', ['ngResource'])
  .factory('User', ['$resource',
    function($resource){
      return $resource('/users/:userId', {userId:'@id'});
    }]
  );

Quelques explications :

  • le service REST /users est appelé
  • :userId sera remplacé par la valeur passée en paramètre au moment du GET, ou récupérée automatiquement en cas de mise à jour ou de suppression après avoir fait un GET
  • {userId: '@id'} indique que le champ id de l’utilisateur servira à peupler automatiquement :userId. Il peut contenir autant de paramètres que nécessaire.

La ressource propose automatiquement les méthodes get(), query(), save(), et delete() (ou remove(), au choix).

Si vous êtes habitués à REST, vous avez remarqué la présence de la seule méthode save(). Alors vous vous posez certainement la question comment faire la différence entre POST et PUT. La réponse est simple, save() émet des requêtes POST, et c’est tout.
Tout ceci n’est pas très RESTful, nous y reviendrons.

Il est également possible de créer d’autres méthodes personnalisées, pour créer des raccourcis.

Utilisation

Récupérer une liste de données

Quelle que soit la façon de déclarer la ressource, dans le contrôleur (ou ailleurs), pour reprendre notre premier exemple, où les données étaient statiques :

$scope.users = [
  {"userId": 123,"name": "Utilisateur 1"}, 
  {"userId": 456,"name": "Utilisateur 2"}, 
  {"userId": 789,"name": "Utilisateur 3"}
];

Pour les rendre dynamiques :

$scope.users = User.query();

Et c’est tout.

Récupérer une entrée

Pour récupérer un utilisateur en particulier, il suffit d’utiliser la méthode get() à la place de query(), et d’indiquer son identifiant :

var user = User.get({userId:123});

La création

Pour créer un objet, il suffit d’instancier la ressource, la modifier si nécessaire puis de l’enregistrer, par exemple :

var user = new User();
user.name = 'Utilisateur 1337';
user.$save();

Et si vous voulez réinjecter le nouvel utilisateur dans une liste récupérée avec query() :

$scope.users.push(user);

La modification

Comme je vous l’indiquais, $save() utilise POST, aussi bien pour créer que pour mettre à jour. Si votre API est RESTful, il est nécessaire de créer une nouvelle méthode pour agir sur la ressource : update() (ou tout autre nom de votre choix).

Modifions le service que nous avions déjà créé :

angular.module('monApp', ['ngResource'])
  .factory('User', ['$resource',
    function($resource){
      return $resource('/users/:userId', {userId:'@id'},
        // il suffit d'ajouter ce troisième paramètre :
        { 'update': {method: 'PUT'} }
      );
    }
  ]);

Comme vous le voyez l’ajout de la méthode put n’est pas très longue, mais peut être fastidieuse si l’application comporte de nombreux services.

Si vous voulez rendre les choses un peu plus automatiques, c’est-à-dire implémenter la méthode update() automatiquement, ou encore que save() fasse automatiquement (ou presque) la distinction entre la création ou la mise à jour, je vous conseille cet article.

Et pour utiliser notre méthode update() :

user.name = 'Utilisateur 42';
user.$update();

Méthodes personnalisées

Remarquez aussi qu’il est possible de créer des méthodes personnalisées, qui pourront servir de raccourcis :

angular.module('monApp', ['ngResource'])
  .factory('User', ['$resource',
    function($resource){
      return $resource('/users/:userId', {userId:'@id'},
        {
          'update': {method: 'PUT'},
          'enable': {method: 'PUT', {userId: '@id', params; { 'enabled': true } }
        }
      );
    }
  ]);

Ici, enabled est un champ du service users.
L’appel à user.$enable() passe sa valeur à true automatiquement, sans code supplémentaire dans le contrôleur.

Les méthodes sont asynchrones

Les actions sont effectuées automatiquement en mode asynchrone.

Par exemple, avec :

var user = User.get({userId:123});

la ressource user est créée mais reste vide jusque la réception de la réponse.
Ainsi, avec le système de vues et de two way binding d’AngularJS, cette ligne suffit à mettre à jour automatiquement l’affichage au moment de la réception, sans bloquer l’application.

Pour exécuter d’autres actions lorsque la réponse est effectivement arrivée :

var user = User.get({userId:123}, function() {
  user.name = 'Utilisateur 123';
  user.$save();
});

Ici, l’utilisateur dont l’identifiant est 123 est récupéré. Au moment où la réponse est récupéré avec succès, le nom de l’utilisateur est modifié.

Une seconde fonction peut être passée (en troisième paramètre) pour gérer les erreurs.

Un exemple

Pour finir, un exemple de bout en bout, sans décoration, mais permettant de lister, modifier, supprimer et créer des utilisateurs :

La vue :

<ul>
  <li ng-repeat="user in users">
    <input type="text" ng-model="user.name" />
    <button ng-click="validate(user)">Valider</button>
    <button ng-click="delete(user)">Supprimer</button>
  </li>
</ul>
Nouvel utilisateur :
<input type="text" ng-model="newUserName" />
<button ng-click="create(newUserName)">Créer</button>

Le contrôleur :

angular.module('monApp')
  .controller('UsersCtrl', function ($scope, User) {
    $scope.users = User.query();
    $scope.validate(user) {
      user.$update();
    }
    $scope.delete(user) {
      user.$delete();
    }
    $scope.create(newUserName) {
      var user = new User();
      user.name = newUserName;
      user.$save();
      $scope.users.push(user);
    }
  });

Et bien sûr le service :

angular.module('monApp', ['ngResource'])
  .factory('User', ['$resource',
    function($resource){
      return $resource('/users/:userId', {userId:'@id'},
        {
          'update': {method: 'PUT'}
        }
      );
    }
  ]);

Vous voyez à quel point le code est simple ?

Le mot de la fin

AngularJS est simple à utiliser, et assez fun. Comme je l’ai dit, je débute, mais comme d’habitude sur ce blog, je vais condenser ce que j’apprends sur le sujet. En clair : ce n’est pas le dernier article sur AngularJS.

Cet article vous a été utile ? Partage it !

16 réflexions au sujet de « Comment utiliser les services $resource avec AngularJS »

  1. Hello,
    Merci pour ton tuto..
    Question bête ou en tout cas de novice.. comment gérer les retours éventuels d’erreurs ?
    Imaginons que le service Rest génère une erreur ? (je vais poursuivre la lecture de tes articles.. dès fois que tu en fasses allusion)..
    Y a t il moyen de « catcher » l’erreur ?
    Merci d’avance pour ton retour

    1. Bonjour Stan92,
      J’imagine que tu as trouvé mon article sur les promises, et que tu as la réponse à ta question.
      Sinon, sans les promesses, tu peux ajouter une deuxième callback qui gère l’action en cas d’erreur.
      Dans les deux cas, la callback prend un seul paramètre qui contient le propriété data, qui est le corps de la réponse, et d’autres informations à propos de la réponse HTTP (comme le status code).
      En clair:
      User.get(
      {userId:123},
      function() {
      // Succès
      },
      function(response) {
      // Echec
      console.log(response.data); // le résultat, certainement le code d’erreur
      }
      );
      David

  2. J’ai testé le framework JavaScript AngularJS de Google et j’ai un peu galéré. Heureusement j’ai pu m’appuyer sur votre tutoriel. J’ai eu du mal à gérer les retours d’erreurs et à comprendre spontanément d’où cela provenait. Bon après il faut dire que je n’ai qu’un an d’expérience en JS donc ceci explique cela.
    Nicolas

  3. Bonjour , je vous fais part d’un bug que je rencontre depuis 2 jours.En effet , je développe une application en utilisant yeoman. Et je fais une requête ajax et j’ai comme erreur au niveau de mon navigateur Firefox.

    Blocage d’une requête multi-origines (Cross-Origin Request) : la politique « Same Origin » ne permet pas de consulter la ressource distante située sur http://2.2.4.118:8080/personne-rest-identit/api/identit/224. Raison : l’en-tête CORS « Access-Control-Allow-Origin » est manquant.

    le code de mon service

    angular.module(‘apptestApp’)
    .factory(‘serviceAjax’, function serviceAjax($http) {
    return{
    personne: function(id){
    // return $http.jsonp(‘http://2.2.4.118:8080/personne-rest-ident/api/ident/’+ id);
    return $http.get(‘http://2.2.4.118:8080/personne-rest-ident/api/ident/’+ id);
    },
    }
    });

    au niveau de mon controleur

    serviceAjax.personne($scope.personId).success(function(data){

    $scope.PersonneDemo =data.prenoms;
    console.log($scope.PersonneDemo);

    dans mon app.js

    .config(function($stateProvider, $urlRouterProvider ,$httpProvider) {

    $httpProvider.defaults.useXDomain = true;

    delete $httpProvider.defaults.headers.common[‘X-Requested-With’];

    Merci de votre aide bien cordialement

    1. Bonjour arno,

      Personnellement, je préfère contrôler le cross domain côté serveur REST, en ajoutant par exemple ces en-têtes HTTP à la réponse :
      Access-Control-Allow-Origin: *
      Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, DELETE
      Access-Control-Allow-Headers: Content-Type

      David

      1. merci matre yoda,

        mais n’ayant pas accès au serveur , y a t il un moyen de résoudre ce probleme? coté client? merci?

        1. 😉
          Je n’ai jamais tenté, mais je pencherais plutôt pour $http, comme c’est ce qui est utilisé par $ngResource :
          $http.defaults.useXDomain = true;
          delete $http.defaults.headers.common[‘X-Requested-With’];

          David

          1. Merci pour la réponse maitre yoda, mais je l’avais fait dans mon code

            .config(function($stateProvider, $urlRouterProvider ,$httpProvider) {

            $httpProvider.defaults.useXDomain = true;

            delete $httpProvider.defaults.headers.common[‘X-Requested-With’];

            je continue par chercher

  4. Bonjour,

    Je déterre un peu ce sujet. J’ai essayé de mettre en application ce que vous décrivez dans votre article (très clair au demeurant) à ma problématique (création et maintien d’une liste d’activités au sein d’un atelier).

    Je me retrouve avec une erreur de syntaxe dans le contrôleur au niveau de :
    $scope.validate(activite) { => Unexpected token {

    Je ne suis pas très aguerri en javascript, donc je n’ai pas trop idée d’où peut venir l’erreur. Avez-vous une idée ?

    Merci.
    Nicolas

    1. Bonjour,
      J’espère que vous avez trouvé depuis 🙂
      J’imagine que vous vouliez écrire :
      $scope.validate = function(activite) {
      // …
      };
      David

  5. bonjour
    je ne sais pas si mon commentaire va etre vu.
    deja merci beaucoup sur cette article il m a beaucoup aidé pour mon site.
    mais j aurai une question, dans l exemple que tu as mis. dans la vue pas besoin de faire appel au controller en faisant ng-controller = user ?

    merci 🙂

    1. Bonjour,
      Une solution est d’activer le cross domain côté serveur avec les attributs de header HTTP Access-Control-Allow-*.
      David

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Si vous le souhaitez, renseignez le champ 'Nom' de cette façon : 'VotreNom@VotreMotClef' pour obtenir une ancre optimisée pour les moteurs de recherche.