Jakie „rzeczy” można wstrzyknąć innym w Angular.js?

142

Mam trochę trudności ze zrozumieniem Dependency Injection w Angular. Więc moje pytanie brzmi: czy ktokolwiek może wyjaśnić, które z „typów”, takich jak kontroler, fabryka, dostawca itp., Możemy wstrzyknąć do innych, w tym do innych instancji tego samego „typu”?

To, czego właściwie szukam, to ta tabela wypełniona t / n. W przypadku komórek z tym samym wierszem / kolumną oznacza to wstawienie wartości jednego „typu” do innego o tym samym „typie”

+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Can we inject? | Constant | Controller | Directive | Factory | Filter | Provider | Service | Value |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Constant       |          |            |           |         |        |          |         |       |
| Controller     |          |            |           |         |        |          |         |       |
| Directive      |          |            |           |         |        |          |         |       |
| Factory        |          |            |           |         |        |          |         |       |
| Filter         |          |            |           |         |        |          |         |       |
| Provider       |          |            |           |         |        |          |         |       |
| Service        |          |            |           |         |        |          |         |       |
| Value          |          |            |           |         |        |          |         |       |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
user1527166
źródło
tutaj jest odpowiedź docs.angularjs.org/guide/providers#conclusion
JFouad

Odpowiedzi:

391

Zamiast tego wypełnić tabelę słowami „tak” i „nie” bez wyjaśnienia, przejdę do bardziej szczegółowych informacji.

[Uwaga, dodane po skończeniu: skończyło się to ... trochę dłużej niż się spodziewałem. Na dole znajduje się tl; dr, ale mam nadzieję, że to okaże się przydatne.]

[Ta odpowiedź została również dodana do wiki AngularJS: Understanding Dependency Injection ]


Dostawca ( $provide)

$provideUsługa jest odpowiedzialny za mówienie kątowych, jak tworzyć nowe rzeczy do wstrzyknięć; te rzeczy nazywane są usługami . Usługi są definiowane przez rzeczy zwane dostawcami , czyli to, co tworzysz, używając $provide. Definiowanie dostawcy odbywa się za pomocą providermetody w $provideusłudze i można uzyskać dostęp do $provideusługi, prosząc o wstrzyknięcie jej do funkcji aplikacji config. Przykładem może być coś takiego:

app.config(function($provide) {
  $provide.provider('greeting', function() {
    this.$get = function() {
      return function(name) {
        alert("Hello, " + name);
      };
    };
  });
});

Tutaj zdefiniowaliśmy nowego dostawcę usługi o nazwie greeting; możemy wstrzyknąć zmienną nazwaną greetingdo dowolnej funkcji do wstrzykiwania (jak kontrolery, o tym później), a Angular wywoła funkcję dostawcy $getw celu zwrócenia nowej instancji usługi. W tym przypadku zostanie wstrzyknięta funkcja, która pobiera nameparametr i alertkomunikat oparty na nazwie. Możemy tego użyć w ten sposób:

app.controller('MainController', function($scope, greeting) {
  $scope.onClick = function() {
    greeting('Ford Prefect');
  };
});

Oto sztuczka. factory, servicei valuewszystkie są tylko skrótami do definiowania różnych części dostawcy - to znaczy zapewniają sposób definiowania dostawcy bez konieczności wypisywania wszystkich tych rzeczy. Na przykład możesz napisać dokładnie tego samego dostawcę w ten sposób:

app.config(function($provide) {
  $provide.factory('greeting', function() {
    return function(name) {
      alert("Hello, " + name);
    };
  });
});

Ważne jest, aby to zrozumieć, więc powtórzę: pod maską AngularJS wywołuje dokładnie ten sam kod , który napisaliśmy powyżej ( $provide.providerwersja) dla nas. Nie ma dosłownie 100% żadnej różnicy w obu wersjach. valuedziała tak samo - jeśli cokolwiek byśmy zwrócili z naszej $getfunkcji (czyli naszej factoryfunkcji) jest zawsze dokładnie takie samo, możemy napisać jeszcze mniej kodu używając value. Na przykład, ponieważ zawsze zwracamy tę samą funkcję dla naszej greetingusługi, możemy ją valuerównież zdefiniować:

