Angular project structure and conventions

Home / Angular project structure and conventions

I’m not one to clutch strongly to ideals, or entrench myself in unrelenting philosophies, but I am a creature of habit.  Most of my web applications over the past year have been Angular based and I have certain proclivities to particular structures in my applications.  My IDE of choice has been Visual Studio 2013, so I take advantage of it and .NET to serve up my base structure.

In dealing with VS2013/.NET, there are some base mechanisms of which I take advantage.  The ones I’d like to discuss are .NET bundling and Nuget.


Pulling down Nuget packages is a good method to ensure that your libraries have an upgrade path and you know what version is included in the project.  The major packages that I include are:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Angular.UI" version="0.4" targetFramework="net40" />
  <package id="Angular.UI.Bootstrap" version="0.12.0" targetFramework="net40" />
  <package id="angularjs" version="1.3.13" targetFramework="net45" />
  <package id="bootstrap" version="3.3.1" targetFramework="net40" />
  <package id="jQuery" version="2.1.3" targetFramework="net45" />
  <package id="jQuery.UI.Combined" version="1.11.3" targetFramework="net45" />
  <package id="jQuery.Validation" version="1.13.1" targetFramework="net45" />
  <package id="Select2.js" version="3.2" targetFramework="net40" />
  <package id="toastr" version="2.1.0" targetFramework="net45" />
</packages>

One thing I don’t like about Nuget, though, is that when you pull down a lot of JavaScript resources, they all get dumped into your ~/Scripts folder. When viewing this in Visual Studio’s solution explorer, it’s just cumbersome imho.

So, I structure my ~/Scripts folder to designate what my “app” scripts are and which “lib” scripts I have downloaded (either from Nuget or other sources). It makes a nice separation and makes maintenance, especially if handed off, easier.
Here’s my usual folder structure. I find this to be pretty concise and anyone that takes a cursory glance, maybe with only a slight bit of foreknowledge would be able to make sense of it. I also like using folders to segregate where my different Angular scripts reside. Without this bit of structure, I find that you wind up with a big jumble of files and searching/finding things becomes extremely tedious.

With this structure in place, bundling becomes a breeze. We have enough granular separation of scripts that are bundles become rather succinct. If we want a particular library, for example, we can just include all scripts within its defined folder. For our own app scripts, we can simply include our entire app scripts folder recursively. This methodology places out nicely and simplifies bundling. It ultimately may look something like this:

var libs = new ScriptBundle("~/bundles/scripts/lib")
    .Include( "~/Scripts/lib/jquery/jquery-{version}.js")
    .Include("~/Scripts/lib/toastr/toastr.js")
    .Include(
        "~/Scripts/lib/spin/spin.js",
        "~/Scripts/lib/spin/jquery.spin.js"
    )
    .Include("~/Scripts/lib/input-mask/jquery.maskedinput.js")
    .Include(
        "~/Scripts/lib/angular/angular.js",
        "~/Scripts/lib/angular/angular-animate.js",
        "~/Scripts/lib/angular/angular-cookies.js",
        "~/Scripts/lib/angular/angular-loader.js",
        "~/Scripts/lib/angular/angular-resource.js",
        "~/Scripts/lib/angular/angular-sanitize.js",
        "~/Scripts/lib/angular/angular-touch.js"
    )
    .Include("~/Scripts/lib/angular-ui-bootstrap/ui-bootstrap.js")
    .Include("~/Scripts/lib/angular-ui-bootstrap/ui-bootstrap-tpls.js")
    .IncludeDirectory("~/Scripts/lib/angular-ui", "*.js", true);

var app = new ScriptBundle("~/bundles/scripts/app")
    .IncludeDirectory("~/Scripts/app", "*.js", true);

bundles.Add(libs);
bundles.Add(app);

Taking advantage of Angular’s built in methods for module definition and encapsulation is always a good thing too.

