javascript - w3schools - Processando resposta $ http no serviço




w3schools try out (8)

Recentemente, publiquei uma descrição detalhada do problema que estou enfrentando here no SO. Como não consegui enviar uma solicitação $http real, usei o tempo limite para simular o comportamento assíncrono. 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 here

https://code.i-harness.com


Ao vincular a interface do usuário ao seu array, você precisará 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 isso:

 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ê!


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

A ideia é que você trabalhe com promessas 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ê só 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;
    });
  };
});

Eu li http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/ [AngularJS nos permite agilizar nossa lógica de controle colocando uma promessa diretamente no escopo, ao invés de entregar manualmente a solução resolvida. 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 tem que "saber" sobre 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á posso identificar são

  • Você tem que passar no objeto 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 objeto (embora também possa aceitar uma matriz), o que não será um problema para muitos aplicativos, mas é uma limitação dolorosa
  • Você tem que preparar o objeto de entrada $scope.data com = {} para torná-lo um objeto (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 nos casos em que você deseja usar determinados dados obtidos de $ http em mais de um local enquanto o mantém DRY.


Por ser assíncrono, 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();
  });
});

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

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 .


tosh shimayama tem uma solução, mas você pode simplificar muito se usar o fato de que $ http retorna promessas e que promessas 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 meu método: http://plnkr.co/edit/mwSZGK?p=preview





angular-http