app.config(function($provide) {
  $provide.value('greeting', function(name) {
    alert("Hello, " + name);
  });
});

Ponownie, jest to w 100% identyczne z pozostałymi dwiema metodami, których użyliśmy do zdefiniowania tej funkcji - to tylko sposób na zaoszczędzenie trochę pisania.

Teraz prawdopodobnie zauważyłeś tę irytującą app.config(function($provide) { ... })rzecz, której używałem. Ponieważ definiowanie nowych dostawców (za pomocą dowolnej z powyższych metod) jest tak powszechne, AngularJS ujawnia $providermetody bezpośrednio w obiekcie modułu, aby zaoszczędzić jeszcze więcej wpisywania:

var myMod = angular.module('myModule', []);

myMod.provider("greeting", ...);
myMod.factory("greeting", ...);
myMod.value("greeting", ...);

Wszystkie robią to samo, co bardziej szczegółowe app.config(...)wersje, których używaliśmy wcześniej.

Jedyny wstrzykiwany, który dotychczas pominąłem, to constant. Na razie łatwo powiedzieć, że działa tak samo value. Później zobaczymy jedną różnicę.

Aby przejrzeć , wszystkie te fragmenty kodu robią dokładnie to samo:

myMod.provider('greeting', function() {
  this.$get = function() {
    return function(name) {
      alert("Hello, " + name);
    };
  };
});

myMod.factory('greeting', function() {
  return function(name) {
    alert("Hello, " + name);
  };
});

myMod.value('greeting', function(name) {
  alert("Hello, " + name);
});

Wtryskiwacz ( $injector)

Wtryskiwacz jest odpowiedzialny za faktyczne tworzenie wystąpień naszych usług przy użyciu dostarczonego przez nas kodu $provide(gra słów nie jest przeznaczona). Za każdym razem, gdy piszesz funkcję, która przyjmuje wstrzyknięte argumenty, widzisz, jak działa wtryskiwacz. Każda aplikacja AngularJS ma jedną, $injectorktóra jest tworzona podczas pierwszego uruchomienia aplikacji; możesz go zdobyć, wstrzykując $injectordo dowolnej funkcji do wstrzykiwania (tak, $injectorwie, jak wstrzyknąć się!)

Gdy już to zrobisz $injector, możesz uzyskać wystąpienie określonej usługi, wywołując getją z nazwą usługi. Na przykład,

var greeting = $injector.get('greeting');
greeting('Ford Prefect');

Wtryskiwacz jest również odpowiedzialny za wstrzykiwanie usług do funkcji; na przykład, możesz magicznie wstrzyknąć usługi do dowolnej funkcji, którą posiadasz, używając metody wtryskiwacza invoke;

var myFunction = function(greeting) {
  greeting('Ford Prefect');
};
$injector.invoke(myFunction);

Warto zauważyć, że wtryskiwacz utworzy instancję usługi tylko raz . Następnie buforuje wszystko, co zwraca dostawca według nazwy usługi; następnym razem, gdy poprosisz o usługę, otrzymasz dokładnie ten sam przedmiot.

Tak więc, aby odpowiedzieć na twoje pytanie, możesz wstrzyknąć usługi do dowolnej funkcji, która jest wywoływana with$injector.invoke . To zawiera

  • funkcje definicji kontrolera
  • funkcje definicji dyrektywy
  • funkcje definicji filtrów
  • te $getmetody dostawców (aka factoryfunkcji definicji)

Ponieważ constants i values zawsze zwracają wartość statyczną, nie są wywoływane przez wtryskiwacz, a zatem nie można im nic wstrzyknąć.

Konfigurowanie dostawców

