Plutôt que de remplir le tableau avec «oui» et «non» sans aucune explication, je vais entrer un peu plus en détail.
[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: Understanding Dependency Injection ]
Le fournisseur ( $provide
)
Le $provide
service est chargé de dire à Angular comment créer de nouvelles choses injectables; ces choses s'appellent des services . Les services sont définis par des éléments appelés fournisseurs , ce que vous créez lorsque vous utilisez $provide
. La définition d'un fournisseur se fait via la provider
méthode sur le $provide
service, et vous pouvez mettre la main sur le $provide
service en demandant qu'il soit injecté dans la config
fonction d' une application . 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, plus à ce sujet plus tard) et Angular appellera la $get
fonction du fournisseur afin de renvoyer une nouvelle instance du service. Dans ce cas, la chose qui sera injectée est une fonction qui prend un name
paramètre et alert
un message basé sur le nom. Nous pourrions l'utiliser comme ceci:
app.controller('MainController', function($scope, greeting) {
$scope.onClick = function() {
greeting('Ford Prefect');
};
});
Voici maintenant le truc. factory
, service
et ne value
sont que des raccourcis pour définir diverses parties d'un fournisseur - c'est-à-dire qu'ils fournissent un moyen de définir un fournisseur sans avoir à taper tout cela. Par exemple, vous pouvez écrire exactement le même fournisseur comme ceci:
app.config(function($provide) {
$provide.factory('greeting', function() {
return function(name) {
alert("Hello, " + name);
};
});
});
Il est important de comprendre, donc je vais reformuler: sous le capot, AngularJS appelle exactement le même code que nous avons écrit ci-dessus (la $provide.provider
version) pour nous. Il n'y a littéralement aucune différence entre les deux versions. value
fonctionne exactement de la même manière - si tout ce que nous retournerions de notre $get
fonction (c'est-à-dire notre factory
fonction) est toujours exactement le même, nous pouvons écrire encore moins de code en utilisant value
. Par exemple, puisque nous retournons toujours la même fonction pour notre greeting
service, nous pouvons également l'utiliser value
pour la définir:
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 un peu de frappe.
Maintenant, vous avez probablement remarqué cette app.config(function($provide) { ... })
chose ennuyeuse que j'utilise. Puisque la définition de nouveaux fournisseurs (via l' une des méthodes ci-dessus) est si courante, AngularJS expose les $provider
méthodes 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", ...);
Ceux-ci font tous la même chose que les app.config(...)
versions plus verbeuses que nous avons utilisées précédemment.
Le seul injectable que j'ai sauté jusqu'à présent est constant
. Pour l'instant, il est assez facile de dire que cela fonctionne exactement comme value
. Nous verrons qu'il y a une différence plus tard.
Pour revoir , tous ces morceaux de code font exactement la 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);
});
L'injecteur est responsable de la création des instances de nos services en utilisant le code que nous avons 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 en a une $injector
qui est créée au premier démarrage de l'application; vous pouvez vous en emparer $injector
en l' injectant dans n'importe quelle fonction injectable (oui, $injector
sait comment s'injecter!)
Une fois que vous l'avez fait $injector
, vous pouvez obtenir une instance d'un service défini en l'appelant get
avec le nom du service. Par exemple,
var greeting = $injector.get('greeting');
greeting('Ford Prefect');
L'injecteur est également chargé d'injecter des services dans les fonctions; par exemple, vous pouvez par magie injecter des services dans n'importe quelle fonction que vous avez en utilisant la invoke
méthode de l'injecteur ;
var myFunction = function(greeting) {
greeting('Ford Prefect');
};
$injector.invoke(myFunction);
Cela vaut la peine de noter que l'injecteur ne créer une instance d'un service une fois . 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 n'importe quelle fonction appelée avec$injector.invoke
. Ceci comprend
- fonctions de définition du contrôleur
- fonctions de définition de directive
- fonctions de définition de filtre
- les
$get
méthodes des fournisseurs (aka les factory
fonctions de définition)
Puisque constant
s et value
s renvoient toujours une valeur statique, ils ne sont pas appelés via l'injecteur et vous ne pouvez donc pas leur injecter quoi que ce soit.
Configuration des fournisseurs
Vous demandez peut - être pourquoi quelqu'un prendrait la peine de mettre en place un fournisseur à part entière avec la provide
méthode si factory
, value
etc. sont tellement plus facile. La réponse est que les fournisseurs autorisent beaucoup de configuration. Nous avons déjà mentionné que lorsque vous créez un service via le fournisseur (ou l'un des raccourcis fournis par Angular), vous créez un nouveau fournisseur qui définit la manière dont ce service est construit. Ce que je n'ai pas mentionné, c'est que ces fournisseurs peuvent être injectés dans des config
sections 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
. La config
phase, comme nous l'avons vu, est celle où vous pouvez configurer tous les fournisseurs si nécessaire. C'est également là que les directives, les contrôleurs, les filtres et autres sont configurés. La run
phase, comme vous pouvez le deviner, est celle où Angular compile réellement 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 pendant la config
phase, seuls les fournisseurs peuvent être injectés (à l'exception des services du 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 n'avez accès à tous sont les fournisseurs de services que vous avez fait:
myMod.config(function(greetingProvider) {
// a-ok!
});
Il y a une exception importante: les constant
s, puisqu'ils ne peuvent pas être modifiés, peuvent être injectés à l'intérieur de config
blocs (c'est ainsi qu'ils diffèrent de value
s). Ils sont accessibles uniquement par leur nom (pas de Provider
suffixe nécessaire).
Chaque fois que vous avez défini un fournisseur pour un service, ce fournisseur est nommé serviceProvider
, où service
est le nom du service. Maintenant, nous pouvons utiliser la puissance 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');
});
Maintenant, nous avons une fonction sur notre fournisseur appelée setText
que nous pouvons utiliser pour personnaliser notre alert
; nous pouvons accéder à ce fournisseur dans un config
bloc pour appeler cette méthode et personnaliser le service. Lorsque nous exécutons enfin notre application, nous pouvons récupérer le greeting
service et l'essayer pour voir que notre personnalisation a pris effet.
Puisqu'il s'agit d'un exemple plus complexe, voici une démonstration de travail: http://jsfiddle.net/BinaryMuse/9GjYg/
Les fonctions du contrôleur peuvent être injectées dans, mais les contrôleurs eux-mêmes ne peuvent pas être injectés dans d'autres choses. C'est parce que les contrôleurs ne sont pas créés via le fournisseur. Au lieu de cela, il existe un service Angular intégré appelé $controller
qui est responsable de la configuration de vos contrôleurs. Lorsque vous appelez myMod.controller(...)
, vous accédez en fait au 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 faites réellement:
myMod.config(function($controllerProvider) {
$controllerProvider.register('MainController', function($scope) {
// ...
});
});
Plus tard, quand Angular a besoin de créer une instance de votre contrôleur, il utilise le $controller
service (qui à son tour utilise le $injector
pour appeler votre fonction de contrôleur afin qu'il obtienne également ses dépendances injectées).
Filtres et directives
filter
et directive
fonctionnent exactement de la même manière que controller
; filter
utilise un service appelé $filter
et son fournisseur $filterProvider
, tandis directive
qu'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 configurer ces services.
Donc, pour résumer, toute fonction appelée avec $injector.invoke
peut être injectée dans . Cela inclut, à partir de votre graphique (mais sans s'y limiter):
- manette
- directif
- usine
- filtre
- fournisseur
$get
(lors de la définition du fournisseur comme objet)
- fonction de fournisseur (lors de la définition du fournisseur comme fonction de constructeur)
- un service
Le fournisseur crée de nouveaux services qui peuvent être injectés dans les choses . Ceci comprend:
- constant
- usine
- fournisseur
- un service
- valeur
Cela dit, les services intégrés aiment $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 sont pas, par eux-mêmes, capables d'être injecté dans les choses).
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 aucune restriction (autre que les config
et les run
différences énumérées ici).