Должна ли директива angularjs напрямую взаимодействовать со службами или она считается анти-паттерном?

35

Какой из них считается лучше:

  • имея директиву, которая напрямую взаимодействует со службами

или

  • иметь директиву, которая выставляет определенные ловушки, с которыми контроллер может связывать поведение (включая службы)?
ВТК
источник
Мне нужно немного больше контекста о том, чего вы хотите достичь, что сообщается, какой объем исходного кода следует ожидать, каков ваш домен, как он должен масштабироваться?
Пользователь
Это директива, отвечающая за рендеринг виджета комментариев - он отображает поле комментария вместе с кнопками отправки / отмены. Предполагается, что эта директива будет использоваться только в одном контексте - комментируя «документ». В настоящее время контроллер обрабатывает функции для создания реального комментария (внедренный экземпляр сервиса комментариев get для контроллера). Другой способ сделать это - инкапсулировать все это (вместе с обработкой ошибок / успехов) в директиве (директива получит сервис комментариев, внедренный).
WTK

Ответы:

24

Директива лучше всего (как правило), когда она короткая (с точки зрения кода), (потенциально) может использоваться повторно и имеет ограниченную область действия с точки зрения функциональности. Создание директивы, которая включает в себя пользовательский интерфейс и зависит от службы (которая, как я предполагаю, обрабатывает подключение к бэкэнду), не только дает ему 2 функциональные роли, а именно:

  • Управление пользовательским интерфейсом для отображения / ввода данных для виджета.
  • Отправка в бэкэнд (через сервис).

но также делает его менее пригодным для повторного использования, так как вы не сможете использовать его снова с другим сервисом или с другим пользовательским интерфейсом (по крайней мере, нелегко).

Принимая эти решения, я часто сравниваю со встроенными элементами HTML: например <input>, <textarea>или <form>: они полностью независимы от какого-либо конкретного бэкэнда. HTML5 дал <input>элементу несколько дополнительных типов, например date, которые по-прежнему не зависят от бэкэнда, и куда именно идут данные или как они используются. Они являются чисто интерфейсными элементами. Ваши пользовательские виджеты, созданные с использованием директив, я думаю, должны следовать той же схеме, если это возможно.

Однако это не конец истории. Выйдя за пределы аналогии со встроенными элементами HTML, вы можете создавать директивы многократного использования, которые как вызывают службы, так и используют чисто директиву пользовательского интерфейса, точно так же, как она может использовать a <textarea>. Скажем, вы хотите использовать HTML следующим образом:

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

Чтобы кодировать commentEntryдирективу, вы можете создать очень маленькую директиву, которая просто содержит контроллер, который связывает сервис с UI-виджетом. Что-то типа:

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

Принимая это до крайности, вам, возможно, никогда не понадобится иметь ручной ng-controllerатрибут в HTML: вы можете делать все это с помощью директив, если у каждого из них есть четкая роль «пользовательского интерфейса» или роль «данных».

Следует отметить один недостаток: он дает больше «движущихся частей» приложению, что добавляет сложности. Тем не менее, если каждая часть имеет четкую роль и хорошо (проверено устройство + E2E), я бы сказал, что это того стоит, и общая выгода в долгосрочной перспективе.

Михал Чарамза
источник
59

Позвольте мне не согласиться с ответом Михала Чарамзы.

Хотя его ответ теоретически правильный, он не очень практичен для реального мира.

Я говорю это, потому что раньше я так думал и пытался применить его в большом приложении реального мира, которое создаем я и моя команда, и это стало слишком хлопотным.

Аналогия с языком HTML не очень хорошая, потому что вы не должны стремиться создавать универсальные директивы многократного использования, потому что вы не создаете универсальное приложение, такое как веб-браузер.

Вместо этого вы должны использовать директивы для создания доменного языка (DSL) для вашего приложения, которое живет в своем собственном домене.

Это не означает, что все директивы не должны быть общими. Некоторые могут быть, если это в их природе. Если вы создаете пользовательский инструмент выбора даты, во что бы то ни стало сделайте его универсальным и повторно используемым в разных приложениях.

Но если вы создаете что-то вроде поля входа в систему, которое привязывается к вашему бэкэнду, просто сделайте это.

Единственное эмпирическое правило должно заключаться в следующем: никогда не дублируйте код (абстрактные маленькие кусочки для фабрик и сервисов) и делайте его тестируемым посредством внедрения зависимостей. К счастью, с Angular это просто кусок пирога.

Будь проще. :)

Дема
источник
5
Хорошие моменты, Дема - хотя я принял ответ Михала, я согласен с твоим подходом в том, что нам не следует прыгать с обручем, чтобы сделать что-то повторно используемое просто ради этого. На самом деле это был мой первоначальный инстинкт - связывать службу с директивой, потому что это имело смысл, а не потому, что гуру angularjs хотели или не хотели этого делать. В конце я создал директиву с сервисом, внедренным непосредственно в него, и в качестве публичного API я предоставляю ловушку для обратного вызова, который запускается после того, как комментарии действительно созданы.
WTK
2

Я думаю, что вопрос «должна ли директива взаимодействовать со службой» зависит от того, чем занимается ваша служба.

У меня были директивы, взаимодействующие со службами, которые ничего не делают с HTTP-запросами, и я думаю, что это хороший шаблон. Службы / фабрики отлично подходят для инкапсуляции логики, ориентированной на данные, а директивы - для инкапсуляции логики, ориентированной на представление. Заявленная цель служб в документации Angular: «Вы можете использовать службы для организации и совместного использования кода в вашем приложении». Это довольно широко, но службы могут быть использованы для достижения этой цели в директивах.

При этом я понимаю желание в некоторых случаях сделать так, чтобы директивы не делали напрямую HTTP-запросов. Опять же, это зависит от службы и от того, как вы организуете свои услуги.

ccnokes
источник
1

В соответствии с инфраструктурой AngularJS, мы должны использовать отдельные фабрики / сервисы для получения любых данных с сервера. Таким образом, эти фабрики могут быть повторно использованы во всех приложениях без переписывания одного и того же. Внутри директивы мы можем вызывать эти фабрики для получения данных, получаемых с Api / сервера.

Басаварадж Кабууре
источник