I just watched Alicia Liu's HTML5 Developer Conference talk Levelling Up in AngularJs which had a lot of great information on common design patterns in Angular. I wrote some notes to make sure I really absorb the information, hopefully others will find them useful.

Views can be separated into modules

Video 2:43

I've never seen anyone do this but I really like the idea, especially the concept of keeping your routes inside a dedicated module. This also would allow you to just remove a dependency from the main app to remove a view, which would immediately remove the route as well. She suggests the following:

  • Separate your application components into individual modules to keep the code organized and allow for lazy loading of modules in the future.
  • Define routes for each of those modules inside the module to avoid a huge route file.

Services vs Factory

Video 4:35

I've been waiting for someone to tell me this is more than just a very subtle pattern, this is not really true. Services and Factories are both just convenience functions which help you register a provider.
By convention:

  • Services should be used with pre-defined object models, which are then exposed via the service.
  • Factories should be used in all other cases.

Directives: Scope properties

Video 15:27

I've always found the angular docs to be a little unclear in this area, I feel like Alicia helped clear it up for me. First the code:

//Parent HTML Template
<enemy enemy-type="type: {{enemyType}}" lives="enemy.lives" on-destroy="destroy()">

//myDirective Javascript
app.directive('myDirective', function() {
  ...
  scope: {
    type: '@enemyType', //'type: Bowser'
    currentLives: '=lives', //5
    onDestroy: '&onDestroy'
  }
  ...
}

//myDirective HTML Template
<div>
  <p>{{type}}, {{currentLives}} lives</p>
  <button ng-click="onDestroy()"></button>
</div>

The @ prefix

The @ prefix provides one-way string binding. In the example, @enemyType is passed in to the directive scope as a string, and as you can see in the html it can still be based on a parent scope variable.

The & prefix

The & prefix also provides one-way binding through a getter function. In the example, onDestroy it looks like it is passing the result of destroy(); I found this pretty confusing. What you're actually defining is an expression which will be called on the parent scope. destroy() will be executed on the parent scope, and therefore when you define the template for the directive you still need to add the parenthesis to call the method: ng-click="onDestroy()". On top of all that there is a special syntax for passing data into these methods, watch this, or play with this to learn more.

The = prefix

The = prefix provides two-way data binding. In the example, =lives is automagically available as a reference from the parent object. Additionally any changes made to the currentLives property on the child scope will also change on the parent scope.

This was also incredibly helpful in understanding the scope binding prefixes.

Communication between directives

Video 18:36

How do you write directives in a way that they can be applied to the same element and then talk to each other easily? This is a pretty common problem that I've tried to avoid in several different ways. In her example Alicia uses $observe and $watch to track the attributes on the primary mario directive. This allows the fireMode directive to act like a decorator and add additional functionality to mario in a manner that can be reused with other directives using the same interface.

//parent HTML Template
<mario fire-mode="{{mario.mode}}"></mario>

//Javascript
myModule.directive('mario', function() {
    return {
        restrict: "E",
        scope: {
            fireMode: "@"
        }
    }
}).
directive('fireMode', function() {
    return {
        restrict: "A",
        link: function(scope, iElement, iAttrs) {
            var $character = $(iElement[0]);
            iAttrs.$observe('fireMode', function(mode) {
                if (mode === "fire") {
                    $character.addClass("fire-mode");
                } else {
                    $character.removeClass("fire-mode");
                }
            });
        }
    };
})

$observe vs $watch

Up till now I've only used $watch to track $scope values, this works pretty well but now that we've clarified all the easy ways to use attributes above, we need a way to listen to those changes directly. $observe is a method on the attributes object which is passed in to a directives link function. In the example below $observe is being put to use for this purpose. You can also use $observe to avoid creating a scope altogether, and just observe changes that a parent makes to the attributes.

I didn't take notes on everything

I tried to focus on some of my favorite information from this talk but you might find some of the below valuable.