AngularJS: Zainicjuj usługę za pomocą danych asynchronicznych

475

Mam usługę AngularJS, którą chcę zainicjować za pomocą niektórych danych asynchronicznych. Coś takiego:

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();
        }
    };
});

Oczywiście to nie zadziała, ponieważ jeśli coś spróbuje zadzwonić doStuff()przed myDatapowrotem, otrzymam wyjątek wskaźnika zerowego. O ile mogę stwierdzić po przeczytaniu niektórych innych pytań zadanych tutaj i tutaj mam kilka opcji, ale żadne z nich nie wydaje się bardzo czyste (być może czegoś mi brakuje):

Usługa instalacyjna z „uruchom”

Podczas konfigurowania mojej aplikacji wykonaj następujące czynności:

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

Wtedy moja usługa wyglądałaby tak:

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

Działa to czasami, ale jeśli dane asynchroniczne zdarzają się dłużej niż potrzeba, aby wszystko zostało zainicjowane, otrzymuję wyjątek wskaźnika zerowego podczas wywoływania doStuff()

Użyj obiektów obietnicy

To prawdopodobnie zadziałałoby. Jedynym minusem jest to, że wszędzie, gdzie nazywam MyService, będę musiał wiedzieć, że doStuff () zwraca obietnicę i cały kod będzie musiał do nas wejść thenw interakcję z obietnicą. Wolę poczekać, aż myData wróci przed załadowaniem mojej aplikacji.

Ręczny pasek startowy

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
    });
});

Global JavaScript Var Mógłbym wysłać mój JSON bezpośrednio do globalnej zmiennej JavaScript:

HTML:

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

data.js:

var dataForMyService = { 
// myData here
};

Wtedy byłby dostępny przy inicjalizacji MyService:

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

To też by działało, ale mam globalną zmienną javascript, która źle pachnie.

Czy to moje jedyne opcje? Czy jedna z tych opcji jest lepsza od innych? Wiem, że to dość długie pytanie, ale chciałem pokazać, że próbowałem zbadać wszystkie moje opcje. Wszelkie wskazówki będą mile widziane.

testowanie123
źródło
angular - bootstrap asynchronicznie przechodzi przez kod, aby pobrać dane z serwera $http, a następnie zapisać dane w usłudze, a następnie uruchomić aplikację.
Steven Wexler

Odpowiedzi:

327

Rzuciłeś okiem $routeProvider.when('/path',{ resolve:{...}? Może sprawić, że obietnica stanie się nieco czystsza:

Ujawnij obietnicę w swojej usłudze:

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();
      }
    };
});

Dodaj resolvedo konfiguracji trasy:

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;
      }
    }})
  }):

Twój kontroler nie zostanie utworzony, dopóki wszystkie zależności nie zostaną rozwiązane:

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

Zrobiłem przykład na plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview

joakimbl
źródło
1
Bardzo Ci dziękuję za odpowiedź! To by działało dla mnie, gdybym nie miał jeszcze usługi w mapie rozstrzygania, która korzysta z MyService. Zaktualizowałem twój plunker moją sytuacją: plnkr.co/edit/465Cupaf5mtxljCl5NuF?p=preview . Czy jest jakiś sposób, aby MyOtherService czekał na zainicjowanie MyService?
testowanie123
2
Chyba połączyłbym obietnice w MyOtherService - zaktualizowałem plunker o łańcuchy i kilka komentarzy - jak to wygląda? plnkr.co/edit/Z7dWVNA9P44Q72sLiPjW?p=preview
joakimbl 30.04.2013
3
Wypróbowałem to i nadal napotykałem pewne problemy, ponieważ mam dyrektywy i inne kontrolery (kontroler, którego używam z $ routeProvider obsługuje podstawowe, dodatkowe elementy nawigacyjne ... czyli „MyOtherService”), które muszą czekać, aż „MyService ' został rozwiązany. Będę próbować i aktualizować to z każdym moim sukcesem. Chciałbym tylko, żeby był kątowy hak, w którym mogłem poczekać na powrót danych przed zainicjowaniem moich kontrolerów i dyrektyw. Jeszcze raz dziękuję za pomoc. Gdybym miał główny kontroler, który wszystko zapakował, to by działało.
testowanie123
43
Pytanie tutaj - w jaki sposób przypiszesz resolvewłaściwość do kontrolera, który nie jest wymieniony w $routeProvider. Na przykład <div ng-controller="IndexCtrl"></div>. Tutaj kontroler jest wyraźnie wymieniony i nie jest ładowany przez routing. W takim przypadku, w jaki sposób opóźnić wystąpienie kontrolera?
callmekatootie
19
Ummm co jeśli nie korzystasz z routingu? To prawie tak, jakby powiedzieć, że nie możesz napisać aplikacji kątowej z danymi asynchronicznymi, chyba że używasz routingu. Zalecanym sposobem na pobranie danych do aplikacji jest ich asynchroniczne ładowanie, ale gdy masz więcej niż jeden kontroler i dodajesz usługi, BOOM jest niemożliwe.
wired_in
88

