AngularJS: 非同期データでサービスを初期化する 質問する

AngularJS: 非同期データでサービスを初期化する 質問する

非同期データで初期化したい AngularJS サービスがあります。次のようになります。

myModule.service('MyService', function($http) {
    var myData = null;

    $http.get('data.json').success(function (data) {
        myData = data;
    });

    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

明らかにこれは機能しません。なぜなら、何かが戻るdoStuff()前に呼び出しを行おうとするとmyData、nullポインタ例外が発生するからです。私が他の質問のいくつかを読んだ限りでは、ここそしてここいくつかのオプションがありますが、どれもあまりきれいではないようです (おそらく何か見落としているのでしょう)。

「run」でサービスをセットアップする

アプリを設定するときは、次のようにします。

myApp.run(function ($http, MyService) {
    $http.get('data.json').success(function (data) {
        MyService.setData(data);
    });
});

私のサービスは次のようになります。

myModule.service('MyService', function() {
    var myData = null;
    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

これは時々機能しますが、非同期データの初期化に時間がかかる場合、呼び出し時にヌルポインタ例外が発生します。doStuff()

プロミスオブジェクトを使用する

これはおそらくうまくいくでしょう。唯一の欠点は、MyService を呼び出すたびに、doStuff() が Promise を返すことを知っておく必要があり、すべてのコードでthenPromise と対話する必要があることです。myData が返されるまで待ってからアプリケーションをロードする方がよいでしょう。

手動ブートストラップ

angular.element(document).ready(function() {
    $.getJSON("data.json", function (data) {
       // can't initialize the data here because the service doesn't exist yet
       angular.bootstrap(document);
       // too late to initialize here because something may have already
       // tried to call doStuff() and would have got a null pointer exception
    });
});

グローバル Javascript 変数JSON をグローバル Javascript 変数に直接送信できます。

HTML:

<script type="text/javascript" src="data.js"></script>

データ.js:

var dataForMyService = { 
// myData here
};

すると、初期化時に利用できるようになりますMyService:

myModule.service('MyService', function() {
    var myData = dataForMyService;
    return {
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

これも機能しますが、グローバル JavaScript 変数が存在するため、問題が生じます。

これらが唯一の選択肢でしょうか? これらの選択肢のうち、どれが他の選択肢よりも優れているでしょうか? かなり長い質問であることは承知していますが、私はすべての選択肢を検討しようとしたということを示したかったのです。ご指導いただければ幸いです。

ベストアンサー1

ご覧になりましたか?$routeProvider.when('/path',{ resolve:{...}? これにより、Promise アプローチが少しわかりやすくなります。

サービスで約束を公開します:

app.service('MyService', function($http) {
    var myData = null;

    var promise = $http.get('data.json').success(function (data) {
      myData = data;
    });

    return {
      promise:promise,
      setData: function (data) {
          myData = data;
      },
      doStuff: function () {
          return myData;//.getSomeData();
      }
    };
});

resolveルート設定に追加:

app.config(function($routeProvider){
  $routeProvider
    .when('/',{controller:'MainCtrl',
    template:'<div>From MyService:<pre>{{data | json}}</pre></div>',
    resolve:{
      'MyServiceData':function(MyService){
        // MyServiceData will also be injectable in your controller, if you don't want this you could create a new promise with the $q service
        return MyService.promise;
      }
    }})
  }):

すべての依存関係が解決されるまで、コントローラーはインスタンス化されません。

app.controller('MainCtrl', function($scope,MyService) {
  console.log('Promise is now resolved: '+MyService.doStuff().data)
  $scope.data = MyService.doStuff();
});

plnkr に例を作成しました:http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview

おすすめ記事