34

Which one is considered better:

  • having a directive that interacts with services directly

or

  • having a directive that exposes certain hooks to which controller may bind behaviour (involving services)?
  • I would need a bit more context of what you want to achieve, what is communicated, how much source code is to be expected what is your domain, how does it have to scale? – User Dec 28 '13 at 16:37
  • It's a directive responsible for rendering a commenting widget - it displays the comment field, along with submit / cancel buttons. This directive is suppose to be used just in one context - commenting on "document". The way it's currently handled the controller is exposing functions for creating actual comment (controller get's injected instance of comments service). The other way of doing it is encapsulating the whole thing (along with error/success handling) in a directive (directive would get comments service injected). – WTK Dec 28 '13 at 18:31
24

A directive is best (as a rule-of-thumb) when it's short (code-wise), (potentially) re-usable, and has a limited a scope in terms of functionality. Making a directive that includes UI and depends on a service (that I assume handles connection to the backend), not only gives it 2 functional roles, namely:

  • Controlling the UI for display/entry of data for the widget.
  • Submitting to the backend (via the service).

but also making it less re-usable, as you then can't use it again with another service, or with a different UI (at least not easily).

When making these decisions, I often compare to the built-in HTML elements: for example <input>, <textarea> or <form>: they are completely independent of any specific backend. HTML5 has given the <input> element a few extra types, e.g. date, which is still independent of backend, and where exactly the data goes or how it is used. They are purely interface elements. Your custom widgets, built using directives, I think should follow the same pattern, if possible.

However, this isn't the end of the story. Going beyond the analogy with the built-in HTML elements, you can create re-usable directives that both call services, and use a purely UI directive, just like it might use a <textarea>. Say you want to use some HTML as follows:

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

To code up the commentEntry directive, you could make a very small directive that just contains the controller that links up a service with a UI-widget. Something like:

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

Taking this to an extreme, you might not ever need to have a manual ng-controller attribute in the HTML: you can do it all using directives, as long as each directly has a clear "UI" role, or a clear "data" role.

There is a downside I should mention: it gives more "moving parts" to the application, which adds a bit of complexity. However, if each part has a clear role, and is well (unit + E2E tested), I would argue it's worth it and an overall benefit in the long term.

59

Allow me to disagree with Michal Charemza answer.

Although his answer is theoretically correct, it is not very practical for the real world.

I am saying that because I used to think like that and tried to enforce it on a large real world app that myself and my team are building and it just became too troublesome.

The analogy to the HTML language is not good, because you should not strive to build general purpose, extremely reusable directives, because you're not building a generic application like a web browser.

Instead, you should use the directives to build a Domain Specific Language (DSL) for your app, that lives on its own domain.

That does not mean that all directives should not be generic. Some might be, if it's in their nature. If you're building a custom date picker, by all means make it generic and reusable across apps.

But if you're building something like a login box that binds to your back-end, just do it.

The only rule of thumb should be: do not ever duplicate code (abstract little pieces to factories and services) and make it testable through dependency injection. Fortunately, with Angular, those are a piece of cake.

Keep it simple. :)

  • 5
    Good points Dema - although I accepted Michal's answer I agree with your approach, that we shouldn't jump hoops to make something reusable just for the sake of it. That was my initial instinct really, to bind service with directive, because it made sense, not because it's how angularjs guru's would or wouldn't do it. In in the end I created directive with service injected directly into it, and as a public API I provide a hook for a callback that's fired after comments is actually created. – WTK Jan 16 '14 at 11:37
2

I think the "should a directive interact with a service" question depends on what your service is doing.

I've had directives interact with services that don't do anything with HTTP requests and I think it's a good pattern. Services/Factories are great for encapsulating more data oriented logic, and directives are great for encapsulating presentation oriented logic. The stated purpose of services in the Angular docs are: "You can use services to organize and share code across your app.". That's pretty broad but services can be used to achieve that goal in directives.

That being said, I do understand the desire in some cases to make it so that directives do not directly make any HTTP requests. Again, it depends on the service and how you're organizing your services.

1

As per AngularJS framework , we should singleton factories/services for getting any data from server. So that these factories can be reused across application without rewriting the same.Well inside directive we can call these factories to get data fetched from Api/server.

protected by gnat Jan 22 '16 at 12:39

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.