użyj $ http wewnątrz niestandardowego dostawcy w konfiguracji aplikacji, angular.js

90

Główne pytanie - czy to możliwe? Próbowałem bez powodzenia ...

główna aplikacja.js

...
var app = angular.module('myApp', ['services']);
app.config(['customProvider', function (customProvider) {

}]);
...

sam dostawca

var services = angular.module('services', []);
services.provider('custom', function ($http) {
});

I mam taki błąd:

Uncaught Error: Unknown provider: $http from services 

Jakieś pomysły?

Dzięki!

Kosmetika
źródło
stary, tak to prawda, ale mówię o app.configczęści
Kosmetika
też wiem o tym ograniczeniu, ale pomyślałem, że wewnątrz dostawcy jest to jakoś możliwe ..
Kosmetika

Odpowiedzi:

158

Najważniejsze jest to:

  • Ty NIE wstrzykiwać usługę w sekcji konfiguracji dostawcy .
  • Ci CAN wstrzyknąć usługę do odcinka, który inicjuje usługę usługodawcy .

Detale:

Framework Angular ma 2-fazowy proces inicjalizacji:

FAZA 1: Konfig

W tej configfazie inicjowani są wszyscy dostawcy i configwykonywane są wszystkie sekcje. Te configsekcje mogą zawierać kod, który konfiguruje obiektów dostawcy i dlatego można je podawać z obiektami dostawcy. Ponieważ jednak dostawcy są fabrykami obiektów usług i na tym etapie dostawcy nie są w pełni zainicjowani / skonfigurowani -> na tym etapie nie możesz poprosić dostawcy o utworzenie usługi dla Ciebie -> na etapie konfiguracji nie możesz użyć / wstrzyknąć usługi . Po zakończeniu tej fazy wszyscy dostawcy są gotowi (po zakończeniu fazy konfiguracji nie można już konfigurować dostawców).

FAZA 2: Uruchom

W runfazie wszystkie runsekcje są wykonywane. Na tym etapie dostawcy są gotowi i mogą tworzyć usługi -> podczas runfazy możesz korzystać z usług / wstrzykiwać .

Przykłady:

1. Wstrzyknięcie $httpusługi do funkcji inicjalizacji dostawcy NIE BĘDZIE działać

//ERRONEOUS
angular.module('myModule').provider('myProvider', function($http) {
    // SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
    ...

    this.$get = function() {
        // code to initialize/configure the SERVICE goes here (executed during `run` stage)

        return myService;
    };
});

Ponieważ próbujemy wstrzyknąć $httpusługę do funkcji, która jest wykonywana w tej configfazie, otrzymamy błąd:

Uncaught Error: Unknown provider: $http from services 

Ten błąd w rzeczywistości mówi, że plik, $httpProviderktóry jest używany do tworzenia $httpusługi, nie jest jeszcze gotowy (ponieważ wciąż jesteśmy w tej configfazie).

2. Wprowadzenie $httpusługi do funkcji inicjalizacji usługi BĘDZIE zadziałało:

//OK
angular.module('myModule').provider('myProvider', function() {
    // SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
    ...

    this.$get = function($http) {
        // code to initialize/configure the SERVICE goes here (executed during `run` stage)

        return myService;
    };
});

Ponieważ teraz wstrzykujemy usługę do funkcji inicjalizacji usługi, która jest wykonywana w runfazie, ten kod będzie działał.

Dana Shalev
źródło
63
Dobra odpowiedź, ale chociaż wyjaśnia, w jaki sposób nie można wstrzyknąć usług podczas konfiguracji, nie wyjaśnia, jak wykonać HTTP POST / GET podczas konfiguracji. Jest to ważne w przypadku aplikacji, które są konfigurowane przy użyciu wartości udostępnianych przez interfejs API.
Sean O'Dell
3
@bebraw & Kosmetika - Myślę, że jedyną rzeczą, o której myślę, że będziesz musiał zażądać podczas fazy konfiguracji, jest jakiś obiekt ustawień. Może zawiera punkt końcowy API, informacje o użytkowniku, ustawienia regionalne i językowe użytkownika, itp. Jeśli tak jest, poleciłbym w jakiś sposób umieścić te informacje w źródle javascript. Możesz użyć renderowania po stronie serwera w pliku index.html, aby wprowadzić kilka ustawień, aby były one dostępne przed zainicjowaniem aplikacji. Wszystko inne, spróbuję wymyślić, jak to zrobić po inicjalizacji
Sean Clark Hess
2
@Sean: Jak zrobić HTTP POST / GET to inne pytanie niż OP (czy można użyć $ http w fazie konfiguracji?) I prawdopodobnie zasługuje na osobny wpis; Ze względu na synchroniczny charakter fazy konfiguracji Angulara, dobrym sposobem na dostarczenie danych po stronie serwera do kodu konfiguracyjnego jest renderowanie go jako obiektu javascript na stronie HTML podczas renderowania po stronie serwera (np <script>var config = <% = mySettings.toJson() %>;</script>.). Można to zrobić za pomocą silnika tworzenia szablonów, takiego jak Smarty for PHP, Jinja2 for Python, Nunchucks for NodeJS itp.
Trevor
4
@threed: Wstawianie danych konfiguracyjnych bezpośrednio do kodu HTML lub js na serwerze działa tylko wtedy, gdy kod klienta pochodzi z tego samego serwera. Dzięki CORS jest teraz możliwe (i bardzo pożądane), aby kod klienta był obsługiwany z innego serwera, a dane były obsługiwane z oddzielnych serwerów. W takich przypadkach musimy pobrać dane konfiguracyjne za pomocą protokołu HTTP.
Bernard
4
Chociaż jest to odpowiedź, nie jest to odpowiedź na zadane pytanie.
Eric
64