W oparciu o rozwiązanie Martina Atkinsa, oto kompletne, zwięzłe rozwiązanie czysto kątowe:

(function() {
  var initInjector = angular.injector(['ng']);
  var $http = initInjector.get('$http');
  $http.get('/config.json').then(
    function (response) {
      angular.module('config', []).constant('CONFIG', response.data);

      angular.element(document).ready(function() {
          angular.bootstrap(document, ['myApp']);
        });
    }
  );
})();

To rozwiązanie korzysta z samoczynnie wykonującej się funkcji anonimowej, aby uzyskać usługę $ http, zażądać konfiguracji i wstrzyknąć ją do stałej o nazwie CONFIG, gdy będzie dostępna.

Po zakończeniu czekamy, aż dokument będzie gotowy, a następnie uruchamiamy aplikację Angular.

Jest to niewielkie ulepszenie w stosunku do rozwiązania Martina, które odroczyło pobieranie konfiguracji do momentu, gdy dokument będzie gotowy. O ile mi wiadomo, nie ma powodu opóźniać połączenia $ http z tego powodu.

Testów jednostkowych

Uwaga: Odkryłem, że to rozwiązanie nie działa dobrze podczas testowania jednostkowego, gdy kod jest zawarty w twoim app.jspliku. Powodem tego jest to, że powyższy kod jest uruchamiany natychmiast po załadowaniu pliku JS. Oznacza to, że środowisko testowe (w moim przypadku Jasmine) nie ma szansy zapewnić fałszywej implementacji $http.

Moim rozwiązaniem, z którego nie jestem w pełni usatysfakcjonowany, było przeniesienie tego kodu do naszego index.htmlpliku, więc infrastruktura testów jednostkowych Grunt / Karma / Jasmine tego nie widzi.

JBCP
źródło
1
Należy przestrzegać następujących zasad: „nie zanieczyszczaj globalnego zasięgu” tylko w takim stopniu, w jakim poprawiają one nasz kod (mniej złożony, łatwiejszy w utrzymaniu, bezpieczniejszy itp.). Nie rozumiem, jak to rozwiązanie jest lepsze niż zwykłe ładowanie danych do pojedynczej zmiennej globalnej. czego mi brakuje?
david004
4
Pozwala na użycie systemu wstrzykiwania zależności Angulara w celu uzyskania dostępu do stałej „CONFIG” w modułach, które jej potrzebują, ale nie ryzykujesz zablokowania innych modułów, które tego nie robią. Na przykład, jeśli użyłeś globalnej zmiennej „config”, istnieje szansa, że ​​kod innej firmy może również szukać tej samej zmiennej.
JBCP
1
Jestem kanciastym nowicjuszem, oto kilka uwag na temat rozwiązania zależności konfiguracyjnej w mojej aplikacji: gist.github.com/dsulli99/0be3e80db9b21ce7b989 ref: tutorials.jenkov.com/angularjs/... Dziękuję za to rozwiązanie.
dps
7
Zostało to wspomniane w komentarzu w jednym z innych ręcznych rozwiązań bootstrap poniżej, ale jako kanciasty początkujący, który tego nie zauważył, mogę po prostu wskazać, że musisz usunąć dyrektywę ng-app w kodzie HTML, aby to działało poprawnie - zastępuje automatyczny pasek startowy (za pośrednictwem aplikacji ng) tą ręczną metodą. Jeśli nie wyjmiesz aplikacji ng, aplikacja może faktycznie działać, ale zobaczysz w konsoli błędy różnych nieznanych dostawców.
IrishDubGuy,
49

Użyłem podobnego podejścia do opisanego przez @XMLilley, ale chciałem mieć możliwość korzystania z usług AngularJS, takich jak $http ładowanie konfiguracji i przeprowadzanie dalszej inicjalizacji bez użycia interfejsów API niskiego poziomu lub jQuery.

