Extensible Angular: Mixing up a Batch of Awesome

I’m taking a time out from learning Node and the new Angular router (I haven’t forgotten my promise on upcoming posts) to give you a quick tip on providing your application with extensible, modular directives using inheritance! No, this isn’t the classical inheritance that some of you will be familiar with in Java, C++, Python or C#; nor is this the prototypical inheritance that you have come to love in Javascript. No, this is more of a way to extend the composition of your objects to include new functionality, while preserving their original concept.

That’s right, I’m going to show you how mixins can make your Angular application (and furthermore your javascript) more modular, maintainable, reusable and of course, extensible.

Mixins

First, let’s cover the idea of a mixin. A mixin is a collection of properties and methods that are not meant to represent a full idea by themselves, but rather provide additional functionality to another object. In Java and .Net, there is a somewhat similar concept, known as Interfaces. There is one key difference between the two. Interfaces simply provide the overall scaffolding of shared behavior that will be implemented by the class using the interface. A mixin, on the other hand, is more powerful as it actually provides fully built functionality that will be added to your object.The reason for this is due to javascript’s inherent dynamic nature that allows you to add and remove functionality from an object at will. It’s one of the reasons we all love (and sometimes hate) javascript.

Let’s take a quick look at an interface.

// C# Interface
public interface IShareable {
    public void share();
}

Note that the interface only provides a signature for the share routine; it does not define its inner workings.

Now let’s take a look at a mixin.

var mixin = {
	share: function(users){
		// Actual code to share your object here.
	}
}

Like I said, this allows you to create a function that can be added to any other object. It’s not better, just a different, yet comparable idea. I wish javascript had interfaces as well…but that’s for another day.

For a deeper look at mixins, check out Angus’ post A fresh look at Javascript Mixins. It provides a lot of excellent insight into how to create them and add them to your objects. For the rest of this post, we will be using an Angular method angular.extend, which will allow us to add our mixins to our controllers and directives, mostly because it’s a part of Angular and laziness (err…read as efficiency) is a part of the job!

Extending Directives and Controllers

Alright, now that we have a working understanding of mixins, let’s dive right into the heart of this post: moving common behavior shared between your directives and controllers out into factories and mixins.

Let’s say you have a bunch of directives that have similar directive definition objects (DDO):

angular.module('fields', [])
.directive('email', function(){
	return {
		restrict: 'E',
		controller: 'EmailController',
		scope: {
			model: '='
		}
		link: function(inputScope, inputElement, inputAttrs, ngModel){
			// link all the things!
		}
	};
})
.directive('phone', function(){
	return {
		restrict: 'E',
		controller: 'PhoneController',
		scope: { 
			model: '='
		}
		link: function(inputScope, inputElement, inputAttrs, ngModel){
			// link all the things!
		}
	}; 
})

Not the DRYest code, especially since nearly everything in those DDO’s is the same besides the controller. What can we do? How about creating a Factory that takes in a controller argument and spits out the correct DDO? Well, that would work well if you only ever have a few minor differences. Sure, you could pass in an options object that acts similarly to Python’s keyword arguments to allow the factory to build more customizable DDO’s…but there is a simpler way:

angular.module('fields', [])
.factory('BaseDDO', function(){
	return {
		restrict: 'E',
		scope: {
			model: '='
		}
		link: function(inputScope, inputElement, inputAttrs, ngModel){
			// link all the things!
		}
	}
})
.directive('email', ['BaseDDO', function(BaseDDO){
	return angular.extend(BaseDDO(), {
		controller: 'EmailController'
	});
]})
.directive('email', ['BaseDDO', function(BaseDDO){
	return angular.extend(BaseDDO(), {
		controller: 'PhoneController'
	})
]})

By moving this into a dumb factory that always returns the base object, we can get shared functionality that is DRY. But that only gets us half way there. We need to use angular.extend to take that base object and mix in our custom controller. It can also work the other way, where you defined your DDO in the directive and then have a factory that returns an object with just a controller property to mix in a shared controller.

Before I end this post, I have one more example for you, using controllers:

angular.module('fields', [])
.factory('BaseControllerProps', function(){
    return {
        required: false,
        hidden: false
    };
})
.controller('EmailController', ['BaseControllerProps', function(BaseControllerProps){
    angular.extend(this, BaseControllerProps, {
        readonly: false;
    });
}]);

This example is for a controller that is being aliased with the Controller As functionality within Angular. This is showcasing a bit more of the power from angular.extend. It allows you to extend your objects as much as you want. Simply make your base controller the destination object, and extend it with any other source objects, as needed. This will then copy over functionality and properties from the other objects to your controller.

Note that you can also clone all of your destination objects onto an empty source object {}. This preserves your destination objects, while allowing you to get a new object with all of the old features. I didn’t do this in the above example because Angular does not respect a return from the controller function when you use the Controller As functionality. So, a returned, cloned, and extended object would never be used. Because of this, you need to actually extend the base controller object.

Alright, to wrap up, Mixins are an awesome way to teach an old object new tricks, keep your code DRY (especially your Angular code, which can be quite the chore at times) and make everything much easier to read. Take care and enjoy your code’s modularity, maintainability and finally its extensibility!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s