Welche „Dinge“ können in Angular.js in andere injiziert werden?

142

Es fällt mir ein wenig schwer, die Abhängigkeitsinjektion in Angular zu verstehen. Meine Frage ist also, kann jemand erklären, welchen der "Typen" wie Controller, Factory, Provider usw. wir in andere injizieren können, einschließlich anderer Instanzen desselben "Typs"?

Was ich eigentlich suche, ist diese Tabelle mit j / n gefüllt. Für Zellen mit derselben Zeile / Spalte bedeutet dies, dass der Wert eines "Typs" in einen anderen Wert mit demselben "Typ" eingefügt wird.

+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Can we inject? | Constant | Controller | Directive | Factory | Filter | Provider | Service | Value |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Constant       |          |            |           |         |        |          |         |       |
| Controller     |          |            |           |         |        |          |         |       |
| Directive      |          |            |           |         |        |          |         |       |
| Factory        |          |            |           |         |        |          |         |       |
| Filter         |          |            |           |         |        |          |         |       |
| Provider       |          |            |           |         |        |          |         |       |
| Service        |          |            |           |         |        |          |         |       |
| Value          |          |            |           |         |        |          |         |       |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
user1527166
quelle
Hier ist die Antwort docs.angularjs.org/guide/providers#conclusion
JFouad

Antworten:

391

Anstatt die Tabelle nur mit "Ja" und "Nein" ohne Erklärung auszufüllen, werde ich etwas näher darauf eingehen.

[Anmerkung, hinzugefügt nach Abschluss: Dies endete ... ziemlich viel länger als ich erwartet hatte. Unten ist ein tl; dr, aber ich hoffe, dies erweist sich als informativ.]

[Diese Antwort wurde auch dem AngularJS-Wiki hinzugefügt: Grundlegendes zur Abhängigkeitsinjektion ]


Der Anbieter ( $provide)

Der $provideDienst ist dafür verantwortlich, Angular mitzuteilen, wie neue injizierbare Dinge hergestellt werden sollen. Diese Dinge werden Dienste genannt . Services werden durch sogenannte Provider definiert , die Sie bei der Verwendung erstellen $provide. Das Definieren eines Anbieters erfolgt über die providerMethode des $provideDienstes. Sie können den $provideDienst abrufen, indem Sie ihn in die configFunktion einer Anwendung einfügen. Ein Beispiel könnte ungefähr so ​​sein:

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

Hier haben wir einen neuen Anbieter für einen Dienst namens definiert greeting. Wir können eine Variable mit dem Namen greetingin jede injizierbare Funktion einfügen (wie Controller, dazu später mehr), und Angular ruft die $getFunktion des Anbieters auf , um eine neue Instanz des Dienstes zurückzugeben. In diesem Fall wird eine Funktion injiziert, die einen nameParameter und eine alertNachricht basierend auf dem Namen verwendet. Wir könnten es so verwenden:

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

Hier ist der Trick. factory, serviceUnd valuesind alle nur Verknüpfungen verschiedene Teile eines Providers zu definieren - das heißt, sie ein Mittel zur Definition eines Anbieters zur Verfügung stellen , ohne dass alle Sachen aus eingeben zu müssen. Zum Beispiel könnten Sie genau denselben Anbieter wie folgt schreiben :

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

Es ist wichtig zu verstehen, also werde ich es umformulieren: Unter der Haube ruft AngularJS genau den Code auf , den wir oben (die $provide.providerVersion) für uns geschrieben haben. Es gibt buchstäblich 100% keinen Unterschied zwischen den beiden Versionen. valuefunktioniert genauso - wenn alles, was wir von unserer $getFunktion (auch bekannt als unsere factoryFunktion) zurückgeben würden, immer genau gleich ist, können wir mit noch weniger Code schreiben value. Da wir beispielsweise immer dieselbe Funktion für unseren greetingService zurückgeben, können wir sie auch valuedefinieren:

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

Auch dies ist zu 100% identisch mit den beiden anderen Methoden, mit denen wir diese Funktion definiert haben. Dies ist nur eine Möglichkeit, die Eingabe zu speichern.

Jetzt haben Sie wahrscheinlich diese nervige app.config(function($provide) { ... })Sache bemerkt , die ich benutzt habe. Da das Definieren neuer Anbieter (über eine der oben genannten Methoden) so häufig ist, macht AngularJS die $providerMethoden direkt auf dem Modulobjekt verfügbar, um noch mehr Eingabe zu sparen:

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

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

Diese machen alle dasselbe wie die ausführlicheren app.config(...)Versionen, die wir zuvor verwendet haben.

Das injizierbare, das ich bisher übersprungen habe, ist constant. Im Moment ist es leicht zu sagen, dass es genauso funktioniert value. Wir werden später sehen, dass es einen Unterschied gibt.