Używanie resolvena trasach również nie było opcją, ponieważ potrzebowałem, aby wartości były dostępne jako stałe, gdy moja aplikacja jest uruchomiona, nawet wmodule.config() blokach.

Stworzyłem małą aplikację AngularJS, która ładuje konfigurację, ustawia je jako stałe w rzeczywistej aplikacji i ładuje ją.

// define the module of your app
angular.module('MyApp', []);

// define the module of the bootstrap app
var bootstrapModule = angular.module('bootstrapModule', []);

// the bootstrapper service loads the config and bootstraps the specified app
bootstrapModule.factory('bootstrapper', function ($http, $log, $q) {
  return {
    bootstrap: function (appName) {
      var deferred = $q.defer();

      $http.get('/some/url')
        .success(function (config) {
          // set all returned values as constants on the app...
          var myApp = angular.module(appName);
          angular.forEach(config, function(value, key){
            myApp.constant(key, value);
          });
          // ...and bootstrap the actual app.
          angular.bootstrap(document, [appName]);
          deferred.resolve();
        })
        .error(function () {
          $log.warn('Could not initialize application, configuration could not be loaded.');
          deferred.reject();
        });

      return deferred.promise;
    }
  };
});

// create a div which is used as the root of the bootstrap app
var appContainer = document.createElement('div');

// in run() function you can now use the bootstrapper service and shutdown the bootstrapping app after initialization of your actual app
bootstrapModule.run(function (bootstrapper) {

  bootstrapper.bootstrap('MyApp').then(function () {
    // removing the container will destroy the bootstrap app
    appContainer.remove();
  });

});

// make sure the DOM is fully loaded before bootstrapping.
angular.element(document).ready(function() {
  angular.bootstrap(appContainer, ['bootstrapModule']);
});

Zobacz to w akcji (używając $timeoutzamiast $http) tutaj: http://plnkr.co/edit/FYznxP3xe8dxzwxs37hi?p=preview

AKTUALIZACJA

Poleciłbym zastosować podejście opisane poniżej przez Martina Atkinsa i JBCP.

AKTUALIZACJA 2

Ponieważ potrzebowałem go w wielu projektach, właśnie wydałem moduł altany, który zajmuje się tym: https://github.com/philippd/angular-deferred-bootstrap

Przykład, który ładuje dane z zaplecza i ustawia stałą o nazwie APP_CONFIG w module AngularJS:

deferredBootstrapper.bootstrap({
  element: document.body,
  module: 'MyApp',
  resolve: {
    APP_CONFIG: function ($http) {
      return $http.get('/api/demo-config');
    }
  }
});
philippd
źródło
11
deferredBootstrapper to najlepsza droga
fatlinesofcode
44

Przypadek „ręcznego ładowania” może uzyskać dostęp do usług Angular poprzez ręczne utworzenie wtryskiwacza przed rozpoczęciem ładowania. Ten początkowy wtryskiwacz będzie samodzielny (nie będzie dołączany do żadnych elementów) i będzie zawierał tylko podzbiór modułów, które są załadowane. Jeśli wszystko, czego potrzebujesz, to podstawowe usługi Angular, wystarczy po prostu załadować ng:

angular.element(document).ready(
    function() {
        var initInjector = angular.injector(['ng']);
        var $http = initInjector.get('$http');
        $http.get('/config.json').then(
            function (response) {
               var config = response.data;
               // Add additional services/constants/variables to your app,
               // and then finally bootstrap it:
               angular.bootstrap(document, ['myApp']);
            }
        );
    }
);

Możesz na przykład użyć module.constantmechanizmu, aby udostępnić dane swojej aplikacji:

myApp.constant('myAppConfig', data);

To myAppConfigmoże teraz być wstrzykiwane tak jak wszystkie inne usługi, a w szczególności jest ona dostępna w fazie konfiguracji:

myApp.config(
    function (myAppConfig, someService) {
        someService.config(myAppConfig.someServiceConfig);
    }
);

lub, w przypadku mniejszej aplikacji, możesz po prostu wstrzyknąć globalną konfigurację bezpośrednio do swojej usługi, kosztem rozpowszechniania wiedzy o formacie konfiguracji w całej aplikacji.

