Thursday, October 17, 2013

Try, Catch, Fail: Angular JS - Directive Basics

Try, Catch, Fail: Angular JS - Directive Basics

A while ago I posted some very basic information about AngularJS. There are a lot of really cool things to go over in Angular, but I think the most important thing to go over is probably directives. Directives are what tie everything together.

Where the rubber meets the road


Directives in angular are easily the most powerful and complicated peace to the puzzle. Directives are used to set up DOM manipulations, interactions between the DOM and the scope, and a great many other things. Examples of directives are all over the Angular core framework. ng-modelng-clickng-repeatng-app are all examples of directives. Even the select,textarea and input tags have been extended as a directive. Directives can be used to set up JQuery plugins, do validation, create custom reusable controls.

Directives come in many different flavors


  • Elements - such as expression here
  • Attributes - such as
  • Classes - such as
  • Comments - such as
All of the above examples could even be the exact same directives used differently.

The anatomy of a directive


Warning: the following example is contrived, and really silly. But I'm trying to illustrate the most commonly used pieces of a directive declaration.

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
});

//the following will declare a new directive that
// may be used like 
// where foo is a property on a controller's scope.
app.directive('myDirective', function(){
  // The above name 'myDirective' will be parsed out as 'my-directive'
  // for in-markup uses.
  return {
    // restrict to an element (A = attribute, C = class, M = comment)
    // or any combination like 'EACM' or 'EC'
    restrict: 'E',
    scope: {
      name: '=name' // set the name on the directive's scope
                    // to the name attribute on the directive element.
    },
    //the template for the directive.
    template: '
Hello, {{name}}
'
, //the controller for the directive controller: function($scope) { $scope.reverseName = function(){ $scope.name = $scope.name.split('').reverse().join(''); }; }, replace: true, //replace the directive element with the output of the template. //the link method does the work of setting the directive // up, things like bindings, jquery calls, etc are done in here link: function(scope, elem, attr) { // scope is the directive's scope, // elem is a jquery lite (or jquery full) object for the directive root element. // attr is a dictionary of attributes on the directive element. elem.bind('dblclick', function() { scope.name += '!'; scope.$apply(); }); } }; });

The above directive is a crude example. It will output a "Hello, World" statement with a button to reverse the name with just the following markup: , presuming the parent scope has a property name equal to "World". It will also set up a double-click event that will tack an exclamation point on the end of the name.

And here's my absurd directive in action:

 


Fears of "Custom HTML tags" are unfounded


Have no fear. Angular is not destroying your perfect markup. Angular is using them as placeholders, nothing more. The HTML spec itself even says that custom tags should be ignored. If you're using "replace: true" in your directives, it's all replaced by whatever HTML you put in the template anyhow. This is a common complaint I've heard about Angular, and it's just a bad reason not to at least try Angular. It's an incredibly fun and powerful tool.


Directive Tips & Gotchas


  • Use the existing directives to do your event binding if possible. Don't bind events with JQuery anymore, just stop it. Also, DO NOT do what I did in my example and create your own simple binding like "dblclick", there is already a directive
  • You can nest directives. A directive's template may contain other custom directives.
  • Put them in their own module. Generally, it's a good idea to organize your directives into their own module. This promotes reuse in other modules as well as a separation of concerns.
  • I've witnessed self-closing directive tags not function properly. Always use both the open and close tags for your element directives.

For more comprehensive information about directives, have a look here.

No comments: