web-dev-qa-db-fra.com

Quelles "choses" peuvent être injectées dans d'autres dans Angular.js?

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          |          |            |           |         |        |          |         |       |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
139
user1527166

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 ]


Le fournisseur ( $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 alerts 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);
});

L'injecteur ( $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

  • fonctions de définition du contrôleur
  • fonctions de définition de directive
  • fonctions de définition de filtre
  • les méthodes $get des fournisseurs (aussi les fonctions de définition factory)

Etant donné que constants et values retournent toujours une valeur statique, ils ne sont pas appelés via l'injecteur, vous ne pouvez donc pas les injecter.

Configuration des fournisseurs

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: constants, é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 values). 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/

Contrôleurs ( $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).

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 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.


tl;dr

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):

  • manette
  • directif
  • usine
  • filtre
  • fournisseur $get (lors de la définition du fournisseur en tant qu'objet)
  • fonction provider (lors de la définition du fournisseur en tant que fonction constructeur)
  • un service

Le fournisseur crée de nouveaux services pouvant être injectés dans des objets . Ceci comprend:

  • constant
  • usine
  • fournisseur
  • un service
  • valeur

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).

388
Michelle Tilley

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:

AngularJS ne sont que des fournisseurs http://www.simplygoodcode.com/wp-content/uploads/2015/11/angularjs-provider-service-factory-highlight.png

13
Luis Perez

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 .

7
Gil Birman