La directive angularjs doit-elle interagir directement avec les services ou est-elle considérée comme un anti-modèle?

35

Lequel est considéré comme meilleur:

  • avoir une directive qui interagit directement avec les services

ou

  • avoir une directive exposant certains points d'ancrage à quel contrôleur peut lier un comportement (impliquant des services)?
WTK
la source
J'aurais besoin d'un peu plus de contexte sur ce que vous voulez réaliser, ce qui est communiqué, combien de code source est à prévoir, quel est votre domaine, comment doit-il évoluer?
Utilisateur
C'est une directive responsable du rendu d'un widget de commentaire - elle affiche le champ de commentaire, avec les boutons d'envoi / d'annulation. Cette directive est supposée être utilisée dans un seul contexte - commenter "document". La manière dont il est actuellement géré par le contrôleur expose les fonctions permettant de créer un commentaire réel (instance injectée du service de commentaires du contrôleur get). L'autre façon de le faire consiste à encapsuler le tout (ainsi que le traitement des erreurs / succès) dans une directive (une directive recevrait un service de commentaires injecté).
WTK

Réponses:

24

En règle générale, une directive est préférable lorsqu'elle est courte (en termes de code), (potentiellement) réutilisable et qu'elle a une portée limitée en termes de fonctionnalités. Faire une directive qui inclut l'interface utilisateur et dépend d'un service (qui, je suppose, gère la connexion au backend), ne lui donne pas seulement 2 rôles fonctionnels, à savoir:

  • Contrôle de l'interface utilisateur pour l'affichage / la saisie des données pour le widget.
  • Envoi au backend (via le service).

mais aussi en le rendant moins réutilisable, car vous ne pourrez plus l'utiliser avec un autre service, ou avec une interface utilisateur différente (du moins, pas facilement).

Lorsque je prends ces décisions, je compare souvent les éléments HTML intégrés: par exemple <input>, <textarea>ou <form>: ils sont complètement indépendants d’un backend spécifique. HTML5 a donné à l' <input>élément quelques types supplémentaires, par exemple date, qui sont toujours indépendants du backend et où se trouvent exactement les données ou comment elles sont utilisées. Ils sont purement des éléments d'interface. Je pense que vos widgets personnalisés, construits en utilisant des directives, devraient suivre le même schéma, si possible.

Cependant, ce n'est pas la fin de l'histoire. Au-delà de l'analogie avec les éléments HTML intégrés, vous pouvez créer des directives réutilisables appelant des services et utiliser une directive purement interface utilisateur, comme si elle utilisait a <textarea>. Supposons que vous souhaitiez utiliser du code HTML comme suit:

<document document-url="'documents/3345.html'">
 <document-data></document-data>
 <comments></comments>
 <comment-entry></comment-entry>
</document>

Pour coder la commentEntrydirective, vous pouvez créer une très petite directive qui ne contient que le contrôleur qui relie un service à un widget UI. Quelque chose comme:

app.directive('commentEntry', function (myService) {
  return {
    restrict: 'E',
    template: '<comment-widget on-save="save(data)" on-cancel="cancel()"></comment-widget>',
    require: '^document',
    link: function (scope, iElement, iAttrs, documentController) {
      // Allow the controller here to access the document controller
      scope.documentController = documentController;
    },
    controller: function ($scope) {
      $scope.save = function (data) {
        // Assuming the document controller exposes a function "getUrl"
        var url = $scope.documentController.getUrl(); 

        myService.saveComments(url, data).then(function (result) {
          // Do something
        });
      };
    }
  };
});

À la limite, vous n'aurez peut-être jamais besoin d'un ng-controllerattribut manuel dans le code HTML: vous pouvez tout faire à l'aide de directives, à condition que chacun ait directement un rôle "d'interface utilisateur" ou un rôle "de données" clair.

Il y a un inconvénient que je devrais mentionner: cela donne plus de "pièces mobiles" à l'application, ce qui ajoute un peu de complexité. Cependant, si chaque partie a un rôle clair et est bien (unité + E2E testée), je dirais que cela en vaut la peine et représente un avantage global à long terme.

Michal Charemza
la source
59

Permettez-moi d'être en désaccord avec la réponse de Michal Charemza.

Bien que sa réponse soit théoriquement correcte, elle n’est pas très pratique dans le monde réel.

Je dis cela parce que j’avais l'habitude de penser comme ça et d'essayer de l'appliquer à une grande application du monde réel que moi-même et mon équipe développons et que cela est devenu trop gênant.

L'analogie avec le langage HTML n'est pas bonne, car vous ne devriez pas vous efforcer de créer des directives d'usage général extrêmement réutilisables, car vous ne construisez pas une application générique comme un navigateur Web.

Vous devez plutôt utiliser les directives pour créer un langage DSL (Domain Specific Language) pour votre application, qui réside sur son propre domaine.

Cela ne signifie pas que toutes les directives ne doivent pas être génériques. Certains pourraient être, si c'est dans leur nature. Si vous construisez un sélecteur de date personnalisé, rendez-le générique et réutilisable dans toutes les applications.

Mais si vous construisez quelque chose comme une boîte de connexion qui se lie à votre back-end, faites-le.

La seule règle empirique devrait être la suivante: ne dupliquez jamais le code (fragments abstraits à des usines et des services) et faites-le tester via l'injection de dépendance. Heureusement, avec Angular, ce sont des gâteaux.

Rester simple. :)

Dema
la source
5
Bons points Dema - bien que j'aie accepté la réponse de Michal, je suis d'accord avec votre approche, il ne faut pas sauter d'obstacles pour créer quelque chose de réutilisable rien que pour le plaisir de le faire. C’était vraiment mon instinct initial de lier le service à la directive, parce que cela avait du sens, et non pas parce que c’était la façon dont le gourou angularjs le ferait ou ne le ferait pas. En fin de compte, j'ai créé une directive avec un service injecté directement dans celle-ci, et en tant qu'API publique, je fournis un point d'ancrage pour un rappel qui est déclenché après la création de commentaires.
WTK
2

Je pense que la question "une directive doit-elle interagir avec un service" dépend de ce que fait votre service.

J'ai vu des directives interagir avec des services qui ne font rien avec les requêtes HTTP et je pense que c'est un bon modèle. Les services / usines sont parfaits pour encapsuler plus de logique orientée données, et les directives sont parfaits pour encapsuler une logique orientée présentation. L'objectif déclaré des services dans la documentation angulaire est le suivant: "Vous pouvez utiliser des services pour organiser et partager du code dans votre application.". C'est assez large, mais les services peuvent être utilisés pour atteindre cet objectif dans les directives.

Cela étant dit, je comprends dans certains cas le désir de faire en sorte que les directives ne fassent pas directement de requêtes HTTP. Encore une fois, cela dépend du service et de la façon dont vous organisez vos services.

coke
la source
1

Selon le framework AngularJS, nous devrions singleton des usines / services pour obtenir toutes les données du serveur. Pour que ces usines puissent être réutilisées d'une application à l'autre sans avoir à réécrire la même. Bien à l'intérieur de la directive, nous pouvons appeler ces usines pour récupérer les données depuis Api / server.

Basavaraj Kabuure
la source