Structuring AngularJS Code

If you’re new to AngularJS check out my AngularJS in 60-ish Minutes video tutorial or download the free eBook. Also check out The AngularJS Magazine for up-to-date information on using AngularJS to build Single Page Applications (SPAs).

 

 

There are many different ways to define AngularJS controllers, factories, services, filters, and directives. For example, some devs start out by defining controllers using functions which gets the job done:

 

function MainController($scope) {
    //Code goes here
}


Although this code works, the function is in the global scope and the $scope parameter name could get mangled during script minification. As a result, a lot of people change it to the following to be more aligned with the “Angular way”:

 

angular.module('moduleName').controller('MainController', ['$scope', function($scope) {
    //Code goes here
}]);


While this also gets the job done, I’ve always found it to be a bit messy. I’d rather focus on the JavaScript code first, get it working properly, and then plug it into the Angular machinery. Here’s the general pattern I like to follow for controllers, factories, services, filters, and directives.

  1. Wrap everything with an immediately invoked function to pull code out of the global scope
  2. Define the controller/factory/service using standard JavaScript
  3. Plug the custom JavaScript code for the controller/factory/service/filter/directive into AngularJS
  4. Optional: Inject the parameter names to avoid minification problems using the $inject property. I’ve always done this when actually defining the controller/factory/service/filter but decided that using $inject is a little cleaner (credit to Scott Allen for a discussion we had on $inject that convinced me it was the way to go).

Note: Check out ng-annotate if you don’t want to worry about injecting the parameter names to avoid script minification issues. It’ll handle doing that after the fact. Thanks to Mike Alsup for mentioning it. He also mentioned an interesting alternative when you want to keep the injection of parameter names immediately next to where they’re used in a function that I mention below.

Here’s what this pattern looks like for controllers, factories, services, filters, and directives. Keep in mind there isn’t “one way” to write this code. What follows is my current preference.

 

Controllers

 

(function () {

    var ControllerName = function ($scope) {

    };

    ControllerName.$inject = ['$scope'];

    angular.module('moduleName').controller('ControllerName', ControllerName);

}());

 

If you want to keep the injection of parameter names close to the function definition the following will work (hat tip to Mike Alsup for the idea). I don’t use this approach currently, but it’s another option and the same general pattern could be followed for the code samples that follow:

 

(function () {

    ControllerName.$inject = ['$scope'];

    function ControllerName($scope) {

    }

    angular.module('moduleName').controller('ControllerName', ControllerName);

}());

 


Factories

 

(function () {

    var factoryName = function ($http) {
        var factory = {};

        return factory;
    };

    factoryName.$inject = ['$http'];


    angular.module('moduleName').factory('factoryName', factoryName);

}());

 


Services

 

(function () {

    var serviceName = function ($http) {

    };

    serviceName.$inject = ['$http'];


    angular.module('moduleName').service('serviceName', serviceName);

}());

 


Filters

 

(function () {

    var filterName = function () {

        return function (items, filterValue) {
            if (!filterValue) return items;

            var matches = [];
            filterValue = filterValue.toLowerCase();
            for (var i = 0; i < items.length; i++) {
                var item = items[i];
                //custom filter logic here
            }
            return matches;
        };
    };

    angular.module('moduleName').filter('filterName', filterName);

}());



Directives


I start out with the following for directives to help me remember the main pieces that are available to use. The “wc” prefix is something I add for my company – Wahlin Consulting.

 

(function () {

    var controller = function ($scope) {
        $scope.myVal = $scope.title;
    };

    controller.$inject = ['$scope'];

    var wcDirectiveName = function () {
        return {
            restrict: 'A', //E = element, A = attribute, C = class, M = comment         
            scope: {
                title: '@' //@ reads attribute value, = provides two-way binding, & works w/ functions
            },
            template: '<div>{{ myVal }}</div>',
            controller: controller,
            link: function ($scope, element, attrs) {
} } }; angular.module('moduleName').directive('wcDirectiveName', wcDirectiveName); }());

 

Keep in mind that using $inject is optional as mentioned earlier. I’m fine with injecting the parameters the following way as well:

 

(function () {

    var ControllerName = function ($scope) {

    };

    angular.module('moduleName').controller('ControllerName', ['$scope', ControllerName]);

}());

 

