web-dev-qa-db-fra.com

Traitement de la réponse $ http en service

J'ai récemment posté une description détaillée du problème auquel je suis confronté ici à l'adresse SO. Comme je ne pouvais pas envoyer de demande $http réelle, j'ai utilisé timeout pour simuler un comportement asynchrone. La liaison des données de mon modèle à la vue fonctionne correctement, à l’aide de @Gloopy.

Désormais, lorsque j’utilise $http au lieu de $timeout (testé localement), je pouvais constater que la demande asynchrone avait abouti et que data était rempli de réponses json dans mon service. Mais, mon point de vue n'est pas à jour.

mise à jour Plunkr ici

233
bsr

Voici un Plunk qui fait ce que vous voulez: http://plnkr.co/edit/TTlbSv?p=preview

L'idée est que vous travaillez directement avec les promesses et leurs fonctions "then" pour manipuler et accéder aux réponses renvoyées de manière asynchrone.

app.factory('myService', function($http) {
  var myService = {
    async: function() {
      // $http returns a promise, which has a then function, which also returns a promise
      var promise = $http.get('test.json').then(function (response) {
        // The then function here is an opportunity to modify the response
        console.log(response);
        // The return value gets picked up by the then in the controller.
        return response.data;
      });
      // Return the promise to the controller
      return promise;
    }
  };
  return myService;
});

app.controller('MainCtrl', function( myService,$scope) {
  // Call the async method and then do stuff with what is returned inside our own then function
  myService.async().then(function(d) {
    $scope.data = d;
  });
});

Voici une version un peu plus compliquée qui met la requête en cache afin que vous ne la fassiez que la première fois ( http://plnkr.co/edit/2yH1F4IMZlMS8QsV9rHv?p=preview ):

app.factory('myService', function($http) {
  var promise;
  var myService = {
    async: function() {
      if ( !promise ) {
        // $http returns a promise, which has a then function, which also returns a promise
        promise = $http.get('test.json').then(function (response) {
          // The then function here is an opportunity to modify the response
          console.log(response);
          // The return value gets picked up by the then in the controller.
          return response.data;
        });
      }
      // Return the promise to the controller
      return promise;
    }
  };
  return myService;
});

app.controller('MainCtrl', function( myService,$scope) {
  $scope.clearData = function() {
    $scope.data = {};
  };
  $scope.getData = function() {
    // Call the async method and then do stuff with what is returned inside our own then function
    myService.async().then(function(d) {
      $scope.data = d;
    });
  };
});
418
Pete BD

Que ce soit simple. C'est aussi simple que

  1. Retourne promise dans votre service (inutile d'utiliser then dans le service)
  2. Utilisez then dans votre contrôleur

Démo. http://plnkr.co/edit/cbdG5p?p=preview

var app = angular.module('plunker', []);

app.factory('myService', function($http) {
  return {
    async: function() {
      return $http.get('test.json');  //1. this returns promise
    }
  };
});

app.controller('MainCtrl', function( myService,$scope) {
  myService.async().then(function(d) { //2. so you can use .then()
    $scope.data = d;
  });
});
82
allenhwkim

Comme il est asynchrone, le $scope récupère les données avant la fin de l'appel ajax.

Vous pouvez utiliser $q dans votre service pour créer promise et le restituer au contrôleur, lequel obtient le résultat dans then() appel contre promise.

À votre service,

app.factory('myService', function($http, $q) {
  var deffered = $q.defer();
  var data = [];  
  var myService = {};

  myService.async = function() {
    $http.get('test.json')
    .success(function (d) {
      data = d;
      console.log(d);
      deffered.resolve();
    });
    return deffered.promise;
  };
  myService.data = function() { return data; };

  return myService;
});

Ensuite, dans votre contrôleur:

app.controller('MainCtrl', function( myService,$scope) {
  myService.async().then(function() {
    $scope.data = myService.data();
  });
});
58
Tosh

tosh shimayama a une solution mais vous pouvez simplifier beaucoup si vous utilisez le fait que $ http renvoie des promesses et que les promesses peuvent renvoyer une valeur:

app.factory('myService', function($http, $q) {
  myService.async = function() {
    return $http.get('test.json')
    .then(function (response) {
      var data = reponse.data;
      console.log(data);
      return data;
    });
  };

  return myService;
});

app.controller('MainCtrl', function( myService,$scope) {
  $scope.asyncData = myService.async();
  $scope.$watch('asyncData', function(asyncData) {
    if(angular.isDefined(asyncData)) {
      // Do something with the returned data, angular handle promises fine, you don't have to reassign the value to the scope if you just want to use it with angular directives
    }
  });

});

Une petite démonstration dans coffeescript: http://plunker.no.de/edit/ksnErx?live=preview

Votre plongeur mis à jour avec ma méthode: http://plnkr.co/edit/mwSZGK?p=preview

23
Guillaume86

Une bien meilleure façon, je pense, serait quelque chose comme ceci:

Un service:

app.service('FruitsManager',function($q){

    function getAllFruits(){
        var deferred = $q.defer();

        ...

        // somewhere here use: deferred.resolve(awesomeFruits);

        ...

        return deferred.promise;
    }

    return{
        getAllFruits:getAllFruits
    }

});

Et dans le contrôleur, vous pouvez simplement utiliser:

$scope.fruits = FruitsManager.getAllFruits();

Angular mettra automatiquement le awesomeFruits résolu dans le $scope.fruits.

7
HasanAboShally

J'ai eu le même problème, mais lorsque je surfais sur Internet, j'ai compris que $ http renvoyait par défaut une promesse, je pouvais ensuite l'utiliser avec "then" après avoir renvoyé les "données". regardez le code:

 app.service('myService', function($http) {
       this.getData = function(){
         var myResponseData = $http.get('test.json').then(function (response) {
            console.log(response);.
            return response.data;
          });
         return myResponseData;

       }
});    
 app.controller('MainCtrl', function( myService, $scope) {
      // Call the getData and set the response "data" in your scope.  
      myService.getData.then(function(myReponseData) {
        $scope.data = myReponseData;
      });
 });
6
JhonQO

J'ai lu http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/ [AngularJS nous permet de rationaliser la logique de notre contrôleur en plaçant une promesse directement sur l'étendue , plutôt que de gérer manuellement la valeur résolue dans un rappel de succès.]

si simplement et pratique :)