Być może zastanawiasz się, dlaczego ktoś miałby przejmować założyć pełnoprawnym operatorem z providemetodą jeśli factory, valueitp są o wiele łatwiejsze. Odpowiedź jest taka, że ​​dostawcy pozwalają na wiele konfiguracji. Wspomnieliśmy już, że kiedy tworzysz usługę za pośrednictwem dostawcy (lub dowolnego skrótu, który daje Ci Angular), tworzysz nowego dostawcę, który definiuje sposób konstruowania tej usługi. Co mogę nie wspomnieć, że tych dostawców może być wstrzyknięty do configsekcji aplikacji, więc można wchodzić w interakcje z nimi!

Po pierwsze, Angular uruchamia aplikację w dwóch fazach - fazach configi run. configFazy, jak widzieliśmy, jest miejsce, gdzie można skonfigurować żadnych dostawców, jak to konieczne. Tutaj również konfigurowane są dyrektywy, kontrolery, filtry i tym podobne. runFazy, jak można się domyślać, gdzie jest rzeczywiście kątowa kompiluje swój DOM i uruchamia aplikację.

Możesz dodać dodatkowy kod do uruchomienia w tych fazach za pomocą funkcji myMod.configi myMod.run- każda z nich przyjmuje funkcję do uruchomienia w tej określonej fazie. Jak widzieliśmy w pierwszej sekcji, funkcje te można wstrzykiwać - wstrzyknęliśmy wbudowaną $provideusługę w naszym pierwszym przykładzie kodu. Warto jednak zauważyć, że na configetapie wstrzyknięcia mogą być tylko dostawcy (z wyjątkiem usług w AUTOmodule - $providei $injector).

Na przykład niedozwolone jest :

myMod.config(function(greeting) {
  // WON'T WORK -- greeting is an *instance* of a service.
  // Only providers for services can be injected in config blocks.
});

Co zrobić mają dostępu do jakichkolwiek dostawców za usługi już wykonane:

myMod.config(function(greetingProvider) {
  // a-ok!
});

Jest jeden ważny wyjątek: constants, ponieważ nie można ich zmienić, mogą być wstrzykiwane do configbloków (w ten sposób różnią się od values). Dostęp do nich uzyskuje się za pomocą samej nazwy ( Providerprzyrostek nie jest wymagany).

Za każdym razem, gdy definiujesz dostawcę usługi, ten dostawca jest nazywany serviceProvider, gdzie servicejest nazwa usługi. Teraz możemy wykorzystać moc dostawców do robienia bardziej skomplikowanych rzeczy!

myMod.provider('greeting', function() {
  var text = 'Hello, ';

  this.setText = function(value) {
    text = value;
  };

  this.$get = function() {
    return function(name) {
      alert(text + name);
    };
  };
});

myMod.config(function(greetingProvider) {
  greetingProvider.setText("Howdy there, ");
});

myMod.run(function(greeting) {
  greeting('Ford Prefect');
});

Teraz mamy funkcję naszego dostawcy o nazwie setText, której możemy użyć do dostosowania naszego alert; możemy uzyskać dostęp do tego dostawcy w configbloku, aby wywołać tę metodę i dostosować usługę. Kiedy w końcu uruchomimy naszą aplikację, możemy pobrać greetingusługę i wypróbować ją, aby zobaczyć, czy nasze dostosowanie przyniosło skutek.

Ponieważ jest to bardziej złożony przykład, oto działająca demonstracja: http://jsfiddle.net/BinaryMuse/9GjYg/

Kontrolery ( $controller)

Funkcje kontrolera można wstrzykiwać, ale same kontrolery nie mogą być wstrzykiwane do innych rzeczy. Dzieje się tak, ponieważ kontrolery nie są tworzone przez dostawcę. Zamiast tego istnieje wbudowana usługa Angular o nazwie, $controllerktóra jest odpowiedzialna za konfigurowanie kontrolerów. Kiedy dzwonisz myMod.controller(...), w rzeczywistości uzyskujesz dostęp do dostawcy tej usługi , tak jak w ostatniej sekcji.

Na przykład, gdy zdefiniujesz kontroler w ten sposób:

