Ausgabe
Ich habe einen AngularJS-Dienst, den ich mit einigen asynchronen Daten initialisieren möchte. Etwas wie das:
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();
}
};
});
Offensichtlich wird dies nicht funktionieren, denn wenn etwas versucht anzurufen, doStuff()
bevor myData
es zurückkommt, erhalte ich eine Nullzeiger-Ausnahme. Soweit ich einige der anderen hier und hier gestellten Fragen lesen kann, habe ich ein paar Optionen, aber keine davon scheint sehr sauber zu sein (vielleicht fehlt mir etwas):
Einrichtungsdienst mit „run“
Gehen Sie beim Einrichten meiner App wie folgt vor:
myApp.run(function ($http, MyService) {
$http.get('data.json').success(function (data) {
MyService.setData(data);
});
});
Dann würde mein Service so aussehen:
myModule.service('MyService', function() {
var myData = null;
return {
setData: function (data) {
myData = data;
},
doStuff: function () {
return myData.getSomeData();
}
};
});
Dies funktioniert manchmal, aber wenn die asynchronen Daten länger dauern, als alles initialisiert wird, erhalte ich beim Aufruf eine Nullzeiger-AusnahmedoStuff()
Verwenden Sie Promise-Objekte
Dies würde wahrscheinlich funktionieren. Der einzige Nachteil ist, dass ich überall, wo ich MyService aufrufe, wissen muss, dass doStuff() ein Versprechen zurückgibt und der gesamte Code uns then
mit dem Versprechen interagieren muss. Ich würde lieber warten, bis myData zurück ist, bevor ich meine Anwendung lade.
Manueller Bootstrap
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
});
});
Globale
Javascript-Variable Ich könnte mein JSON direkt an eine globale Javascript-Variable senden:
HTML:
<script type="text/javascript" src="data.js"></script>
data.js:
var dataForMyService = {
// myData here
};
Dann wäre es beim Initialisieren verfügbar MyService
:
myModule.service('MyService', function() {
var myData = dataForMyService;
return {
doStuff: function () {
return myData.getSomeData();
}
};
});
Das würde auch funktionieren, aber dann habe ich eine globale Javascript-Variable, die schlecht riecht.
Sind das meine einzigen Optionen? Ist eine dieser Optionen besser als die anderen? Ich weiß, dass dies eine ziemlich lange Frage ist, aber ich wollte zeigen, dass ich versucht habe, alle meine Optionen auszuloten. Jede Anleitung wäre sehr willkommen.
Lösung
Hast du mal nachgeschaut $routeProvider.when('/path',{ resolve:{...}
? Es kann den Versprechungsansatz etwas sauberer machen:
Setzen Sie ein Versprechen in Ihrem Dienst aus:
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();
}
};
});
Fügen Sie resolve
zu Ihrer Routenkonfiguration hinzu:
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;
}
}})
}):
Ihr Controller wird nicht instanziiert, bevor alle Abhängigkeiten aufgelöst sind:
app.controller('MainCtrl', function($scope,MyService) {
console.log('Promise is now resolved: '+MyService.doStuff().data)
$scope.data = MyService.doStuff();
});
Ich habe ein Beispiel bei plnkr erstellt: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview
Beantwortet von – joakimbl
Antwort geprüft von – Pedro (FixError Volunteer)