Oczywiście, ponieważ operacje asynchroniczne blokują tutaj bootstrap aplikacji, a tym samym blokują kompilację / linkowanie szablonu, rozsądnie jest użyć ng-cloakdyrektywy, aby zapobiec wyświetlaniu niesparowanego szablonu podczas pracy. Możesz również podać jakieś wskazanie ładowania w DOM, dostarczając trochę HTML, który jest pokazywany tylko do momentu zainicjowania AngularJS:

<div ng-if="initialLoad">
    <!-- initialLoad never gets set, so this div vanishes as soon as Angular is done compiling -->
    <p>Loading the app.....</p>
</div>
<div ng-cloak>
    <!-- ng-cloak attribute is removed once the app is done bootstrapping -->
    <p>Done loading the app!</p>
</div>

Stworzyłem kompletny, działający przykład tego podejścia na Plunkerze, ładując konfigurację ze statycznego pliku JSON jako przykład.

Martin Atkins
źródło
Nie sądzę, że musisz odroczyć $ http.get (), dopóki dokument nie będzie gotowy.
JBCP
@JBCP tak, masz rację, że działa równie dobrze, jeśli zamienisz zdarzenia, abyśmy nie czekali na przygotowanie dokumentu do czasu po zwróceniu odpowiedzi HTTP, z korzyścią z możliwości rozpoczęcia HTTP żądać szybciej. Tylko wywołanie bootstrap musi poczekać, aż DOM będzie gotowy.
Martin Atkins
2
Z twoim podejściem stworzyłem moduł altany: github.com/philippd/angular-deferred-bootstrap
philippd
@MartinAtkins, właśnie odkryłem, że twoje świetne podejście nie działa z Angular v1.1 +. Wygląda na to, że wczesne wersje Angulara po prostu nie rozumieją „wtedy”, dopóki aplikacja nie zostanie załadowana. Aby zobaczyć to w Plunk, zamień Angular URL na code.angularjs.org/1.1.5/angular.min.js
vkelman
16

Miałem ten sam problem: uwielbiam ten resolveobiekt, ale działa to tylko w przypadku zawartości ng-view. Co się stanie, jeśli masz kontrolery (powiedzmy na przykład na najwyższym poziomie), które istnieją poza ng-view i które muszą zostać zainicjowane danymi, zanim jeszcze zacznie się routing? W jaki sposób unikamy chowania się po stronie serwera, aby to działało?

Użyj ręcznego paska startowego i stałej kątowej . Naiwny XHR pobiera twoje dane, a Ty odruchowo uruchamiasz w swoim wywołaniu zwrotnym, który zajmuje się problemami z asynchronizacją. W poniższym przykładzie nie musisz nawet tworzyć zmiennej globalnej. Zwracane dane istnieją tylko w zakresie kątowym jako wstrzykiwalne i nie są nawet obecne w kontrolerach, usługach itp., Chyba że zostaną wstrzyknięte. (Tak, jakbyś wstrzyknął swoje wyjścieresolve obiektu do kontrolera w celu uzyskania widoku trasy). Jeśli wolisz później interakcję z tymi danymi jako usługą, możesz utworzyć usługę, wstrzyknąć dane, a nikt nigdy nie będzie mądrzejszy .

Przykład:

//First, we have to create the angular module, because all the other JS files are going to load while we're getting data and bootstrapping, and they need to be able to attach to it.
var MyApp = angular.module('MyApp', ['dependency1', 'dependency2']);

// Use angular's version of document.ready() just to make extra-sure DOM is fully 
// loaded before you bootstrap. This is probably optional, given that the async 
// data call will probably take significantly longer than DOM load. YMMV.
// Has the added virtue of keeping your XHR junk out of global scope. 
angular.element(document).ready(function() {

    //first, we create the callback that will fire after the data is down
    function xhrCallback() {
        var myData = this.responseText; // the XHR output

        // here's where we attach a constant containing the API data to our app 
        // module. Don't forget to parse JSON, which `$http` normally does for you.
        MyApp.constant('NavData', JSON.parse(myData));

        // now, perform any other final configuration of your angular module.
        MyApp.config(['$routeProvider', function ($routeProvider) {
            $routeProvider
              .when('/someroute', {configs})
              .otherwise({redirectTo: '/someroute'});
          }]);

        // And last, bootstrap the app. Be sure to remove `ng-app` from your index.html.
        angular.bootstrap(document, ['NYSP']);
    };

    //here, the basic mechanics of the XHR, which you can customize.
    var oReq = new XMLHttpRequest();
    oReq.onload = xhrCallback;
    oReq.open("get", "/api/overview", true); // your specific API URL
    oReq.send();
})