myMod.controller('MainController', function($scope) {
  // ...
});

Tak naprawdę robisz:

myMod.config(function($controllerProvider) {
  $controllerProvider.register('MainController', function($scope) {
    // ...
  });
});

Później, gdy Angular będzie musiał utworzyć wystąpienie twojego kontrolera, używa $controllerusługi (która z kolei używa $injectordo wywołania funkcji kontrolera, więc dostaje również jej zależności).

Filtry i dyrektywy

filteri directivedziałają dokładnie tak samo jak controller; filterkorzysta z wywołanej usługi $filteri jej dostawcy $filterProvider, podczas gdy directivekorzysta z wywołanej usługi $compilei jej dostawcy $compileProvider. Niektóre linki:

Jak w innych przykładach myMod.filteri myMod.directivesą skrótami do konfiguracji tych usług.


tl; dr

Podsumowując, każda funkcja, która zostanie wywołana za pomocą, $injector.invoke może zostać wstrzyknięta . Obejmuje to z wykresu (ale nie tylko):

  • kontroler
  • dyrektywa
  • fabryka
  • filtr
  • dostawca $get(przy definiowaniu dostawcy jako obiektu)
  • funkcja dostawcy (podczas definiowania dostawcy jako funkcji konstruktora)
  • usługa

Dostawca tworzy nowe usługi, które można wstrzyknąć do rzeczy . To zawiera:

  • stały
  • fabryka
  • dostawca
  • usługa
  • wartość

To powiedziawszy, wbudowane usługi, takie jak $controlleri $filter mogą być wstrzykiwane, i możesz użyć tych usług, aby uzyskać dostęp do nowych filtrów i kontrolerów, które zdefiniowałeś za pomocą tych metod (nawet jeśli rzeczy, które zdefiniowałeś, same nie mogą być wstrzyknięte do rzeczy).

Poza tym każda funkcja wywoływana przez wtryskiwacz może być wstrzykiwana za pomocą dowolnej usługi świadczonej przez dostawcę - nie ma żadnych ograniczeń (poza wymienionymi tutaj configi runróżnicami).

Michelle Tilley
źródło
6
Łał! dziękuję za poświęcenie czasu na udzielenie szczegółowych odpowiedzi! Przeczytałem to dwa razy i myślę, że całkiem sporo zrozumiałem. Przestudiuję to i linki, które podałeś szczegółowo później, dzisiaj. I kolejne +1 dla kota. :)
user1527166
18
Jedna z najbardziej przydatnych i szczegółowych odpowiedzi SO, z jakimi się spotkałem - dzięki!
Godders
11
Ta odpowiedź definiuje nowy poziom niesamowitości. Rozświetlające rzeczy.
Ngure Nyaga
4
Zdecydowanie najlepsze źródło informacji o AngularJS, z jakim się spotkałem. Dzięki.
code90
5
Dosłownie najlepszy fragment dokumentacji AngularJS, jaki widziałem. Tak trzymać!
Iain Duncan
13

Kwestia, którą BinaryMuse czyni w swojej niesamowitej odpowiedzi na temat dostawców, fabryk i usług, są niezwykle ważne.

Poniżej znajduje się obraz, który moim zdaniem może wizualnie zilustrować jej punkt widzenia:

AngularJS są tylko dostawcami
(źródło: Simplygoodcode.com )

Luis Perez
źródło
7

Świetna odpowiedź Michelle. Chcę tylko zaznaczyć, że dyrektywy można wstrzyknąć. Jeśli masz dyrektywę o nazwie myThing, możesz ją wstrzyknąć myThingDirective: Oto wymyślony przykład .

Powyższy przykład nie jest zbyt praktyczny, jednak możliwość wstrzyknięcia dyrektywy jest przydatna, gdy chcesz ją ozdobić .

Gil Birman
źródło
Wygląda na to, że drugi przykład do dekoracji tej dyrektywy nie działa od czasu Angular 1.4. (patrz tam komentarz Juana
Biscaia