Sollte die anglejs-Direktive direkt mit Diensten interagieren oder wird sie als Anti-Pattern angesehen?

35

Welches gilt als besser:

  • mit einer Direktive, die direkt mit Diensten interagiert

oder

  • Haben Sie eine Direktive, die bestimmte Hooks anzeigt, an die der Controller das Verhalten binden kann (was Dienste betrifft)?
WTK
quelle
Ich würde ein bisschen mehr Kontext brauchen von dem, was Sie erreichen wollen, was kommuniziert wird, wie viel Quellcode zu erwarten ist, was Ihre Domain ist, wie muss sie skaliert werden?
Benutzer
Dies ist eine Anweisung, die für das Rendern eines Kommentar-Widgets verantwortlich ist. Sie zeigt das Kommentarfeld zusammen mit den Schaltflächen zum Senden / Abbrechen an. Es wird davon ausgegangen, dass diese Direktive nur in einem Kontext verwendet wird - dem Kommentieren von "document". Die Art und Weise, wie der Controller derzeit arbeitet, macht Funktionen zum Erstellen eines aktuellen Kommentars verfügbar (Controller erhält die injizierte Instanz des Kommentarservices). Die andere Möglichkeit besteht darin, das Ganze (zusammen mit der Fehler- / Erfolgsbehandlung) in einer Direktive zu kapseln (Direktive würde Kommentardienst injiziert bekommen).
WTK

Antworten:

24

Eine Direktive ist (als Faustregel) am besten geeignet, wenn sie kurz (in Bezug auf den Code) ist, (möglicherweise) wiederverwendbar ist und einen begrenzten Funktionsumfang aufweist. Wenn Sie eine Direktive erstellen, die die Benutzeroberfläche enthält und von einem Dienst abhängt (von dem ich annehme, dass er die Verbindung zum Backend herstellt), werden nicht nur zwei Funktionsrollen zugewiesen, nämlich:

  • Steuerung der Benutzeroberfläche für die Anzeige / Eingabe von Daten für das Widget.
  • Übermittlung an das Backend (über den Service).

Sie können es aber auch weniger wiederverwenden, da Sie es dann nicht mehr mit einem anderen Dienst oder mit einer anderen Benutzeroberfläche verwenden können (zumindest nicht so einfach).

Wenn diese Entscheidungen zu treffen, vergleiche ich oft auf den integrierten HTML - Elementen: zum Beispiel <input>, <textarea>oder <form>: sie sind völlig unabhängig von einer bestimmten Backend. HTML5 hat dem <input>Element einige zusätzliche Typen gegeben, z. B. date, die immer noch unabhängig vom Backend sind und wo genau die Daten gespeichert sind oder wie sie verwendet werden. Sie sind reine Schnittstellenelemente. Ihre benutzerdefinierten Widgets, die mithilfe von Direktiven erstellt wurden, sollten nach Möglichkeit dem gleichen Muster folgen.

Dies ist jedoch nicht das Ende der Geschichte. Über die Analogie mit den integrierten HTML-Elementen hinaus können Sie wiederverwendbare Anweisungen erstellen, die sowohl Dienste aufrufen als auch eine reine Benutzeroberflächenanweisung verwenden, genau wie sie möglicherweise a verwendet <textarea>. Angenommen, Sie möchten HTML wie folgt verwenden:

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

Um die commentEntryDirektive zu kodieren , können Sie eine sehr kleine Direktive erstellen, die nur den Controller enthält, der einen Service mit einem UI-Widget verbindet. So etwas wie:

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
        });
      };
    }
  };
});

Im Extremfall müssen Sie möglicherweise nie ein manuelles ng-controllerAttribut im HTML haben: Sie können dies alles mithilfe von Direktiven tun, sofern jede direkt eine eindeutige "UI" -Rolle oder eine eindeutige "Daten" -Rolle hat.

