web-dev-qa-db-ja.com

サービスで$ http応答を処理しています

私は最近、私が直面している問題の詳細な説明を here でSOに投稿しました。実際の$http要求を送信できなかったので、非同期動作をシミュレートするためにタイムアウトを使用しました。私のモデルからビューへのデータバインディングは正しく機能しています。@Gloopyの助けを借りて

さて、$httpの代わりに$timeoutを使用すると(ローカルでテストされた)、非同期要求は成功し、dataは私のサービスではjson応答で埋められていることがわかります。しかし、私の見解は更新されていません。

更新されたPlunkr here

232
bsr

これはあなたがやりたいことをするPlunkです: http://plnkr.co/edit/TTlbSv?p=preview

これは、プロミスとその「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;
    });
  };
});
417
Pete BD

簡単にしましょう。それはとても簡単です

  1. あなたのサービスでpromiseを返してください(サービスでthenを使う必要はありません)
  2. コントローラでthenを使う

デモ。 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

非同期なので、$scopeはajax呼び出しが完了する前にデータを取得しています。

サービスで$qを使用してpromiseを作成してそれをコントローラに返すことができ、コントローラはpromiseに対してthen()呼び出し内で結果を取得します。

あなたのサービスでは、

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

次に、あなたのコントローラーで:

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

tosh shimayamaには解決策がありますが、$ httpが約束を返し、その約束が値を返すことができるという事実を使用すれば、非常に単純化できます。

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

});

コーヒースクリプトでのデモンストレーション: http://plunker.no.de/edit/ksnErx?live=preview

あなたのプランカーは私の方法で更新されました: http://plnkr.co/edit/mwSZGK?p=preview

23
Guillaume86

もっと良い方法は、次のようになると思います。

サービス:

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に入れます。

7
HasanAboShally

私は同じ問題を抱えていたが、私がインターネット上でサーフィンをしていたとき、私は$ httpがデフォルトで返品を約束することを理解し、そして "data"を返却した後に "then"でそれを使うことができる。コードを見てください:

 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

http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/ [AngularJSを使うと、スコープに直接約束を置くことで、コントローラロジックを合理化できます。解決された値を成功コールバックで手動で処理します。]

とてもシンプルで便利:)

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

この助けを願っています

4
Whisher

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

これはフィドルです は、新しい配列を設定することと空にすることと既存のものを追加することの違いを示しています。私はあなたのplnkrを動かすことができませんでしたが、うまくいけばこれはあなたのために働く!

4
Gloopy

これに関連して、私は同様の問題を経験しましたが、Angularによって作られたgetやpostではなく、サードパーティによって作られた拡張子(私の場合はChrome Extension)を使っていました。
私が直面した問題は、Chrome拡張機能がthen()を返さないことです。そのため、上記の解決方法ではできませんでしたが、結果はまだAsynchronousです。
だから私の解決策はサービスを作成してコールバックに進むことです

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

他の人が同じ問題を解決するのに役立つことを願っています。

4
Shadowbob

"約束"のやり方のために、$ httpを使うサービスの利用者が応答を解く方法について "知っていなければならない"という事実を私は本当に好きではありません。

私はただ何かを呼び出してデータを取り出したいだけです。これは古い$scope.items = Data.getData();の方法と似ています。これは 現在は非推奨です です。

私はしばらくの間試してみましたが、完璧な解決策を思いついたわけではありませんが、これが私のベストショットです( 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パラメータしか受け付けることができません(配列も受け付けることができますが)。これは多くのアプリケーションにとって問題にはなりませんが、それは厳しい制限です。
  • 入力オブジェクト$scope.data= {}でオブジェクトにする(基本的に$scope.clearData()が行うこと)、または= []を配列に作成する必要があります。 。私はこの準備ステップをgetDataでやろうとしましたが、運が悪いです。

それにもかかわらず、それはコントローラの "promise unwrap"ボイラープレートを削除するパターンを提供し、DRYを保ちながら$ httpから取得した特定のデータを複数の場所で使用したい場合に役立ちます。

2
poshest

サービスでの応答をキャッシュすることに関しては、これまで見てきたものよりももっと直接的に見える別のバージョンがあります。

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

以下のコードを試してください

あなたはコントローラ(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;
             });
         }
    }
}());
0
Ratheesh