var app = angular.module('myApp', []);
            app.factory('Data', function($http,$q) {
                return {
                    getData : function(){
                        var deferred = $q.defer();
                        var promise = $http.get('./largeLoad').success(function (response) {
                            deferred.resolve(response);
                        });
                        // Return the promise to the controller
                        return deferred.promise; 
                    }
                }
            });
            app.controller('FetchCtrl',function($scope,Data){
                $scope.items = Data.getData();
            });

J'espère que cette aide

4
Whisher

Lorsque vous liez l'interface utilisateur à votre tableau, vous devez vous assurer de mettre à jour directement le même tableau en définissant la longueur sur 0 et en transférant les données dans le tableau.

Au lieu de cela (qui définit une référence de tableau différente sur data que votre interface utilisateur ne saura pas):

 myService.async = function() {
    $http.get('test.json')
    .success(function (d) {
      data = d;
    });
  };

essaye ça:

 myService.async = function() {
    $http.get('test.json')
    .success(function (d) {
      data.length = 0;
      for(var i = 0; i < d.length; i++){
        data.Push(d[i]);
      }
    });
  };

Voici un violon qui montre la différence entre définir un nouveau tableau et le vider et l’ajouter à un tableau existant. Je ne pouvais pas faire fonctionner votre plnkr mais j'espère que cela fonctionne pour vous!

4
Gloopy

En rapport avec cela, je suis passé par un problème similaire, mais pas avec get ou publication faite par Angular, mais avec une extension réalisée par un tiers (dans mon cas, Chrome Extension).
Le problème que j'ai rencontré est que l'extension Chrome ne renvoie pas then(). Je ne pouvais donc pas le faire comme dans la solution ci-dessus, mais le résultat est toujours asynchrone.
Ma solution est donc de créer un service et de procéder à un rappel

app.service('cookieInfoService', function() {
    this.getInfo = function(callback) {
        var model = {};
        chrome.cookies.get({url:serverUrl, name:'userId'}, function (response) {
            model.response= response;
            callback(model);
        });
    };
});

Puis dans mon contrôleur

app.controller("MyCtrl", function ($scope, cookieInfoService) {
    cookieInfoService.getInfo(function (info) {
        console.log(info);
    });
});

J'espère que cela peut aider les autres à résoudre le même problème.

