Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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)?
share|improve this question
 
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
add comment

2 Answers

up vote 1 down vote accepted

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) {
  '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.

share|improve this answer
add comment

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. :)

share|improve this answer
 
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 at 11:37
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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