Processando resposta $ http no serviço

Recentemente, publiquei uma descrição detalhada do problema que estou enfrentando aqui no SO. Como não consegui enviar uma solicitação $http real, usei o tempo limite para simular o comportamento asynchronous. A vinculação de dados do meu modelo para exibição está funcionando corretamente, com a ajuda do @Gloopy

Agora, quando eu uso $http vez de $timeout (testado localmente), pude ver que a solicitação assíncrona foi bem-sucedida e os data preenchidos com a resposta json no meu serviço. Mas, minha opinião não está atualizando.

Plunkr atualizado aqui

Aqui está um Plunk que faz o que você quer: http://plnkr.co/edit/TTlbSv?p=preview

A ideia é que você trabalhe com promises diretamente e seu “então” funcione para manipular e acessar as respostas retornadas de forma assíncrona.

 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; }); }); 

Aqui está uma versão um pouco mais complicada que armazena em cache a solicitação para que você apenas a use pela primeira vez ( 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; }); }; }); 

Deixe ser simples. É tão simples quanto

  1. Devolva a promise em seu serviço (não é necessário usar em serviço)
  2. Use then no seu controlador

Demo 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; }); }); 

Por ser asynchronous, o $scope está obtendo os dados antes que a chamada do ajax seja concluída.

Você poderia usar $q em seu serviço para criar uma promise e devolvê-la ao controlador, e o controlador obteria o resultado dentro de then() call against promise .

Em seu serviço,

 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; }); 

Então, no seu controlador:

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

tosh shimayama tem uma solução, mas você pode simplificar muito se usar o fato de que $ http retorna promises e que promises podem retornar um valor:

 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 } }); }); 

Uma pequena demonstração em coffeescript: http://plunker.no.de/edit/ksnErx?live=preview

Seu plunker atualizado com o meu método: http://plnkr.co/edit/mwSZGK?p=preview

Uma maneira muito melhor eu acho que seria algo assim:

Serviço:

 app.service('FruitsManager',function($q){ function getAllFruits(){ var deferred = $q.defer(); ... // somewhere here use: deferred.resolve(awesomeFruits); ... return deferred.promise; } return{ getAllFruits:getAllFruits } }); 

E no controlador você pode simplesmente usar:

 $scope.fruits = FruitsManager.getAllFruits(); 

Angular colocará automaticamente os awesomeFruits resolvidos no $scope.fruits .

Eu tive o mesmo problema, mas quando eu estava navegando na internet eu entendi que $ http voltar por padrão uma promise, então eu poderia usá-lo com “então” depois de retornar os “dados”. olhe o código:

  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; }); }); 

Ao vincular a interface do usuário ao seu array, você deve certificar-se de atualizar o mesmo array diretamente definindo o tamanho como 0 e enviando os dados para o array.

Em vez disso (que define uma referência de matriz diferente aos data que sua interface do usuário não conhece):

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

tente isto:

  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]); } }); }; 

Aqui está um violino que mostra a diferença entre definir uma nova matriz vs esvaziar e adicionar a uma já existente. Eu não consegui fazer seu plnkr funcionar, mas espero que isso funcione para você!

Relacionado a isso eu passei por um problema semelhante, mas não com get ou post feito por Angular, mas com uma extensão feita por um terceiro (no meu caso Chrome Extension).
O problema que enfrentei é que a extensão do Google Chrome não retornará then() por isso, não consegui fazer o mesmo na solução acima, mas o resultado ainda é Assíncrono.
Então, minha solução é criar um serviço e continuar com um retorno de chamada

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

Então no meu controlador

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

Espero que isso ajude outras pessoas a terem o mesmo problema.

Eu li http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/ [AngularJS nos permite agilizar nossa lógica de controlador, colocando uma promise diretamente no escopo, ao invés de entregar manualmente o resolvido valor em um callback de sucesso.]

tão simples e prático 🙂

 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(); }); 

Espero que esta ajuda

Eu realmente não gosto do fato de que, por causa da maneira “promissora” de fazer as coisas, o consumidor do serviço que usa $ http deve “saber” como descompactar a resposta.

Eu só quero chamar algo e obter os dados, semelhante ao antigo $scope.items = Data.getData(); forma, que agora está obsoleta .

Eu tentei por um tempo e não encontrei uma solução perfeita, mas aqui está minha melhor foto ( Plunker ). Pode ser útil para alguém.

 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; }); 

Então controlador:

 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 }; }); 

Falhas que eu já consigo identificar são

  • Você tem que passar no object que você deseja que os dados sejam adicionados , o que não é um padrão intuitivo ou comum no Angular
  • getData só pode aceitar o parâmetro obj na forma de um object (embora também possa aceitar uma matriz), o que não será um problema para muitos aplicativos, mas é uma limitação ferida
  • Você tem que preparar o object de input $scope.data com = {} para torná-lo um object (essencialmente o que $scope.clearData() faz acima), ou = [] para um array, ou ele não funcionará (we ‘ já está tendo que assumir algo sobre quais dados estão chegando). Eu tentei fazer esta etapa de preparação em getData , mas sem sorte.

No entanto, ele fornece um padrão que remove o clichê “promova desdobrar” do controlador e pode ser útil em casos em que você deseja usar determinados dados obtidos de $ http em mais de um local enquanto o mantém DRY.

Quanto ao armazenamento em cache da resposta no serviço, aqui está outra versão que parece mais direta do que eu vi até agora:

 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; }); })(); }); 

este serviço retornará os dados em cache ou $http.get ;

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

Por favor, tente o código abaixo

Você pode dividir o controlador (PageCtrl) e serviço (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; }); } } }()); 
Intereting Posts