WebDev – Pretty AngularJS directives

Terrible.

Terrible terrible.

The first thing I thought when I saw AngularJS directives was “holy HOLY!”, then I promptly screamed in agony. Nothing too dramatic, you know.

So directives are like WebComponents. I get that. They are damn useful. I get that. They are even friggin’ composable. I get that mucho bueno senorita Angularita. What I don’t get, though, is the terrible readability of this mess.

The Necronomicon

You might need a R’lyehian to English dictionary.

I can’t help but see how useful and unfriendly this is. This is practically shouting “Beginners! Flee now, or face several hours of therapy where you’ll revisit all your inadequacies!“. Seriously, just look at this.

angular.module('docsTabsExample', [])
.directive('myTabs', function() {
  return {
    restrict: 'E',
    transclude: true,
    scope: {},
    controller: function($scope) {
      var panes = $scope.panes = [];

      $scope.select = function(pane) {
        angular.forEach(panes, function(pane) {
          pane.selected = false;
        });
        pane.selected = true;
      };

      this.addPane = function(pane) {
        if (panes.length === 0) {
          $scope.select(pane);
        }
        panes.push(pane);
      };
    },
    templateUrl: 'my-tabs.html'
  };
})
.directive('myPane', function() {
  return {
    require: '^myTabs',
    restrict: 'E',
    transclude: true,
    scope: {
      title: '@'
    },
    link: function(scope, element, attrs, tabsCtrl) {
      tabsCtrl.addPane(scope);
    },
    templateUrl: 'my-pane.html'
  };
});

This is straight from the AngularJS Developer’s Guide. It is kinda… repulsive. Some people like it like that, however I believe that these people might not be working in teams. When you’re starting out with directives, like the first five times, you need to not feel like you can’t understand the mess of indents and squeezed anonymous functions. I remember freaking out the first few times.

This is not over though, there is something we can do, and no, it does not imply fleeing, therapy sessions, or asking an Old One to translate anything.

// Module Declaration
var docsTabsExample = angular.module('docsTabsExample', []);

// -------------------------------------------
// my-tabs :: ...description...
// Element only
// 
// Scope exposes:
//        (public)      select(pane)
//        (protected)   addPane(pane)
// -------------------------------------------
var myTabs = function() {

  // -------------------------------------------
  // Controller
  // -------------------------------------------
  var controller = function($scope) {

    // Variables
    var panes = $scope.panes = [];

    // Public methods
    $scope.select = function(pane) {
      angular.forEach(panes, function(pane) {
        pane.selected = false;
      });
      pane.selected = true;
    };

    // Protected methods
    this.addPane = function(pane) {
      if (panes.length === 0) {
        $scope.select(pane);
      }
      panes.push(pane);
    };
  }


  // -------------------------------------------
  // Return
  // -------------------------------------------
  return {

    // Restricted to Elements only
    restrict: 'E',

    // Transclusion
    transclude: true,

    // Scope
    scope: {},

    // Controller
    controller: controller,

    // Template
    templateUrl: 'my-tabs.html'
  }
}


// -------------------------------------------
// my-pane :: ...description...
// Element only
// 
// Scope exposes:
//        (property) title: [string]
// -------------------------------------------
var myPane = function() {

  // -------------------------------------------
  // Link
  // -------------------------------------------
  var link = function(scope, element, attrs, tabsCtrl) {
    tabsCtrl.addPane(scope);
  }
  
  // -------------------------------------------
  // Template
  // -------------------------------------------
  var template = '<div class="tab-pane" ng-show="selected" ng-transclude></div>';

  // -------------------------------------------
  // Return
  // -------------------------------------------
  return {

    // Required directives
    require: '^myTabs',

    // Restricted to Elements only
    restrict: 'E',

    // Transclusion
    transclude: true,

    // Scope
    scope: {
      title: '@'
    },

    // Link
    link: link,

    // Template
    template: template
  }
}


// Add my-tabs and my-pane directives to the module
docsTabsExample.directive('myTabs', [myTabs]);
docsTabsExample.directive('myPane', [myPane]);

This is the same code, but prettified! Yes, my little lambs of the web, I used the power of COMMENTS*! And the power of moving stuff around too, that’s pretty useful too.

Did I win your heart? Do you now believe in peace of mind, and in the power of understanding cryptic stuff? If this wasn’t enough to win you, here’s a Cthulhu themed directive template for all your future uses:

var oldOnesModule = angular.module('oldOnes', []);

// -------------------------------------------
// ng-cthulhu :: Old One
// Element only
// 
// Scope exposes:
//                methodA(param1, param2)
//                methodB(): [return type]
//                propertyC: [type]
// -------------------------------------------
var ngCthulhu = function() {


  // -------------------------------------------
  // Controller
  // -------------------------------------------
  var controller = function($scope) {
    // ...
  }


  // -------------------------------------------
  // Link
  // -------------------------------------------
  var link = function($scope) {
    // ...
  }


  // -------------------------------------------
  // Template
  // -------------------------------------------
  var template = "..."


  // -------------------------------------------
  // Return
  // -------------------------------------------
  return {

    // Restricted to Elements and Attributes
    restrict: 'EA',

    // Scope
    scope: {},

    // Controller
    controller: controller,

    // Link
    link: link,

    // Template
    template: template
  }
}

oldOnesModule.directive('ngCthulhu', [ngCthulhu]);

Use it wisely, and don’t wake the Old Ones…

*Don’t forget to add your own comments below! You can also subscribe to never miss any other post!