web-dev-qa-db-ja.com

angularサービスから解決する約束をコントローラーに待機させる方法

バックエンドにAJAXリクエストを行っているサービスがあります

サービス:

    function GetCompaniesService(options)
    {
        this.url = '/company';
        this.Companies = undefined;
        this.CompaniesPromise = $http.get(this.url);

    }

コントローラ:

var CompaniesOb = new GetCompanies();
CompaniesOb.CompaniesPromise.then(function(data){
   $scope.Companies = data;
});

「.then」関数をコントローラーで処理するのではなく、サービスで処理したいのですが、サービス内のプロミスが解決された後、コントローラーがサービスからのデータに作用できるようにしたいと考えています。

基本的に、私はそのようなデータにアクセスできるようにしたいです:

var CompaniesOb = new GetCompanies();
$scope.Companies = CompaniesOb.Companies;

サービス自体の内部で取り扱われる約束の解決。

これは可能ですか?それとも、そのプロミスの解決策にアクセスできる唯一の方法は、サービスの外部からですか?

11
Axschech

サービス自体で$httpの応答を処理するだけの場合は、次のように、then関数をサービスに追加して、return関数からthenよりも多くの処理を行うことができます。

function GetCompaniesService(options) {
  this.url = '/company';
  this.Companies = undefined;
  this.CompaniesPromise = $http.get(this.url).then(function(response) {
    /* handle response then */
    return response
  })
}

ただし、コントローラーでプロミスを使用することはできますが、返されるものはすでにサービスで処理されています。

var CompaniesOb = new GetCompanies();
CompaniesOb.CompaniesPromise.then(function(dataAlreadyHandledInService) {
  $scope.Companies = dataAlreadyHandledInService;
});
4
M.K. Safi

それを達成するのに問題はありません!

あなたが心に留めておかなければならない主なことは、あなたはあなたのサービスで同じオブジェクト参照を保持しなければならないということです(そしてJavaScript配列ではオブジェクトです)。

シンプルなHTMLは次のとおりです。

<div ng-controller = "companiesCtrl"> 
  <ul ng-repeat="company in companies">
     <li>{{company}}</li>
  </ul>
</div>

これがサービスの実装です。

serviceDataCaching.service('companiesSrv', ['$timeout', function($timeout){
  var self = this;

  var httpResult = [
    'company 1',
    'company 2',
    'company 3'
  ];

  this.companies = ['preloaded company'];
  this.getCompanies = function() {
    // we simulate an async operation
    return $timeout(function(){
      // keep the array object reference!!
      self.companies.splice(0, self.companies.length);

      // if you use the following code:
      // self.companies = [];
      // the controller will loose the reference to the array object as we are creating an new one
      // as a result it will no longer get the changes made here!
      for(var i=0; i< httpResult.length; i++){
        self.companies.Push(httpResult[i]);
      }
      return self.companies;
    }, 3000);                    
}}]);   

そして最後に、あなたが望む通りのコントローラー:

serviceDataCaching.controller('companiesCtrl', function ($scope, companiesSrv) {
  $scope.companies = companiesSrv.companies;
  companiesSrv.getCompanies();
});

説明

上記のように、コツはサービスとコントローラー間の参照を維持することです。これを尊重したら、コントローラースコープをサービスのパブリックプロパティに完全にバインドできます。

ここでそれを包むフィドルです。

コードのコメントで、機能しない部分のコメントを外してみると、コントローラーが参照を失っていることがわかります。実際、コントローラーは古いアレイへの参照を保持し続けますが、サービスは新しいアレイを変更します。

最後にもう1つ重要なことは、$ timeoutがrootSCopeで$ apply()をトリガーしていることを覚えておいてください。これが、コントローラースコープが「単独」で更新される理由です。これがない場合、通常のsetTimeout()に置き換えようとすると、コントローラーが会社リストを更新していないことがわかります。これを回避するには、次の方法があります。

  • データが$ httpでフェッチされた場合、何もしないでください。成功すると$ applyが呼び出されます。
  • ラップすると$ timeout(...、0);になります。
  • $ rootSCopeをサービスに注入し、非同期操作が完了したときに$ apply()を呼び出します
  • コントローラーで、getCompanies()に$ scope。$ apply()を追加して成功を約束します

お役に立てれば!

3
Piou