Zur Überprüfung , alle diese Stücke von Code tun , um die genaue gleiche Sache:

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

Der Injektor ( $injector)

Der Injektor ist dafür verantwortlich, Instanzen unserer Dienste mithilfe des von uns bereitgestellten Codes zu erstellen $provide(kein Wortspiel beabsichtigt). Jedes Mal, wenn Sie eine Funktion schreiben, die injizierte Argumente akzeptiert, sehen Sie den Injektor bei der Arbeit. Jede AngularJS-Anwendung verfügt über eine einzelne Anwendung $injector, die beim ersten Start der Anwendung erstellt wird. Sie können es erreichen, indem Sie es $injectorin eine injizierbare Funktion injizieren (ja, $injectorweiß, wie man sich selbst injiziert!)

Sobald Sie dies getan haben $injector, können Sie eine Instanz eines definierten Dienstes abrufen, indem getSie ihn mit dem Namen des Dienstes aufrufen . Beispielsweise,

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

Der Injektor ist auch für das Injizieren von Diensten in Funktionen verantwortlich. Mit der invokeMethode des Injektors können Sie beispielsweise Dienste auf magische Weise in jede Funktion injizieren, die Sie haben .

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

Sein bemerkenswert, dass der Injektor nur eine Instanz eines Dienstes erstellen einmal . Anschließend wird alles zwischengespeichert, was der Anbieter mit dem Namen des Dienstes zurückgibt. Wenn Sie das nächste Mal nach dem Service fragen, erhalten Sie genau das gleiche Objekt.

Um Ihre Frage zu beantworten, können Sie Dienste in jede Funktion$injector.invoke einfügen , mit der aufgerufen wird . Das beinhaltet

  • Controller-Definitionsfunktionen
  • Richtliniendefinitionsfunktionen
  • Filterdefinitionsfunktionen
  • die $getMethoden der Anbieter (auch bekannt als die factoryDefinitionsfunktionen)

Da constants und values immer einen statischen Wert zurückgeben, werden sie nicht über den Injektor aufgerufen, sodass Sie ihnen nichts injizieren können.

Anbieter konfigurieren

Sie wundern sich vielleicht , warum jemand einen vollwertigen Anbieter mit der einzurichten stören würde provideMethode , wenn factory, valueusw. sind so viel einfacher. Die Antwort ist, dass Anbieter viel Konfiguration zulassen. Wir haben bereits erwähnt, dass Sie beim Erstellen eines Dienstes über den Anbieter (oder eine der von Angular bereitgestellten Verknüpfungen) einen neuen Anbieter erstellen, der definiert, wie dieser Dienst aufgebaut ist. Was ich nicht erwähnt habe, ist, dass diese Anbieter in configAbschnitte Ihrer Anwendung eingefügt werden können, damit Sie mit ihnen interagieren können!

Zunächst führt Angular Ihre Anwendung in zwei Phasen aus - der configund run-Phase. Die configPhase, wie wir gesehen haben, ist , wo Sie alle Anbieter nach Bedarf einrichten. Hier werden auch Direktiven, Controller, Filter und dergleichen eingerichtet. In der runPhase, wie Sie vielleicht erraten haben, kompiliert Angular Ihr DOM und startet Ihre App.

Sie können zusätzlichen Code hinzufügen, der in diesen Phasen mit den Funktionen myMod.configund ausgeführt myMod.runwerden soll. Jede Funktion übernimmt eine Funktion, die während dieser bestimmten Phase ausgeführt werden soll. Wie wir im ersten Abschnitt gesehen haben, sind diese Funktionen injizierbar - wir haben den integrierten $provideService in unserem allerersten Codebeispiel injiziert . Bemerkenswert ist jedoch, dass während der configPhase nur Anbieter injiziert werden können (mit Ausnahme der Dienste im AUTOModul - $provideund $injector).

Folgendes ist beispielsweise nicht zulässig :

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

Was Sie tun haben Zugriff zu alle sind Anbieter für Dienstleistungen , die Sie gemacht haben:

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

Es gibt eine wichtige Ausnahme: constants dürfen, da sie nicht geändert werden können, in configBlöcke injiziert werden (so unterscheiden sie sich von values). Sie werden nur über ihren Namen aufgerufen (kein ProviderSuffix erforderlich).

Immer wenn Sie einen Anbieter für einen Dienst definiert haben, wird dieser Anbieter benannt serviceProvider, wobei serviceder Name des Dienstes lautet. Jetzt können wir die Macht der Anbieter nutzen, um etwas komplizierteres zu tun!

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

Jetzt haben wir eine Funktion auf unserem Provider setText, mit der wir unsere anpassen können alert. Wir können in einem configBlock auf diesen Anbieter zugreifen, um diese Methode aufzurufen und den Dienst anzupassen. Wenn wir unsere App endlich ausführen, können wir den greetingService nutzen und ausprobieren, um festzustellen, ob unsere Anpassung wirksam wurde.

