486 Stimmen

AngularJS: Initialisiere den Service mit asynchronen Daten.

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, weil wenn etwas versucht, doStuff() aufzurufen, bevor myData zurückkommt, werde ich eine Nullzeiger-Ausnahme erhalten. So weit ich aus dem Lesen einiger der anderen gestellten Fragen hier und hier erkennen kann, habe ich ein paar Optionen, aber keine davon scheint sehr sauber zu sein (vielleicht übersehe ich etwas):

Service mit "run" einrichten

Wenn ich meine App einrichte, mache dies:

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

Dann würde mein Dienst 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 eine Nullzeiger-Ausnahme, wenn ich doStuff() aufrufe

Versprechenobjekte verwenden

Dies würde wahrscheinlich funktionieren. Der einzige Nachteil ist, dass überall, wo ich MyService aufrufe, wissen muss, dass doStuff() ein Versprechen zurückgibt und der gesamte Code das verwenden muss then, um mit dem Versprechen zu interagieren. Ich würde lieber einfach warten, bis myData zurück ist, bevor ich meine Anwendung lade.

Manuelles Bootstrapping

angular.element(document).ready(function() {
    $.getJSON("data.json", function (data) {
       // kann die Daten hier nicht initialisieren, weil der Dienst noch nicht existiert
       angular.bootstrap(document);
       // hier zu spät zu initialisieren, weil möglicherweise schon etwas versucht hat, doStuff() aufzurufen und eine Nullzeiger-Ausnahme erhalten hätte
    });
});

Globaler Javascript-Var Ich könnte meine JSON direkt an eine globale Javascript-Variable senden:

HTML:

data.js:

var dataForMyService = { 
// myData hier
};

Dann wäre es verfügbar, wenn MyService initialisiert wird:

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

Dies würde auch funktionieren, aber dann hätte ich eine globale Javascript-Variable, was nicht gut ist.

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 zu erkunden. Jede Anleitung wäre sehr hilfreich.

8voto

dewd Punkte 4342

Was du tun kannst, ist in deiner .config für die App das resolve-Objekt für die Route zu erstellen und in der Funktion $q (Promise-Objekt) und den Namen des Dienstes, von dem du abhängig bist, zu übergeben, und das Versprechen im Rückruf für das $http im Dienst wie folgt aufzulösen:

ROUTE CONFIG

app.config(function($routeProvider){
    $routeProvider
     .when('/',{
          templateUrl: 'home.html',
          controller: 'homeCtrl',
          resolve:function($q,MyService) {
                //Lege die defer-Variable an und übergebe sie unserem Dienst
                var defer = $q.defer();
                MyService.fetchData(defer);
                //Dies wird nur zurückgegeben, wenn das Versprechen
                //eingelöst wurde. MyService wird das für uns tun
                return defer.promise;
          }
      })
}

Angular rendert das Template nicht oder stellt den Controller nicht zur Verfügung, bis defer.resolve() aufgerufen wurde. Das können wir in unserem Dienst tun:

SERVICE

app.service('MyService',function($http){
       var MyService = {};
       //Unser Dienst akzeptiert ein Promise-Objekt, das
       //es im Namen der aufrufenden Funktion lösen wird
       MyService.fetchData = function(q) {
             $http({method:'GET',url:'data.php'}).success(function(data){
                 MyService.data = data;
                 //Wenn dies aufgerufen wird, wird
                 //die aufrufende Funktion freigegeben. In diesem
                 //Fall ist es die Auflösefunktion in unserem
                 //Routen-Konfigurationsobjekt
                 q.resolve();
             }
       }

       return MyService;
});

Jetzt, da MyService die den Daten zugewiesene Daten hat und das Versprechen im Routen-Resolve-Objekt gelöst wurde, wird unser Controller für die Route aktiviert und wir können die Daten aus dem Dienst unserem Controller-Objekt zuweisen.

CONTROLLER

  app.controller('homeCtrl',function($scope,MyService){
       $scope.servicedata = MyService.data;
  });

Jetzt kann unser Binding im Scope des Controllers alle Daten nutzen, die von MyService stammen.

5voto

testing123 Punkte 11157

Also fand ich eine Lösung. Ich habe einen AngularJS-Service erstellt, den wir MyDataRepository nennen, und ein Modul dafür erstellt. Dann lade ich diese JavaScript-Datei über meinen Server-Controller hoch:

HTML:

Server-seitig:

@RequestMapping(value="path/myData.js", method=RequestMethod.GET)
public ResponseEntity getMyDataRepositoryJS()
{
    // Daten, die ich benötige, in eine Map eintragen
    Map myData = new HashMap();
    ...
    // Jackson verwenden, um es in JSON zu konvertieren
    ObjectMapper mapper = new ObjectMapper();
    String myDataStr = mapper.writeValueAsString(myData);

    // Dann einen String erstellen, der meine JavaScript-Datei ist
    String myJS = "'use strict';" +
    "(function() {" +
    "var myDataModule = angular.module('myApp.myData', []);" +
    "myDataModule.service('MyDataRepository', function() {" +
        "var myData = "+myDataStr+";" +
        "return {" +
            "getData: function () {" +
                "return myData;" +
            "}" +
        "}" +
    "});" +
    "})();"

    // Jetzt sende es an den Client:
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.add("Content-Type", "text/javascript");
    return new ResponseEntity(myJS , responseHeaders, HttpStatus.OK);
}

Dann kann ich MyDataRepository überall einfügen, wo ich es brauche:

someOtherModule.service('MyOtherService', function(MyDataRepository) {
    var myData = MyDataRepository.getData();
    // Tun, was du tun musst...
}

Dies hat für mich gut funktioniert, aber ich bin für jedes Feedback offen, falls jemand etwas hat.

2voto

Slava Fomin II Punkte 23725

Außerdem können Sie die folgenden Techniken verwenden, um Ihren Service global bereitzustellen, bevor die tatsächlichen Controller ausgeführt werden: https://stackoverflow.com/a/27050497/1056679. Lösen Sie einfach Ihre Daten global auf und übergeben Sie diese beispielsweise Ihrem Service im run Block.

1voto

hegemon Punkte 6256

Sie können JSONP verwenden, um Service-Daten asynchron zu laden. Die JSONP-Anforderung wird während des initialen Seitenladens gestellt und die Ergebnisse werden verfügbar sein, bevor Ihre Anwendung startet. Auf diese Weise müssen Sie Ihre Routing nicht mit redundanten Auflösungen belasten.

Ihr HTML würde so aussehen:

function MyService {
  this.getData = function(){
    return MyService.data;
  }
}
MyService.setData = function(data) {
  MyService.data = data;
}

angular.module('main')
.service('MyService', MyService)

-1voto

Ronak Amlani Punkte 654

Einfachster Weg, um jeden Initialisierungspunkt im ng-init-Verzeichnis abzurufen.

Platzieren Sie einfach den ng-init div scope dort, wo Sie die Initialisierungsdaten abrufen möchten

index.html

        Land

index.js

$scope.init=function(){
    $http({method:'GET',url:'/countries/countries.json'}).success(function(data){
      alert();
           $scope.countries = data;
    });
  };

HINWEIS: Sie können diese Methodik verwenden, wenn Sie denselben Code nicht an mehreren Stellen haben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X