To może dać ci niewielką dźwignię:

var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');

Ale bądź ostrożny, wywołania zwrotne sukcesu / błędu mogą utrzymywać Cię w sytuacji wyścigu między uruchomieniem aplikacji a odpowiedzią serwera.

Cody
źródło
6
„Zaakceptowana odpowiedź” zawiodła u mojego dostawcy… Spędziłem 2 dni frustracji próbując wykonać tę pracę bez nadziei. Twoje podejście zadziałało natychmiast.
Dave Alperovich
Czy możesz wyjaśnić, czy utworzona tutaj instancja jest „prawdziwym” singletonem usługi, czy tylko instancją usługi, która jest odrzucana, gdy Angular wykonuje swoją prawdziwą magię iniektora.
Eric
Eric, w tej chwili nie mogę tego potwierdzić. Jednak to, co zwykle robię (jeśli dotyczy), to angular.injector(['mymodule'])- ale nie jestem pewien, czy możesz zastosować to podejście do $httpusługi. Chcę powiedzieć, że tak. Nie jestem pewien, czy to pomoże, czy nie: - /
Cody
2
To powinna być akceptowana odpowiedź. Przez jakiś czas walczyłem, próbując to zadziałać, i takie podejście natychmiast rozwiązało mój problem. Myślę, że może to być bardzo częsty problem. Dzięki @Cody
iamdash
5
Potwierdzam, że zaakceptowane rozwiązanie nie działa przy używaniu $ http w dostawcy. Ale odpowiedź @Cody załatwia sprawę
Dino
1

To stare pytanie, wydaje się, że mamy do czynienia z jajkiem kurzym, jeśli chcemy polegać na podstawowych możliwościach biblioteki.

Zamiast rozwiązać problem w fundamentalny sposób, zrobiłem obejście. Utwórz dyrektywę, która otacza całe ciało. Dawny.

<body ng-app="app">
  <div mc-body>
    Hello World
  </div>
</body>

Teraz mc-bodymusi zostać zainicjowany przed renderowaniem (raz), np.

link: function(scope, element, attrs) {
  Auth.login().then() ...
}

Auth jest usługą lub dostawcą, np.

.provider('Auth', function() {
  ... keep your auth configurations
  return {
    $get: function($http) {
      return {
        login: function() {
          ... do something about the http
        }
      }
    }
  }
})

Wydaje mi się, że mam kontrolę nad kolejnością ładowania początkowego, jest to po tym, jak zwykły program ładujący rozwiązuje całą konfigurację dostawcy, a następnie próbuje zainicjować mc-bodydyrektywę.

Wydaje mi się, że ta dyrektywa może wyprzedzać routing, ponieważ routing jest również wstrzykiwany za pośrednictwem dyrektywy ex. <ui-route />. Ale co do tego mogę się mylić. Potrzebuje więcej badań.

windmaomao
źródło
Czy możesz opisać swoje rozwiązanie?
Mark
-2

W odpowiedzi na Twoje pytanie „Jakieś pomysły?” Odpowiedziałbym „tak”. Ale czekaj, jest więcej!

Proponuję po prostu użyć JQuery w konfiguracji. Na przykład:

var app = angular.module('myApp', ['services']);
app.config(['$anyProvider', function ($anyProvider) {
    $.ajax({
        url: 'www.something.com/api/lolol',
        success: function (result) {
            $anyProvider.doSomething(result);
        }
    });
}]);
Suamere
źródło
$ customProvider w wywołaniu zwrotnym sukcesu zawiera $ tak, jakby był dostawcą wewnętrznym.
Jeff Fischer,
1
Masz rację, że miałem mieszankę $ i nie- $. Zaktualizowałem to na $.
Suamere