7
\$\begingroup\$

This is a question about whether my coding style follows AngularJS best practices.

One of the selling points to Angular is directives. Directives allow you to create custom DOM with elements or attributes you create. I've found myself creating lots of directives, thinking of each one as little controls to be placed on the page. Looking back, I'm wondering if I'm too directive focused and if Angular directives should only be used when you need a custom link method?

What I find myself doing a lot is wrapping up some HTML in a directive with a simple controller that does one or two basic things. I have a lot of code that looks like this code of mine from a blog post:

'use strict';

angular.module('solrAngularDemoApp')
  .directive('searchResults', function () {
    return {
      scope: {
        solrUrl: '=',
        displayField: '=',
        query: '&',
        results: '&'
      },
      restrict: 'E',
      controller: function($scope, $http) {
        console.log('Searching for ' + $scope.query + ' at ' + $scope.solrUrl);
        $scope.$watch('query', function() {
          $http(
            {method: 'JSONP',
             url: $scope.solrUrl,
             params:{'json.wrf': 'JSON_CALLBACK',
                    'q': $scope.query,
                    'fl': $scope.displayField}
            })
            .success(function(data) {
              var docs = data.response.docs;
              console.log('search success!');
              $scope.results.docs = docs;

            }).error(function() {
              console.log('Search failed!');
            });
        });
      },
      template: '<input ng-model="query" name="Search"></input>' +
                '<h2>Search Results for {{query}}</h2>' +
                '<span ng-repeat="doc in results.docs">' +
                '  <p>{{doc[displayField]}}</p>'  +
                '</span>'
    };
  });

So now I can think of my search results as a little widget to embed on the page:

 <search-results solrUrl="..." etc></search-results>

OK Let's say I take the next step in my application and want to display more information about the individual documents from the search results. My inclination up to this point has been to create a custom directive, like a little "widget" that handles all the functionality here. If I need to say wrap-up how a document from the search results gets displayed, I'll add a directive like lets say "docDisplay". Something like:

angular.module('solrAngularDemoApp')
   .directive('docDisplay', function() {
         // same pattern as above
    });

Then in the template for search Results, I'll change out my template:

      template: '<input ng-model="query" name="Search"></input>' +
                '<h2>Search Results for {{query}}</h2>' +
                '<span ng-repeat="doc in results.docs">' +
                '  <doc-display></doc-display>'  + // THIS LINE CHANGED
                '</span>'

I may or may not use prototypical inheritance of $scope or use an isolate $scope, depending on how strongly I feel this directive is tied to the parent.

After writing a lot of code this way and learning more about Angular, I realized that I could just have

  1. Thrown all this HTML into my main HTML
  2. Used ng-controller on specific pieces of HTML

As nothing here takes advantage of the link functionality of the directive (binding DOM events to create some custome functionality) and my directives are just a bunch of HTML and controller code I'm left wondering the following two questions:

  1. Is it ok to use directives like this to break up my UI? or am I thinking too much in the directives==controls mindset
  2. Should I avoid directives when there's no link function? and just stick with plain-jane HTML with ng-controller?
\$\endgroup\$
  • \$\begingroup\$ Old question, however.. avoid ng-controller (its not reusable), use custom directives and TDD those directives. You should use HTML as your vehicle of user state instead of relying on implicit scope hierarchy to help communicate your intent better, and its actually very rare to have a section of your code that doesn't need input that wouldn't be better as a filter. \$\endgroup\$ – Dan Pantry Nov 13 '15 at 10:17
6
\$\begingroup\$

AngularJS encourages slim controllers. They'd rather you have fat directives and services than controllers.

I think you are absolutely correct to have many directives.

In my angular apps, I have some directives without link to make my templates clean and readable. You can compress trees of HTML into their high-level meaning. Plus, if things get more complex later on, you can add a link function and add functionality with relative ease, and you don't have to change code in multiple places. As a Rails developer, I believe in DRY (don't repeat yourself). The fewer places the same code is, the easier it is to maintain.

So, directive or ng-controller?

If you are going to reuse a "widget" in multiple places, directives are ideal!

If it's a one-off or the main feature of the page, you're probably better off making it a ng-controller.

\$\endgroup\$
  • 1
    \$\begingroup\$ I do this same kind of thing all the time. The only other thing I'd add on is you move that $http request into a companion service. Then you can easily mock and test this directive and service. \$\endgroup\$ – Michael J. Calkins Jan 4 '14 at 0:28
3
\$\begingroup\$

One thing I would say is having a lot of directives can have an impact on the performance of your app.

If your directive does not have a link or compile function then I would suggest it is better just to have it as an html file (partial or view) and use ng-include on the div you wish to inject it.

this way you still have nice declarative html, but with better performance, and more clear coding conventions to follow.

looking at that 1st code example there is no compile or link function so that directive is unnecessary. you could include the template as an html file with ng-include within the scope of a self contained controller with the same functionality as the controller above. Also create a factory which exposes your necessary http requests and inject that into the controller.

then it will be nice and de-coupled, you will have an html template you can inject into any controller scope you like, like wise you can inject your controller anywhere you like, and you will have a factory for http requests which you can inject into any controller you like.

\$\endgroup\$
  • \$\begingroup\$ This doesn't make any sense. In order to include code - and/or actually use the controller - you need to use a directive. Might as well use a custom directive. Angular 2.0 will move towards components which will be directive + controller (react-style) and remove the separation of controller and directive \$\endgroup\$ – Dan Pantry Nov 13 '15 at 10:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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