Da dies ein komplexeres Beispiel ist, finden Sie hier eine funktionierende Demonstration: http://jsfiddle.net/BinaryMuse/9GjYg/

Controller ( $controller)

Controller-Funktionen können injiziert werden, aber Controller selbst können nicht in andere Dinge injiziert werden. Dies liegt daran, dass Controller nicht über den Anbieter erstellt werden. Stattdessen gibt es einen integrierten Angular-Dienst namens $controller, der für die Einrichtung Ihrer Controller verantwortlich ist. Wenn Sie anrufen myMod.controller(...), greifen Sie genau wie im letzten Abschnitt auf den Anbieter dieses Dienstes zu .

Wenn Sie beispielsweise einen Controller wie folgt definieren:

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

Was Sie tatsächlich tun, ist Folgendes:

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

Wenn Angular später eine Instanz Ihres Controllers erstellen muss, verwendet es den $controllerDienst (der wiederum den verwendet $injector, um Ihre Controller-Funktion aufzurufen, damit auch die Abhängigkeiten eingefügt werden).

Filter und Richtlinien

filterund directivearbeiten genauso wie controller; filterverwendet einen angerufenen Dienst $filterund seinen Anbieter $filterProvider, während directiveein genannter Dienst $compileund sein Anbieter verwendet werden $compileProvider. Einige Links:

Gemäß den anderen Beispielen myMod.filterund myMod.directivesind Verknüpfungen , um diese Dienste zu konfigurieren.


tl; dr

Zusammenfassend kann also jede Funktion, mit der aufgerufen wird $injector.invoke , injiziert werden . Dies beinhaltet aus Ihrem Diagramm (ist aber nicht beschränkt auf):

  • Regler
  • Richtlinie
  • Fabrik
  • Filter
  • Anbieter $get(beim Definieren des Anbieters als Objekt)
  • Provider-Funktion (wenn Provider als Konstruktorfunktion definiert wird)
  • Bedienung

Der Anbieter erstellt neue Dienste , die in Dinge eingefügt werden können . Das beinhaltet:

  • Konstante
  • Fabrik
  • Anbieter
  • Bedienung
  • Wert

Das sei gesagt, integrierte Dienste wie $controllerund $filter kann injiziert werden, und Sie können verwenden diejenigen Service halten , die neuen Filter zu bekommen und Controllern Sie mit diesen Methoden definiert (auch wenn die Dinge , die Sie definiert nicht, selbst, in der Lage sein , in Dinge injiziert).

Andere als die, jede Injektor-aufgerufene Funktion kann mit jedem Provider-Service bereitgestellt injiziert werden - es gibt keine Beschränkung (mit Ausnahme der configund runUnterschieden , die hierin aufgelistet).

Michelle Tilley
quelle
6
Beeindruckend! Vielen Dank, dass Sie sich die Zeit genommen haben, so ausführlich zu antworten! Ich habe das zweimal gelesen und ich glaube, ich habe einiges verstanden. Ich werde es und die Links studieren, die Sie später im Detail gegeben haben. Und noch +1 für die Katze. :)
user1527166
18
Eine der nützlichsten und detailliertesten SO-Antworten, die mir begegnet sind - danke!
Godders
11
Diese Antwort definiert eine neue Ebene von fantastisch. Beleuchtendes Zeug.
Ngure Nyaga
4
Mit Abstand die beste Ressource, die mir für AngularJS begegnet ist. Vielen Dank.
Code90
5
Buchstäblich das beste Stück AngularJS-Dokumentation, das ich je gesehen habe. Gut gemacht!
Iain Duncan
13

Der Punkt, den BinaryMuse in ihrer erstaunlichen Antwort darauf macht, dass Anbieter, Fabriken und Dienstleistungen alle dasselbe sind, ist äußerst wichtig.

Unten ist ein Bild, von dem ich denke, dass es ihren Standpunkt visuell veranschaulichen kann:

AngularJS sind alle nur Anbieter
(Quelle: simplygoodcode.com )

Luis Perez
quelle
7

Tolle Antwort von Michelle. Ich möchte nur darauf hinweisen, dass Direktiven injiziert werden können. Wenn Sie eine Direktive mit dem Namen haben myThing, können Sie sie einfügen myThingDirective: Hier ist ein erfundenes Beispiel .

Das obige Beispiel ist nicht sehr praktisch, jedoch ist die Möglichkeit, eine Direktive einzufügen, nützlich, wenn Sie diese Direktive dekorieren möchten .

Gil Birman
quelle
Es scheint, dass das zweite Beispiel zum Dekorieren dieser Direktive seit Angular 1.4 nicht funktioniert. (siehe Kommentar von Juan Biscaia dort)
Vadorequest