Es gibt einen Nachteil, den ich erwähnen sollte: Es gibt der Anwendung mehr "bewegliche Teile", was ein bisschen Komplexität hinzufügt. Wenn jedoch jedes Teil eine klare Rolle spielt und gut ist (Einheit + E2E getestet), würde ich argumentieren, dass es sich lohnt und langfristig einen Gesamtnutzen darstellt.

Michal Charemza
quelle
59

Gestatten Sie mir, der Antwort von Michal Charemza nicht zuzustimmen.

Obwohl seine Antwort theoretisch richtig ist, ist sie für die reale Welt nicht sehr praktisch.

Ich sage das, weil ich früher so gedacht und versucht habe, es in einer großen realen App durchzusetzen, die ich und mein Team gerade aufbauen, und es wurde einfach zu mühsam.

Die Analogie zur HTML-Sprache ist nicht gut, da Sie sich nicht bemühen sollten, universelle, extrem wiederverwendbare Anweisungen zu erstellen, da Sie keine generische Anwendung wie einen Webbrowser erstellen.

Verwenden Sie stattdessen die Anweisungen, um eine domänenspezifische Sprache (Domain Specific Language, DSL) für Ihre App zu erstellen, die auf einer eigenen Domäne lebt.

Das bedeutet nicht, dass alle Richtlinien nicht generisch sein sollten. Einige könnten es sein, wenn es in ihrer Natur liegt. Wenn Sie eine benutzerdefinierte Datumsauswahl erstellen, sollten Sie diese generisch und für alle Apps wiederverwendbar machen.

Aber wenn Sie so etwas wie eine Login-Box erstellen, die an Ihr Back-End gebunden ist, tun Sie es einfach.

Die einzige Faustregel sollte lauten: Vervielfältige niemals Code (abstrakte kleine Teile von Fabriken und Diensten) und mache ihn durch Abhängigkeitsinjektion testbar. Glücklicherweise sind diese mit Angular ein Kinderspiel.

Halte es einfach. :)

Dema
quelle
5
Gute Punkte Dema - obwohl ich Michals Antwort akzeptierte, stimme ich Ihrem Ansatz zu, dass wir nicht in die Knie gehen sollten, um etwas nur deswegen wieder verwendbar zu machen. Das war wirklich mein ursprünglicher Instinkt, den Dienst an die Direktive zu binden, weil es Sinn machte, nicht weil es so war, wie winklige Gurus es tun oder nicht tun würden. Am Ende habe ich eine Direktive erstellt, in die der Dienst direkt eingefügt wurde, und als öffentliche API stelle ich einen Hook für einen Rückruf bereit, der ausgelöst wird, nachdem tatsächlich Kommentare erstellt wurden.
WTK
2

Ich denke, die Frage, ob eine Direktive mit einem Dienst interagieren soll, hängt davon ab, was Ihr Dienst tut.

Ich habe Direktiven mit Diensten interagieren lassen, die mit HTTP-Anfragen nichts zu tun haben, und ich denke, das ist ein gutes Muster. Services / Factories eignen sich hervorragend zur Verkapselung von mehr datenorientierter Logik, und Direktiven eignen sich hervorragend zur Verkapselung von darstellungsorientierter Logik. Der in den Angular-Dokumenten angegebene Zweck von Diensten lautet: "Sie können Dienste zum Organisieren und Freigeben von Code in Ihrer App verwenden." Das ist ziemlich weit gefasst, aber Services können verwendet werden, um dieses Ziel in Direktiven zu erreichen.

Abgesehen davon verstehe ich den Wunsch, es in einigen Fällen so zu machen, dass Direktiven keine HTTP-Anfragen direkt stellen. Auch hier kommt es auf den Dienst an und darauf, wie Sie Ihre Dienste organisieren.

ccnokes
quelle
1

Gemäß AngularJS-Framework sollten wir einzelne Fabriken / Services zum Abrufen von Daten vom Server verwenden. Damit diese Fabriken innerhalb einer Anwendung wiederverwendet werden können, ohne sie erneut zu schreiben. Nun, innerhalb der Direktive können wir diese Fabriken aufrufen, um Daten von Api / Server abzurufen.

Basavaraj Kabuure
quelle