Puis-je injecter un service dans une directive dans AngularJS?

234

J'essaie d'injecter un service dans une directive comme ci-dessous:

 var app = angular.module('app',[]);
 app.factory('myData', function(){
     return {
        name : "myName"
     }
 });
 app.directive('changeIt',function($compile, myData){
    return {
            restrict: 'C',
            link: function (scope, element, attrs) {
                scope.name = myData.name;
            }
        }
 });

Mais cela me renvoie une erreur Unknown provider: myDataProvider. Est-ce que quelqu'un pourrait regarder le code et me dire si je fais quelque chose de mal?

Exception
la source

Réponses:

388

Vous pouvez faire de l'injection sur des directives, et cela ressemble à ce qu'il fait partout ailleurs.

app.directive('changeIt', ['myData', function(myData){
    return {
        restrict: 'C',
        link: function (scope, element, attrs) {
            scope.name = myData.name;
        }
    }
 }]);
grendian
la source
13
Je pense que c'est une meilleure solution car cela fonctionne même après avoir réduit votre code.
czerasz
5
J'ai dû ajouter '_myData = myData' avant le retour {}, puis référencer l'objet en tant que _myData à l'intérieur de la fonction de lien.
Jelling
Merci @Jelling. J'ai dû faire la même chose. Je me demande si quelqu'un pourrait nous dire pourquoi ...?
sfletche
6
une raison particulière pour injecter $ compile dans la directive? il ne semble être utilisé nulle part.
gru
4
Existe-t-il une solution à l'injection si vous souhaitez créer la fonction de liaison en dehors de l'appel de directive?
ThinkBonobo
19

Modifiez votre définition de directive de app.moduleà app.directive. A part ça, tout va bien. Au fait, vous devez très rarement injecter un service dans une directive. Si vous injectez un service (qui est généralement une source de données ou un modèle) dans votre directive (qui fait en quelque sorte partie d'une vue), vous créez un couplage direct entre votre vue et votre modèle. Vous devez les séparer en les câblant ensemble à l'aide d'un contrôleur.

Cela fonctionne bien. Je ne sais pas ce que vous faites qui est mal. Voici un morceau de cela fonctionne.

http://plnkr.co/edit/M8omDEjvPvBtrBHM84Am

ganaraj
la source
Pouvez-vous fournir un exemple s'il vous plaît
Exception
@Exception Pouvez-vous mettre votre code dans un violon? Je peux jeter un œil et voir pourquoi votre code ne fonctionne pas et probablement vous aider à le corriger.
ganaraj
@Exception a ajouté un plunk fonctionnel qui montre que le code fonctionne.
ganaraj
3
Je viens de découvrir quelque chose: si vous définissez une injection dans les paramètres de la fonction, function($location) { ...mais ne vous référez pas réellement à l' $locationintérieur de la fonction, AngularJS n'effectuera pas l'injection. La seule fois où vous remarquerez ce comportement est dans le débogueur.
Walter Stabosz
13
Je ne suis pas sûr d'être d'accord avec votre commentaire "couplé". Nous couplons déjà le contrôleur et le service à l'échelle mondiale - nous ne pouvons pas remplacer par programmation l'implémentation du service au moment de l'exécution. Ce qui signifie qu'un seul contrôleur obtient un seul service. Cependant - les directives ont une configuration isolée par balise sur la page, donc nous pouvons potentiellement activer un service différent pour différentes instances de directive. Il me semble que c'est moins découplé.
guy mograbi
11

Vous pouvez également utiliser le service $ inject pour obtenir le service que vous souhaitez. Je trouve cela utile si je ne connais pas le nom du service à l'avance mais que je connais l'interface du service. Par exemple, une directive qui connectera une table à un point de terminaison ngResource ou à un bouton générique de suppression d'enregistrement qui interagit avec n'importe quel point de terminaison api. Vous ne voulez pas réimplémenter la directive table pour chaque contrôleur ou source de données.

template.html

<div my-directive api-service='ServiceName'></div>

my-directive.directive.coffee

angular.module 'my.module'
  .factory 'myDirective', ($injector) ->
    directive = 
      restrict: 'A'
      link: (scope, element, attributes) ->
        scope.apiService = $injector.get(attributes.apiService)

maintenant, votre service «anonyme» est entièrement disponible. Si c'est ngResource par exemple, vous pouvez alors utiliser l'interface standard ngResource pour obtenir vos données

Par exemple:

scope.apiService.query((response) ->
  scope.data = response
, (errorResponse) ->
  console.log "ERROR fetching data for service: #{attributes.apiService}"
  console.log errorResponse.data
)

J'ai trouvé cette technique très utile lors de la création d'éléments qui interagissent avec les points de terminaison API en particulier.

Tyrone Wilson
la source