J'ai un peu de difficulté à comprendre l'injection de dépendance dans Angular. Ma question est donc la suivante: quelqu'un peut-il expliquer lequel des "types", tels que Contrôleur, Usine, Fournisseur, etc., pouvons-nous injecter dans d'autres, y compris d'autres instances du même "type"?
Ce que je recherche réellement, c’est cette table remplie de y/n. Pour les cellules ayant la même rangée/colonne, cela signifie d’injecter la valeur d’un "type" dans un autre avec le même "type"
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Can we inject? | Constant | Controller | Directive | Factory | Filter | Provider | Service | Value |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Constant | | | | | | | | |
| Controller | | | | | | | | |
| Directive | | | | | | | | |
| Factory | | | | | | | | |
| Filter | | | | | | | | |
| Provider | | | | | | | | |
| Service | | | | | | | | |
| Value | | | | | | | | |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
Plutôt que de simplement remplir le tableau avec "oui" et "non" sans explication, je vais entrer un peu plus dans les détails.
[Remarque, ajouté après avoir terminé: cela a fini par être ... un peu plus long que prévu. Il y a un tl; dr en bas, mais j'espère que cela sera informatif.]
[Cette réponse a également été ajoutée au wiki AngularJS: Comprendre l’injection de dépendance ]
$provide
)Le service $provide
Est chargé de dire à Angular comment créer de nouveaux objets injectables; ces choses s'appellent des services . Les services sont définis par des choses appelées fournisseurs , ce que vous créez lorsque vous utilisez $provide
. La définition d’un fournisseur s’effectue via la méthode provider
du service $provide
, Et vous pouvez obtenir le service $provide
En demandant qu’il soit injecté dans une application config
fonction. Un exemple pourrait être quelque chose comme ceci:
app.config(function($provide) {
$provide.provider('greeting', function() {
this.$get = function() {
return function(name) {
alert("Hello, " + name);
};
};
});
});
Ici, nous avons défini un nouveau fournisseur pour un service appelé greeting
; nous pouvons injecter une variable nommée greeting
dans n'importe quelle fonction injectable (comme les contrôleurs, nous en parlerons plus tard) et Angular appellera la fonction $get
du fournisseur afin de renvoyer un nouvelle instance du service. Dans ce cas, la chose qui sera injectée est une fonction qui prend un paramètre name
et alert
s un message basé sur le nom. Nous pourrions l'utiliser comme ceci:
app.controller('MainController', function($scope, greeting) {
$scope.onClick = function() {
greeting('Ford Prefect');
};
});
Maintenant, voici le truc. factory
, service
et value
sont tous juste raccourcis permettant de définir différentes parties d’un fournisseur - c’est-à-dire qu’ils permettent de définir un fournisseur sans avoir à taper tous ces éléments. Par exemple, vous pourriez écrire que le même fournisseur exact exactement comme ceci:
app.config(function($provide) {
$provide.factory('greeting', function() {
return function(name) {
alert("Hello, " + name);
};
});
});
Il est important de comprendre, je vais donc reformuler: sous le capot, AngularJS appelle le même code exact que celui que nous avons écrit ci-dessus (le $provide.provider
version) pour nous. Il n'y a littéralement 100% pas de différence entre les deux versions. value
fonctionne exactement de la même manière - si tout ce que nous renverrions de notre fonction $get
(alias notre fonction factory
est toujours identique, nous pouvons écrire même moins de code en utilisant value
. Par exemple, puisque nous renvoyons toujours la même fonction pour notre service greeting
, nous pouvons également utiliser value
:
app.config(function($provide) {
$provide.value('greeting', function(name) {
alert("Hello, " + name);
});
});
Encore une fois, c'est 100% identique aux deux autres méthodes que nous avons utilisées pour définir cette fonction - c'est juste un moyen de sauvegarder une frappe.
Maintenant, vous avez probablement remarqué cette chose ennuyante que j’utilise app.config(function($provide) { ... })
. Puisque la définition de nouveaux fournisseurs (via toute des méthodes données ci-dessus) est si commune, AngularJS expose les méthodes $provider
Directement sur l'objet module, pour économiser encore plus de frappe. :
var myMod = angular.module('myModule', []);
myMod.provider("greeting", ...);
myMod.factory("greeting", ...);
myMod.value("greeting", ...);
Celles-ci font toutes la même chose que les versions plus verbeuses app.config(...)
que nous avons utilisées précédemment.
L'injectable que j'ai sauté jusqu'à présent est constant
. Pour le moment, il est assez facile de dire que cela fonctionne comme value
. Nous verrons qu'il y a une différence plus tard.
Pour réviser , tous ces morceaux de code font exactement le ( même chose:
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);
});
$injector
)L’injecteur est responsable de la création des instances de nos services à l’aide du code fourni via $provide
(Sans jeu de mots). Chaque fois que vous écrivez une fonction qui prend des arguments injectés, vous voyez l'injecteur au travail. Chaque application AngularJS a un seul $injector
Qui est créé lors du premier démarrage de l'application; vous pouvez l'obtenir en injectant $injector
dans n'importe quelle fonction injectable (oui, $injector
sait comment s'injecter!)
Une fois que vous avez $injector
, Vous pouvez obtenir une instance d'un service défini en appelant get
avec le nom du service. Par exemple,
var greeting = $injector.get('greeting');
greeting('Ford Prefect');
L'injecteur est également responsable de l'injection de services dans les fonctions; Par exemple, vous pouvez magiquement injecter des services dans n'importe quelle fonction utilisant la méthode invoke
de l'injecteur;
var myFunction = function(greeting) {
greeting('Ford Prefect');
};
$injector.invoke(myFunction);
Il est à noter que l'injecteur ne créera qu'une seule fois l'instance d'un service . Il met ensuite en cache tout ce que le fournisseur renvoie par le nom du service; la prochaine fois que vous demanderez le service, vous obtiendrez exactement le même objet.
Donc, pour répondre à votre question, vous pouvez injecter des services dans toute fonction appelée avec $injector.invoke
. Ceci comprend
$get
des fournisseurs (aussi les fonctions de définition factory
)Etant donné que constant
s et value
s retournent toujours une valeur statique, ils ne sont pas appelés via l'injecteur, vous ne pouvez donc pas les injecter.
Vous vous demandez peut-être pourquoi quiconque se donnerait la peine de mettre en place un fournisseur à part entière avec la méthode provide
si factory
, value
, etc. sont tellement plus faciles. La réponse est que les fournisseurs permettent beaucoup de configuration. Nous avons déjà mentionné que lorsque vous créez un service via le fournisseur (ou l'un des raccourcis que vous donne Angular), vous créez un nouveau fournisseur qui définit la structure de ce service. Ce que je n'ai pas mentionné est que ces fournisseurs peuvent être injectés dans les sections config
de votre application afin que vous puissiez interagir avec eux!
Tout d'abord, Angular exécute votre application en deux phases: les phases config
et run
. Comme nous l’avons vu précédemment, la phase config
vous permet de configurer n’importe quel fournisseur. C'est également à cet endroit que les directives, les contrôleurs, les filtres, etc. sont configurés. La phase run
, comme vous pouvez le deviner, est l'endroit où Angular compile votre DOM et démarre votre application.
Vous pouvez ajouter du code supplémentaire à exécuter dans ces phases avec les fonctions myMod.config
Et myMod.run
- chacune prend une fonction à exécuter pendant cette phase spécifique. Comme nous l'avons vu dans la première section, ces fonctions sont injectables. Nous avons injecté le service intégré $provide
Dans notre tout premier exemple de code. Cependant, il convient de noter que au cours de la phase config
, seuls les fournisseurs peuvent être injectés (à l'exception des services de la AUTO
module - $provide
et $injector
).
Par exemple, ce qui suit n’est pas autorisé :
myMod.config(function(greeting) {
// WON'T WORK -- greeting is an *instance* of a service.
// Only providers for services can be injected in config blocks.
});
Ce que vous avez accès à n’importe quel fournisseur pour les services que vous avez rendus:
myMod.config(function(greetingProvider) {
// a-ok!
});
Il existe une exception importante: constant
s, étant donné qu'ils ne peuvent pas être modifiés, sont autorisés à être injectés à l'intérieur de blocs config
(c'est ainsi qu'ils diffèrent de value
s). Ils sont accessibles uniquement par leur nom (pas de suffixe Provider
nécessaire).
Chaque fois que vous définissez un fournisseur pour un service, ce fournisseur est nommé serviceProvider
, où service
est le nom du service. Maintenant, nous pouvons utiliser le pouvoir des fournisseurs pour faire des choses plus compliquées!
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');
});
Nous avons maintenant une fonction sur notre fournisseur appelée setText
que nous pouvons utiliser pour personnaliser notre alert
; nous pouvons avoir accès à ce fournisseur dans un bloc config
pour appeler cette méthode et personnaliser le service. Lorsque nous exécutons enfin notre application, nous pouvons récupérer le service greeting
et l'essayer pour vérifier que notre personnalisation a pris effet.
Comme il s’agit d’un exemple plus complexe, voici une démonstration pratique: http://jsfiddle.net/BinaryMuse/9GjYg/
$controller
)Les fonctions de contrôleur peuvent être injectées dans, mais les contrôleurs eux-mêmes ne peuvent pas être injectés dans d'autres choses. En effet, les contrôleurs ne sont pas créés via le fournisseur. Au lieu de cela, il existe un service intégré Angular appelé $controller
Qui est responsable de la configuration de vos contrôleurs. Lorsque vous appelez myMod.controller(...)
, vous accédez en fait à le fournisseur de ce service , comme dans la dernière section.
Par exemple, lorsque vous définissez un contrôleur comme celui-ci:
myMod.controller('MainController', function($scope) {
// ...
});
Voici ce que vous êtes en train de faire:
myMod.config(function($controllerProvider) {
$controllerProvider.register('MainController', function($scope) {
// ...
});
});
Plus tard, lorsque Angular doit créer une instance de votre contrôleur, il utilise le service $controller
(Qui à son tour utilise le $injector
Pour appeler votre fonction de contrôleur afin qu'elle obtienne son dépendances injectées aussi).
filter
et directive
fonctionnent exactement de la même manière que controller
; filter
utilise un service appelé $filter
et son fournisseur $filterProvider
, tandis que directive
utilise un service appelé $compile
et son fournisseur $compileProvider
. Quelques liens:
Comme dans les autres exemples, myMod.filter
Et myMod.directive
Sont des raccourcis pour la configuration de ces services.
Donc, pour résumer, toute fonction appelée avec $injector.invoke
peut être injectée dans . Cela inclut, à partir de votre tableau (sans toutefois s'y limiter):
$get
(lors de la définition du fournisseur en tant qu'objet)Le fournisseur crée de nouveaux services pouvant être injectés dans des objets . Ceci comprend:
Cela dit, des services intégrés tels que $controller
Et $filter
peuvent être injectés et vous pouvez utiliser ces services pour obtenir les nouveaux filtres et contrôleurs que vous avez définis avec ces méthodes (même si les éléments que vous avez définis ne peuvent pas, par eux-mêmes, être injectés dans des éléments).
En dehors de cela, toute fonction invoquée par l'injecteur peut être injectée avec n'importe quel service fourni par le fournisseur - il n'y a pas de restriction (autre que les différences config
et run
énumérées ici).
La remarque de BinaryMuse dans sa réponse étonnante sur les fournisseurs, les usines et les services est extrêmement importante.
Ci-dessous, une image qui, à mon avis, peut illustrer son propos de manière visuelle:
Grande réponse par Michelle. Je veux juste souligner que des directives peuvent être injectées. Si vous avez une directive nommée myThing
, vous pouvez l'injecter avec myThingDirective
: voici un exemple artificiel .
L'exemple ci-dessus n'est pas très pratique, cependant, la possibilité d'injecter une directive est utile lorsque vous voulez décorer cette directive .