Script conventions

  • Wrap all scripts as IIFE functions
  • Use Angular’s $inject instead of anonymous functions for injection
  • Define all modules in app.js with a setter and associate your scripts with those modules with the getter
  • Define all of your modules and module dependencies in app.js
  • Use controllerAs syntax wherever possible
  • Use prototypical inheritance where it makes sense
  • Define view models only if your server-side code doesn’t do this for you, or if you need prototype methods, to keep things DRY
  • Avoid business logic in your controllers; defer business logic to services

These conventions give us useful capabilities. Using IIFE blocks does a couple of nice things for us. IIFE blocks let us avoid polluting the global space and it allows us to write our Angular modules in such a way that they aren’t totally reliant on Angular entirely. That is to say, we can define the module as a simple JavaScript method, and since we’re in an IIFE block, we can call $inject to inject the Angular specific services and providers. Otherwise, if we use anonymous method injection, all of our code has to be wrapped as an Angular module and be completely dependent on Angular. Separating these things makes code re-factoring easier down the line.
Here’s an example:
In app.js, we’ll define all of our modules (as empty modules initially with [] setter syntax) and their dependencies:

(function () {
    angular.module('.services', ['ngResource', 'ngAnimate']);
    angular.module('myApp.controllers', %5B%5D);
    angular.module("myApp.directives", %5B%5D;
    angular.module("myApp.infrastructure", %5B%5D);
    angular.module("myApp.models", %5B%5D);
    angular.module("myApp.values", %5B%5D);

    var myApp = angular.module('myApp',
    [
        'myApp.services',
        'myApp.controllers',
        'myApp.directives',
        'myApp.infrastructure',
        'myApp.models',
        'myApp.values',
        'ngSanitize',
        'ui.bootstrap',
        'ui.router',
        'ui'
    ]);

    myApp.config(function () { });
    myApp.run(function () { });
})()

Then, when we utilize Angular’s factory methods, we can attach our services, controllers, etc to the appropriate modules. Calling the angular.module() with only the module name as a parameter as a getter. Calling the appropriate factory methods attach our name object to the module:

(function () {
    var myService = function ($resource) {
        get = function () {
            return;
        };

        return {
            get: get
        };
    };

    myService.$inject = ['$resource'];
    angular.module('myApp.services')
        .factory('myService', myService);
})()

ControllerAs syntax is a great feature of Angular so that we don’t have to refer to $scope nearly as much and our bound objects in our views can reference the controller by name. In our controller’s script attaching anything to “this” basically attaches it to the controllers scope. To make things readable, I usually assign “this” to a local variable called “vm.” This, in my mind, lets me think of everything I attach to vm as being accessible to my view. Thus, “this,” or “vm,” is effectively my view model and it makes an MVVM pattern more visible.

Regarding models, generally only define models if I have empty forms or if I’m creating data on the fly in a controller/service to post back, or if I want to adhere to OO patterns through prototypical inheritance. Otherwise, I let the return data from the server’s API define my models. Angular makes defining models pretty easy. Here’s an example where I may have a customer model where a user has to fill out information on a form and submit it:

(function () {
    var model = function () {
        var customerModel = function () {
            // Public properties
            this.firstName = "";
            this.lastName = "";
            this.email = "";
            this.phoneNumber = "";
        };

        // Extend customer w/ a method via prototype if we want to implement some OO functionality
        customerModel.prototype.fullName = function () {
            return this.firstName + ' ' + this.lastName;
        };
        return customerModel;
    };

    angular.module("myApp.models")
        .factory('customerModel', model);
})()

In my controller or service, the model is just another dependency that is injected. A new instance can be created, and then we use it as part of our view model.

(function () {
    var customerController = function (customerModel) {
        var vm = this;
        vm.customer = customer = new customerModel();
    };

    customerController.$inject = ['customerMoodel'];
    angular
        .module('myApp.controllers')
        .controller('customerCtrl', customerController);
})()

That’s it for this post. As I said, these are general conventions that I follow, but if better conventions or ideas come along, I think it’s important to be able to recognize them and change if necessary.

Leave a Reply