The reason I like $inject more is that it keeps the injected parameter names close to the actual function that uses the parameters which makes it easier to keep things matched up properly. It’s simply a personal choice though and only something I’ve switched to using recently.

One thing that I don’t like is duplicating the module name over and over across controllers, factories, etc. especially since it’s a string. While I try to avoid global variables, I’ve defined the module name using a variable in apps (as a global variable – I treat it like a “constant”) that has a specific name that’s unlikely to be duplicated and then reference it in angular.module() to avoid typos in strings. Ultimately it doesn’t really matter and it’s important to keep in mind that there’s no one “right” way to do this. Anyone who tells you otherwise either doesn’t know what they’re talking about or is too high and mighty to consider alternatives. :-)

Finally, some people like to declare a global variable that represents the angular.module object and then reference it when defining controllers, etc. As long as the variable name is descriptive and not likely to be stepped on that works as well (this topic just came up today in a discussion so here’s an example of what I mean):


var yourRootNamespace = yourRootNamespace || {};
yourRootNamespace.yourApp = angular.module('moduleName', ['ngRoute']);

//Can now get to main app anywhere using namespace
yourRootNamespace.yourApp.controller('ControllerName', ControllerName); 


I like to lookup the module dynamically as shown in the earlier samples and avoid as many global variables as possible. It all comes down to personal preference and how you like to write your code though.



Conclusion


I’ve never believed that “one size fits all” and typically evolve my code as I spend more and more time with a given framework. For now, this is the pattern I like to follow though when working with AngularJS controllers, factories, services, filters, and directives. Feel free to adjust it based on your individual likes/dislikes.

comments powered by Disqus

11 Comments

  • Hi Dan,
    very interesting article. Just one question: instead of using .$inject, are there any reasons why not using it like that:

    angular.module('moduleName').controller('ControllerName',['$scope', ControllerName]);

  • I prefer to post-process the injection using ng-annotate, but regarding the "cleanness" of the $inject syntax, I think it only appears clean because your function bodies are empty. If there were 50 lines of code within the function body then you'd have 50 lines of code between your function arguments and the $inject of those same arguments. That seems distinctly messing to me. But if you used function declarations instead of function assignment you could place the $inject directly above the function.

    ControllerName.$inject = ['$scope'];
    function ControllerName($scope) {

    };

  • malsup:

    That works as well. The injection piece is messy regardless I think and I've tried it multiple ways. For now I'm OK with either doing it when the component is defined (controller, etc.) or using $inject. The samples above are the code snippets I start with so they definitely get a lot bigger as you mention once all of the parameters are included.

    Dan

  • schacki:

    I've always done it that way in the past actually and it's absolutely fine going that route or the $inject route. The nice thing about $inject is you have the ability to keep it closer to the actual parameter definitions (such as the controller). But, it doesn't really matter either way as long as it's in there somewhere.

    Dan

  • Looks like you might have a copy/paste error ;) Your factory example is using controller()

  • Oops - thanks Brian. It was an error in the code snippet I use actually. You need to win something for taking such a detailed look and noticing that. :-) Appreciate it.

    Dan

  • I guess it's all a matter of preference, but I prefer the full module syntax or more specifically:

    myApp.controller('mapController',
    ["$http"],
    function($http) {
    });

    Reason is primarily that it's all in one place rather than scatter at the top and bottom. I hate having to add another dependency and then adding it in multiple places in the function and the declaration list.

  • Thanks malsup for the ng-annotate reference. I've used ngmin in the past, but ng-annotate seems nicer. I really like not having to clutter up my code with redundant annotations that can be automatically generated as part of the build process.

  • Rick,

    Yep - all comes down to preference. In the past I've done it the way you list. Who knows - I may go back to that at some point....either way works. :-) For now I'm liking the pattern shown above though since it lets me focus on the JavaScript first and then plug that into the Angular stuff.

    Dan

  • malsup:

    To chime in with Oran - thanks for mentioning ng-annotate. I haven't used that to this point but it definitely looks interesting (and gets rid of the clutter).

    Dan

  • My name is Iran Reyes and I am using AngularJS for several project. I like so much the the structure of an AngularJS application that you propose. My question is if there a way to work with a Modularizing AngularJS Applications with Yeoman, Grunt and Bower. This tools simplify in a high level the development flow, but the arquitecture that yeoman propose is not modular. Thanks for all.

Comments have been disabled for this content.