Teraz twoja NavDatastała istnieje. Śmiało i wstrzyknij go do kontrolera lub usługi:

angular.module('MyApp')
    .controller('NavCtrl', ['NavData', function (NavData) {
        $scope.localObject = NavData; //now it's addressable in your templates 
}]);

Oczywiście użycie nagiego obiektu XHR usuwa wiele drobiazgów, o które $httpzadbałby Tobie JQuery, ale ten przykład działa bez specjalnych zależności, przynajmniej dla prostejget . Jeśli potrzebujesz trochę więcej mocy na żądanie, załaduj bibliotekę zewnętrzną, aby Ci pomóc. Ale nie sądzę, że można uzyskać dostęp do kątownika$http w tym kontekście narzędzi lub innych narzędzi.

( Post związany z SO )

XML
źródło
8

To, co możesz zrobić, znajduje się w .config dla aplikacji, aby utworzyć obiekt rozstrzygania trasy i przekazać funkcję w $ q (obiekt przyrzeczenia) i nazwę usługi, od której zależysz, i rozwiązać obietnicę w funkcja oddzwaniania dla $ http w usłudze tak:

KONFIGURACJA TRASY

app.config(function($routeProvider){
    $routeProvider
     .when('/',{
          templateUrl: 'home.html',
          controller: 'homeCtrl',
          resolve:function($q,MyService) {
                //create the defer variable and pass it to our service
                var defer = $q.defer();
                MyService.fetchData(defer);
                //this will only return when the promise
                //has been resolved. MyService is going to
                //do that for us
                return defer.promise;
          }
      })
}

Angular nie wyświetli szablonu ani nie udostępni kontrolera, dopóki nie zostanie wywołana defer.resolve (). Możemy to zrobić w naszym serwisie:

USŁUGA

app.service('MyService',function($http){
       var MyService = {};
       //our service accepts a promise object which 
       //it will resolve on behalf of the calling function
       MyService.fetchData = function(q) {
             $http({method:'GET',url:'data.php'}).success(function(data){
                 MyService.data = data;
                 //when the following is called it will
                 //release the calling function. in this
                 //case it's the resolve function in our
                 //route config
                 q.resolve();
             }
       }

       return MyService;
});

Teraz, gdy MyService ma przypisane dane do swojej właściwości data, a obietnica zawarta w obiekcie rozstrzygania trasy została rozwiązana, nasz kontroler trasy rozpoczyna się i możemy przypisać dane z usługi do obiektu kontrolera.

KONTROLER

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

Teraz wszystkie nasze powiązania w zakresie administratora będą mogły korzystać z danych pochodzących z MyService.

rosa
źródło
Dam temu szansę, kiedy będę miał więcej czasu. Wygląda to podobnie do tego, co inni próbowali zrobić w ngModules.
testowanie123
1
Podoba mi się to podejście i korzystałem z niego wcześniej, ale obecnie próbuję wypracować, jak to zrobić w czysty sposób, gdy mam kilka tras, z których każda może, ale nie musi, zależeć od wstępnie pobranych danych. Jakieś myśli na ten temat?
ivarni
btw, jestem skłonny, aby każda usługa, która wymaga wstępnie pobranych danych, wysłała żądanie podczas inicjalizacji i zwróciła obietnicę, a następnie skonfigurowała obiekty rozstrzygające z usługami wymaganymi przez różne trasy. Chciałbym tylko, żeby był mniej szczegółowy sposób.
ivarni
1
@dewd Do tego właśnie dążyłem, ale wolałbym, gdyby był jakiś sposób, aby powiedzieć „najpierw pobieraj wszystkie te rzeczy bez względu na to, która trasa jest załadowana”, bez konieczności powtarzania moich bloków rozwiązywania. Wszystkie mają coś, na czym polegają. Ale to nie jest wielka sprawa, po prostu poczułbym się trochę bardziej SUCHY :)
ivarni
2
jest to trasa, którą ostatecznie wybrałem, z wyjątkiem tego, że musiałem stworzyć resolveobiekt z właściwością będącą funkcją. więc skończyło się naresolve:{ dataFetch: function(){ // call function here } }
aron.duby
5

Więc znalazłem rozwiązanie. Stworzyłem usługę angularJS, nazwiemy ją MyDataRepository i stworzyłem dla niej moduł. Następnie podaję ten plik javascript z kontrolera po stronie serwera:

HTML:

<script src="path/myData.js"></script>

Po stronie serwera:

@RequestMapping(value="path/myData.js", method=RequestMethod.GET)
public ResponseEntity<String> getMyDataRepositoryJS()
{
    // Populate data that I need into a Map
    Map<String, String> myData = new HashMap<String,String>();
    ...
    // Use Jackson to convert it to JSON
    ObjectMapper mapper = new ObjectMapper();
    String myDataStr = mapper.writeValueAsString(myData);

    // Then create a String that is my javascript file
    String myJS = "'use strict';" +
    "(function() {" +
    "var myDataModule = angular.module('myApp.myData', []);" +
    "myDataModule.service('MyDataRepository', function() {" +
        "var myData = "+myDataStr+";" +
        "return {" +
            "getData: function () {" +
                "return myData;" +
            "}" +
        "}" +
    "});" +
    "})();"

    // Now send it to the client:
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.add("Content-Type", "text/javascript");
    return new ResponseEntity<String>(myJS , responseHeaders, HttpStatus.OK);
}

Następnie mogę wstrzyknąć MyDataRepository tam, gdzie jest to potrzebne:

someOtherModule.service('MyOtherService', function(MyDataRepository) {
    var myData = MyDataRepository.getData();
    // Do what you have to do...
}

Działa to dla mnie świetnie, ale jestem otwarty na wszelkie opinie, jeśli ktoś je ma. }

testowanie123
źródło
Podoba mi się twoje podejście modułowe. Odkryłem, że $ routeScope jest dostępny dla usługi żądającej danych i możesz przypisać do niej dane w wywołaniu zwrotnym $ http.success. Jednak użycie $ routeScope dla przedmiotów nieglobalnych tworzy zapach, a dane należy naprawdę przypisać do zakresu $ kontrolera. Niestety, myślę, że twoje podejście, choć innowacyjne, nie jest idealne (choć szacunek dla znalezienia czegoś, co działa dla ciebie). Jestem pewien, że musi istnieć odpowiedź tylko po stronie klienta, która w jakiś sposób czeka na dane i umożliwia przypisanie do zakresu. Wyszukiwanie trwa!
dewd
W przypadku, gdy jest to przydatne dla kogoś, ostatnio widziałem różne podejścia do modułów napisanych przez innych ludzi i dodanych do strony internetowej ngModules. Kiedy będę miał więcej czasu, będę musiał albo zacząć korzystać z jednego z nich, albo dowiedzieć się, co zrobili i dodać to do moich rzeczy.
testowanie123
2

Ponadto przed użyciem rzeczywistych kontrolerów można użyć następujących technik do globalnego świadczenia usług: https://stackoverflow.com/a/27050497/1056679 . Po prostu rozwiąż swoje dane globalnie, a następnie przekaż je runna przykład do usługi blokowo.

Slava Fomin II
źródło
1

Możesz użyć JSONP do asynchronicznego ładowania danych usługi. Żądanie JSONP zostanie wykonane podczas początkowego ładowania strony, a wyniki będą dostępne przed uruchomieniem aplikacji. W ten sposób nie będziesz musiał nadmuchać routingu za pomocą nadmiarowych rozwiązań.

Twój HTML wyglądałby tak:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>

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

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

</script>
<script src="/some_data.php?jsonp=MyService.setData"></script>
hegemon
źródło
-1

Najprostszym sposobem na pobranie dowolnego katalogu inicjalizacji jest użycie katalogu ng-init.

Wystarczy umieścić zakres div ng-init div w miejscu, w którym chcesz pobrać dane init

index.html

<div class="frame" ng-init="init()">
    <div class="bit-1">
      <div class="field p-r">
        <label ng-show="regi_step2.address" class="show-hide c-t-1 ng-hide" style="">Country</label>
        <select class="form-control w-100" ng-model="country" name="country" id="country" ng-options="item.name for item in countries" ng-change="stateChanged()" >
        </select>
        <textarea class="form-control w-100" ng-model="regi_step2.address" placeholder="Address" name="address" id="address" ng-required="true" style=""></textarea>
      </div>
    </div>
  </div>

index.js

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

UWAGA: możesz użyć tej metodologii, jeśli nie masz tego samego kodu więcej niż jedno miejsce.

Roni
źródło
Nie zaleca się używania ngInit zgodnie z dokumentacją: docs.quarejs.org/api/ng/directive/ngInit
fodma1