在服务中处理$ http响应

时间:2012-09-20 03:36:10

标签: javascript angularjs angular-http

我最近在SO上发布了我所面临的here问题的详细说明。由于我无法发送实际的$http请求,因此我使用超时来模拟异步行为。在@Gloopy

的帮助下,从我的模型到视图的数据绑定工作正常

现在,当我使用$http而不是$timeout(在本地测试)时,我可以看到异步请求成功,data在我的服务中填充了json响应。但是,我的观点并未更新。

更新了Plunkr here

12 个答案:

答案 0 :(得分:417)

这是一个可以满足您需求的Plunk:http://plnkr.co/edit/TTlbSv?p=preview

这个想法是你直接使用promises和它们的“then”函数来操作和访问异步返回的响应。

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

这是一个稍微复杂的版本,用于缓存请求,因此您只能第一次(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;
    });
  };
});

答案 1 :(得分:82)

让它变得简单。它就像

一样简单
  1. 在您的服务中退回promise(无需在服务中使用then
  2. 在控制器中使用then
  3. 演示。 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;
      });
    });
    

答案 2 :(得分:58)

因为它是异步的,所以$scope在ajax调用完成之前获取数据。

您可以在服务中使用$q创建promise并将其还原 控制器和控制器在then()的{​​{1}}调用中获得结果。

在您的服务中,

promise

然后,在你的控制器中:

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

答案 3 :(得分:23)

shoshyama有一个解决方案,但是如果你使用$ http返回promises并且promises可以返回一个值的事实你可以简化很多:

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

});

coffeescript中的一个小演示:http://plunker.no.de/edit/ksnErx?live=preview

您的插件使用我的方法进行了更新:http://plnkr.co/edit/mwSZGK?p=preview

答案 4 :(得分:7)

我认为更好的方法是这样的:

服务:

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

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

        ...

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

        ...

        return deferred.promise;
    }

    return{
        getAllFruits:getAllFruits
    }

});

在控制器中你可以简单地使用:

$scope.fruits = FruitsManager.getAllFruits();

Angular会自动将已解决的awesomeFruits放入$scope.fruits

答案 5 :(得分:6)

我遇到了同样的问题,但是当我在网上冲浪时,我明白$ http默认返回一个承诺,然后我可以用#34;然后"返回"数据"。看看代码:

 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 :(得分:4)

将UI绑定到数组时,您需要确保通过将长度设置为0并将数据推送到数组中来直接更新相同的数组。

而不是这个(设置一个不同的数组引用到你的UI不知道的data):

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

试试这个:

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

Here is a fiddle显示设置新阵列与清空和添加到现有阵列之间的区别。我无法让你的plnkr工作,但希望这适合你!

答案 7 :(得分:4)

与此相关的我遇到了类似的问题,但不是由Angular提出或发布,而是由第三方制作的扩展(在我的案例中为Chrome扩展)。
我遇到的问题是Chrome扩展程序不会返回then(),所以我无法按照上述解决方案的方式执行操作,但结果仍然是异步。
所以我的解决方案是创建服务并继续回调

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

然后在我的控制器中

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

希望这可以帮助其他人解决同样的问题。

答案 8 :(得分:4)

我看过http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/ [AngularJS允许我们通过直接在作用域上放置一个promise来简化我们的控制器逻辑,而不是在成功回调中手动处理已解析的值。]

如此简单而方便:)

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

希望这个帮助

答案 9 :(得分:2)

我真的不喜欢这样一个事实,因为&#34;承诺&#34;服务方式,使用$ http的服务的消费者必须&#34;知道&#34;关于如何解压缩响应。

我只是想调用一些内容并获取数据,类似于旧的$scope.items = Data.getData();方式,即now deprecated

我尝试了一段时间并没有想出一个完美的解决方案,但这是我最好的镜头(Plunker)。它可能对某人有用。

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

然后控制器:

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

我已经发现的缺陷是

  • 您必须传入您想要将数据添加到的对象,这不是Angular中的直观或常见模式
  • getData只能接受对象形式的obj参数(尽管它也可以接受数组),这对许多应用程序来说都不是问题,但它&# 39;痛苦的限制
  • 您必须使用$scope.data准备输入对象= {}以使其成为对象(基本上是上面$scope.clearData()所做的),或者为数组准备= [],或者它不会工作(我们已经不得不假设有关数据的内容)。我试着在getData中做这个准备步骤,但没有运气。

然而,它提供了一种模式,可以删除控制器&#34;承诺解包&#34;样板文件,如果你想在多个地方使用从$ http获得的某些数据,同时保持DRY,这可能很有用。

答案 10 :(得分:1)

就缓存服务中的响应而言,这是另一个看起来比我到目前为止看起来更直接的版本:

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

    })();

});

此服务将返回缓存数据或$http.get;

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

答案 11 :(得分:0)

请尝试以下代码

  

您可以拆分控制器(PageCtrl)和服务(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;
             });
         }
    }
}());