4
Shadowbob

Je n'aime vraiment pas le fait que, en raison de la "promesse", le consommateur du service qui utilise $ http doit "savoir" sur la manière de décompresser la réponse.

Je veux juste appeler quelque chose et extraire les données, comme dans l'ancienne méthode $scope.items = Data.getData();, qui est maintenant obsolète .

J'ai essayé pendant un moment et je n'ai pas trouvé de solution parfaite, mais voici mon meilleur coup ( Plunker ). Cela peut être utile à quelqu'un.

app.factory('myService', function($http) {
  var _data;  // cache data rather than promise
  var myService = {};

  myService.getData = function(obj) { 
    if(!_data) {
      $http.get('test.json').then(function(result){
        _data = result.data;
        console.log(_data);  // prove that it executes once
        angular.extend(obj, _data);
      }); 
    } else {  
      angular.extend(obj, _data);
    }
  };

  return myService;
}); 

Puis contrôleur:

app.controller('MainCtrl', function( myService,$scope) {
  $scope.clearData = function() {
    $scope.data = Object.create(null);
  };
  $scope.getData = function() {
    $scope.clearData();  // also important: need to prepare input to getData as an object
    myService.getData($scope.data); // **important bit** pass in object you want to augment
  };
});

Les défauts que je peux déjà repérer sont

  • Vous devez transmettre l'objet auquel vous souhaitez que les données soient ajoutées (), ce qui n'est pas un modèle intuitif ou commun dans Angular.
  • getData ne peut accepter que le paramètre obj sous la forme d'un objet (bien qu'il puisse également accepter un tableau), ce qui ne posera pas problème pour de nombreuses applications, mais c'est une limitation sévère
  • Vous devez préparer l'objet d'entrée $scope.data avec = {} pour en faire un objet (essentiellement ce que $scope.clearData() fait ci-dessus), ou = [] pour un tableau, sinon ce ne sera pas le cas. travail (nous devons déjà supposer quelque chose à propos des données à venir). J'ai essayé de faire cette étape de préparation IN getData, mais pas de chance.

Néanmoins, il fournit un modèle qui supprime le contrôleur "promet non déroulé" du contrôleur, et peut être utile dans les cas où vous souhaitez utiliser certaines données obtenues à partir de $ http à plus d'un endroit tout en les maintenant sèches.

2
poshest

En ce qui concerne la mise en cache de la réponse dans le service, voici une autre version qui semble plus simple que ce que j'ai vu jusqu'à présent:

App.factory('dataStorage', function($http) {
     var dataStorage;//storage for cache

     return (function() {
         // if dataStorage exists returned cached version
        return dataStorage = dataStorage || $http({
      url: 'your.json',
      method: 'GET',
      cache: true
    }).then(function (response) {

              console.log('if storage don\'t exist : ' + response);

              return response;
            });

    })();

});

ce service renverra soit les données en cache, soit $http.get;

 dataStorage.then(function(data) {
     $scope.data = data;
 },function(e){
    console.log('err: ' + e);
 });
1
maioman

S'il vous plaît essayer le code ci-dessous

Vous pouvez diviser le contrôleur (PageCtrl) et le service (dataService)

'use strict';
(function () {
    angular.module('myApp')
        .controller('pageContl', ['$scope', 'dataService', PageContl])
        .service('dataService', ['$q', '$http', DataService]);
    function DataService($q, $http){
        this.$q = $q;
        this.$http = $http;
        //... blob blob 
    }
    DataService.prototype = {
        getSearchData: function () {
            var deferred = this.$q.defer(); //initiating promise
            this.$http({
                method: 'POST',//GET
                url: 'test.json',
                headers: { 'Content-Type': 'application/json' }
            }).then(function(result) {
                deferred.resolve(result.data);
            },function (error) {
                deferred.reject(error);
            });
            return deferred.promise;
        },
        getABCDATA: function () {

        }
    };
    function PageContl($scope, dataService) {
        this.$scope = $scope;
        this.dataService = dataService; //injecting service Dependency in ctrl
        this.pageData = {}; //or [];
    }
    PageContl.prototype = {
         searchData: function () {
             var self = this; //we can't access 'this' of parent fn from callback or inner function, that's why assigning in temp variable
             this.dataService.getSearchData().then(function (data) {
                 self.searchData = data;
             });
         }
    }
}());
0
Ratheesh