Przetwarzanie odpowiedzi $ http w serwisie

Ostatnio zamieściłem szczegółowy opis problemu, z którym się borykam tutaj W SO. Ponieważ nie mogłem wysłać rzeczywistego żądania $http, użyłem timeoutu do symulacji asynchronicznego zachowania. Powiązanie danych z mojego modelu do widoku działa poprawnie, z pomocą @ Gloopy

Teraz, Kiedy używam $http zamiast $timeout (testowane lokalnie), widziałem, że żądanie asynchroniczne zakończyło się sukcesem i data jest wypełnione odpowiedzią json w moim serwisie. Ale mój pogląd nie jest aktualizowany.

Zaktualizowano Plunkr tutaj

Author: Community, 2012-09-20

12 answers

Oto Plunk, który robi to, co chcesz: http://plnkr.co/edit/TTlbSv?p=preview

Chodzi o to, że pracujesz bezpośrednio z obietnicami i ich funkcjami "wtedy", aby manipulować i uzyskiwać dostęp do asynchronicznie zwracanych odpowiedzi.

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

Tutaj jest nieco bardziej skomplikowana wersja, która buforuje żądanie, więc robisz to tylko za pierwszym razem ( 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;
    });
  };
});
 416
Author: Pete BD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-06 21:21:59

Niech to będzie proste. To tak proste jak

  1. Return promise in your service (no need to use then in service)
  2. Użyj then w kontrolerze

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;
  });
});
 81
Author: allenhwkim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-04 19:09:58

Ponieważ jest asynchroniczny, $scope pobiera dane przed zakończeniem wywołania ajax.

Możesz użyć $q w swoim serwisie, aby utworzyć promise i oddać go do kontroler i kontroler uzyskują wynik w wywołaniu then() przeciwko promise.

W Twojej służbie,

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

Następnie w kontrolerze:

app.controller('MainCtrl', function( myService,$scope) {
  myService.async().then(function() {
    $scope.data = myService.data();
  });
});
 58
Author: Tosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-20 05:35:40

Tosh shimayama ma rozwiązanie, ale możesz znacznie uprościć, jeśli użyjesz faktu, że $http zwraca obietnice i że obietnice mogą zwrócić wartość:

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

});
Mała demonstracja w coffeescript: http://plunker.no.de/edit/ksnErx?live=preview

Your plunker updated with my method: http://plnkr.co/edit/mwSZGK?p=preview

 23
Author: Guillaume86,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-20 12:02:10

O wiele lepszy sposób myślę, że byłoby coś takiego:

Usługa:

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

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

        ...

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

        ...

        return deferred.promise;
    }

    return{
        getAllFruits:getAllFruits
    }

});

A w kontrolerze można po prostu użyć:

$scope.fruits = FruitsManager.getAllFruits();

Angular automatycznie umieści awesomeFruits w $scope.fruits.

 7
Author: HasanAboShally,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-03 15:11:51

Miałem ten sam problem, ale gdy surfowałem po Internecie zrozumiałem, że $http zwraca domyślnie obietnicę, wtedy mogę użyć go z "then" po zwróceniu "danych". spójrz na kod:

 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
Author: JhonQO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-19 23:20:54

Podczas wiązania interfejsu użytkownika z tablicą będziesz chciał się upewnić, że zaktualizujesz tę samą tablicę bezpośrednio, ustawiając długość na 0 i wpychając dane do tablicy.

Zamiast tego (które ustawia inne odniesienie do tablicy na data, o którym twój interfejs nie będzie wiedział):

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

Spróbuj tego:

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

Oto fiddle , który pokazuje różnicę między ustawieniem nowej tablicy a opróżnieniem i dodaniem do istniejącej. Nie udało mi się uruchomić twojego plnkr, ale mam nadzieję, że to działa dla Ciebie!

 4
Author: Gloopy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-20 05:00:39

W związku z tym miałem podobny problem, ale nie z get lub post wykonane przez Angular, ale z rozszerzeniem wykonane przez 3rd party (w moim przypadku rozszerzenie Chrome).
Problem, z którym się spotkałem, polega na tym, że rozszerzenie Chrome nie zwróci then(), więc nie byłem w stanie zrobić tego tak, jak w powyższym rozwiązaniu, ale wynik jest nadal asynchroniczny.
Więc moim rozwiązaniem jest stworzenie usługi i przejść do callback

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

Potem w moim kontrolerze

app.controller("MyCtrl", function ($scope, cookieInfoService) {
    cookieInfoService.getInfo(function (info) {
        console.log(info);
    });
});
Mam nadzieję, że to pomoże innym mam ten sam problem.
 4
Author: Shadowbob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-23 11:52:46

Przeczytałem http://markdalgleish.com/2013/06/using-promises-in-angularjs-views / [AngularJS pozwala nam usprawnić naszą logikę kontrolera, umieszczając obietnicę bezpośrednio na zakresie, a nie ręcznie przekazując rozwiązaną wartość w odpowiedzi zwrotnej sukcesu.]

Takie proste i poręczne:)

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

Hope this help

 4
Author: Whisher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-13 16:22:20

Naprawdę nie podoba mi się to, że ze względu na "obiecujący" sposób robienia rzeczy, konsument usługi używającej $http musi "wiedzieć" jak rozpakować odpowiedź.

Chcę po prostu zadzwonić i wyciągnąć dane, podobne do starego $scope.items = Data.getData(); sposobu, który jest teraz przestarzały .

Próbowałem przez jakiś czas i nie wymyśliłem idealnego rozwiązania, ale oto mój najlepszy strzał (Plunker ). Może się komuś przydać.

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

Then kontroler:

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

Wady, które już dostrzegam to

  • musisz przekazać obiekt, w którym chcesz dane dodane do, co nie jest intuicyjnym lub powszechnym wzorcem w Angular
  • getData może akceptować tylko parametr obj w postaci obiektu (chociaż może również akceptować tablicę), co nie będzie problemem dla wielu aplikacji, ale jest bolesnym ograniczeniem
  • musisz przygotować obiekt wejściowy $scope.data z = {} aby uczynić go obiektem (zasadniczo to, co $scope.clearData() robi powyżej), lub = [] dla tablicy, albo nie będzie działać(już musimy założyć coś o tym, jakie dane nadchodzą). Próbowałem zrobić ten etap przygotowania getData, ale bez powodzenia.

Niemniej jednak dostarcza wzorzec, który usuwa płytkę kotła kontrolera "promise unwrap" i może być przydatny w przypadkach, gdy chcemy użyć pewnych danych uzyskanych z $http w więcej niż jednym miejscu, utrzymując je w stanie suchym.

 2
Author: poshest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 17:43:16

Jeśli chodzi o buforowanie odpowiedzi w serwisie, oto inna wersja, która wydaje się bardziej prosta niż to, co widziałem do tej pory:

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

    })();

});

Ta usługa zwróci dane z pamięci podręcznej lub $http.get;

 dataStorage.then(function(data) {
     $scope.data = data;
 },function(e){
    console.log('err: ' + e);
 });
 1
Author: maioman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-14 09:53:42

Proszę wypróbować poniższy kod

Możesz podzielić kontroler (PageCtrl) i usługę (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
Author: Ratheesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-23 01:37:41