1. ホーム
  2. javascript

[解決済み] AngularJS 。非同期データでサービスを初期化する

2022-03-20 02:36:32

質問

私はいくつかの非同期データで初期化したい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 が戻ってくると、ヌルポインター例外が発生します。他の質問を読む限りでは こちら こちら いくつかのオプションがありますが、どれもあまりきれいとは思えません(もしかしたら、私が何かを見逃しているのかもしれません)。

Run"でServiceをセットアップする。

私のアプリをセットアップするときは、こうしてください。

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()がプロミスを返すことを知る必要があり、すべてのコードが以下のようになることです。 then を使用して、プロミスと対話します。むしろ、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>

data.jsです。

var dataForMyService = { 
// myData here
};

を初期化する際に利用できるようになります。 MyService :

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

これもうまくいくのですが、グローバルなjavascript変数を持っていることになり、悪い匂いがします。

これしかないのでしょうか?どれか一つでも他の選択肢より優れているのでしょうか?これはかなり長い質問だと思いますが、私がすべての選択肢を探そうとしたことを示したかったのです。何かご指導いただければ幸いです。

どのように解決するのですか?

をご覧になりましたか? $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