Building a large scale AngularJS application (following Object Oriented principles)

square

So, AngularJS hey? Node.js, Grunt, Yeoman, Bower… so many buzzwords, so little time.

Up until a couple of weeks ago I was in the lands of Java and Actionscript, JQuery and  HTML. Now I’m knee deep in gruntfiles, scss files, Coffeescript and AngularJS. It’s a whole new world, and yet has a lot of comfortable similarities to what I was already doing, and the more I dig into the AngularJS world, the more I’m finding that its bringing some proper MVC and OO ability to the loose and free world of Javascript.

In my team we’ve worked long and hard to ensure that we keep business logic where it should be, application control where it belongs, and styling floating around on the top. Through some forethought in the beginning we’ve ended up with a way of building applications in Flex that has a lot of reusable components and libraries, a lot of visual customisation, and very good separation of responsibility. Flex has been very good to us in this, as Actionscript is a strongly typed language which is truly Object Oriented. It lends itself to such nicely ordered coding, and via the Flash Builder IDE and some funky Air runtime goodness, you also can deploy your same code onto iOS and Android devices as native applications. It’s all very productive.

Unfortunately the world has moved on from Flash, and providing your web based apps via it is now proving a liability. Companies baulk at the idea of needing to have a plugin, they gasp that your application doesn’t use the latest HTMLness and start to think you’re some kind of monolithic dinosaur just waiting to become extinct.

And they’re kind of right.

We’ve known for some time that HTML5 is the way forward, and we wanted to jump, but every time we looked to, and looked at the tools and frameworks available, we always saw far, far too many ‘buts’, ‘excepts’, workarounds, hacks and tricks to get just a semblance of the proper separation of code functionality that we took for granted in our Flex world.

Then my colleague started playing with Angular, and there was much thoughtful nodding and quiet declarations of success. Eventually he declared it to be the best framework for giving us easy to use data binding (a huge plus of Flex), and seemed to be built by people who got how things should work.

Now, I’ve been watching talks, reading blog posts, rummaging through APIs, all trying to work out the best workflow for us to make and deploy applications that continue to have reusable libraries, good separation of responsibility, and are easy to maintain. I’ve read a lot of posts that say “this is how you should structure your large scale AngularJS application” and have tutted and scoffed at their short sighted methods of piling all their Controllers and Services into single Module files that will become monstrously large and impossible to maintain in a team coding environment.

Allow me to outline what I (a complete rank amateur in AngularJS, but an experienced Object Oriented coder) believe to be a scalable folder/file/code structure that allows for excellent code reuse and easy team code management.

yeoman-003

Tools used

For any workflow, you’re going to need some tools, and a lot of this is personal preference and optional, but as I work through things I’m finding them to provide really big productivity boosts and they get rid of so much drudgery.

A word of note. I’m in the minority in that I work on a Windows PC, it’s just kind of how it fell out, and this can make things that are a few easy lines on a Linux box be traumatic experiences not wished on your worst enemy. However I have a workflow that is working really well now and can point out some tips for fellow sailors on the Microsoft waters.

  • Console (Windows users): The command prompt that comes with Windows is… well… crap. And you’re going to be doing command line stuff here so it’s best to beef it up. You could put Cygwin on your machine and pretend it’s a Linux box, but I prefer the simple
    step of installing Console. It’s a tiny app, but makes your command prompt feel so much nicer.
  • Git (Windows users): The ol’ git… if you don’t yet have it (we didn’t, we use SVN in house), you’ll need it, as it’s the repository of choice these days and where Bower pulls some packages from. When you install it on your Windows box, make sure you select the method where it adds itself to your PATH so that it’s available within your command prompt. This is not the default, and it caused me some grief for a while as things appeared to be installing when in fact they weren’t, all because git wasn’t available.
  • Node.js: I hadn’t even heard of this before I started down this road a few weeks back, and the idea of server side Javascript blew my tiny little mind. After being initially seduced by its new and shiny sheen and thinking this was the way of the future for everything, I now know it’s an amazing piece of software that has its place. It’s place is not to wholesale replace all instances of Apache, but its package manager and ability to run javascript on the server makes most of what comes next possible. So install it.
  • Yeoman: Yeoman is a friendly little chap, he’ll pull together Grunt and Bower, write out config files, get the bits and bobs you need and make it all work. Yup, he makes like a lot easier, so I’d recommend using him. Once you have git and Node.js, follow the getting started guide on the Yeoman site, and use the angular generator it describes to get yourself a stock AngularJS application.
  • Webstorm: Coming from the Flex world, we had a pretty damn good IDE in Flash Builder (although it’s substantially less stable these days than it used to be), and so it was feeling like a huge backwards step to use straight text editors or IDEs that don’t know about the Angular API. While watching tutorials on the web, we noticed there were two editors that kept being the ones in use, Sublime Text and Webstorm. They both have AngularJS plugins, they both seem like great editors, they’re both about the same price. I’ve just settled on Webstorm and find it to be great to use. I think you’re not going to far wrong using either of them though.

 

Application Structure

Ok, now we’re down to the nitty gritty, and this is where everything else I read drops the ball. They may show some folder structures, or talk about how to break things out, but when it comes down to it, they either don’t show the actual contents of the files, or it ends up being a whole lot of ‘related’ code packed together into single files.

Folders

-app
 |-images
 |-skins
 |-src
 |-styles
-test

At the top level it’s fairly normal, only we have a ‘skins’ folder where we put templates. Also, some people like to have their tests in the same folders as the source files, I find this just makes the folders messy.

If we open out the top level, we get

-app
 |-images
   |-app
   |-package1
   |-package2
 |-skins
   |-app
   |-package1
   |-package2
 |-src
   |-package1
   |-package2
   |-app.js
 |-styles
   |-app.scss
   |-package1.scss
   |-package2.scss
-index.html

So, at the high level, we break things out into packages that can be reused from app to app. We have our Core package, our User Access package and so on. A given application can use a different collection of these packages, but each one can be dropped in at a folder level under src, and then you can just copy the skins and images between applications, but quite often these are the bits that will be most different, so are split out. (Note here: We don’t use numbers in our package names, but I’m using them here for brevity, and also because, by convention, they are not camelcased, so packageone looks odd)

Now, one more level down, just looking within one package, we get

-app
 |-src
   |-package1
     |-controllers
     |-directives
     |-factories
   |-Package1Module.js

So, here we get down to having folders for each of the types of components within Angular. We also have a module file that describes how these components are hooked in to the application. Optionally you might also have a utils folder for global functions that don’t belong in a controller, and also a vos folder for Value Objects.

Looking to the files themselves

-app
 |-src
   |-package1
     |-controllers
       |-PrettyThing.js
     |-directives
       |-PrettyThing.js
     |-factories
       |-ServiceyThing.js
   |-PackageOneModule.js

With the code looking like

PrettyThing.js (controllers folder)

var PackageOneControllerPrettyThing = function($scope){
   $scope.myThings = {};

   $scope.doSomething = function(){ 
      myThings = {'so','much','coolness'};
   };
};

PrettyThing.js (directives folder)

var PackageOneDirectivePrettyThing = function(){
   return{
        restrict: "E",
        scope : {
            title: "@",
            text: "@",
            open: "@",
            select: "@",
            returnFn: "@"
        },
        templateUrl: "app/skins/package1/PrettyThing.html",
        controller: "PackageOneControllerPrettyThing",
        replace: true
}

ServiceyThing.js

var PackageOneFactoryServiceyThing = function($http){
   return{
     call:function(target, data, response){
                stuff in here... using $http
            }
   };
}

PackageOneModule.js

var packageOneModule = angular.module('packageOneModule',['someDependancy']);

//Services
packageOneModule.factory('service',PackageOneFactoryServiceyThing);

//Controllers
packageOneModule.controller('PrettyThingCtrl', ['$scope', PackageOneControllerPrettyThing]);

//Directives
packageOneModule.directive('namespacePrettyThing',[PackageOneDirectivePrettyThing]);

By doing things in this manner, we gain a few things.

  • Each logical object has its own file, meaning no VCS merging nightmares when different team members are working on parts of the one project. I could be working on ServiceyThing while someone else is working on PrettyThing and when we commit we’ll get no merging fun, because they’re in different files. In the way I’ve seen most people do Angular, these would all be declared within the one module file.
  • Makes it easier to find the piece of code you need to work on
  • Makes the module file a simple listing of the given package’s components, easy to read and understand what everything is
  • The folder/file layout tries to mimic the standard packaging layout used within OO languages
  • Via the module definition, you can keep the known name of a controller (‘PrettyThingCtrl’), and switch out the actual instantiation to some other version just by changing the name referenced there. You don’t need to swap out a chunk of code within the module file that defines it, just switch which ‘class’ the module is pointing to. This allows you to have multiple versions of controllers/directives/services coded and defined in their own files and you can make your app point at whichever version you want it to in a snap.
  • If you want to use package1 in another project, just take that folder

Downsides

  • You do end up with a lot of files as the project increases. In a dev project, this means including them all into the index.html, which is bulky. But in the build process, these get minified and combined into only a few files, so it’s not a consideration in the final output
  • Currently we’ve chosen to keep the filenames simple, and using the folders as part of naming, so package1.controllers.PrettyThing.js is a semi package name for it. However Javascript doesn’t have that concept, and so the final var name used within the file (the ‘classname’) contains that all into the name (PackageOneControllerPrettyThing), which means that if you’re looking for that class, the file isn’t named the same. This is to avoid clashes on the names within code (if you had PrettyThing controllers within two packages you don’t want them both to have the same name), but not having huge names within the file system. It’s a little clunky, but I’m not sure how to better do this.

The app.js file is, like the module files, quite simple

angular.module('myApp', ['packageOneModule','packageTwoModule','ui.bootstrap'])
    .config(function ($routeProvider,$locationProvider,$httpProvider) {
        $locationProvider.html5Mode(false);
        $routeProvider
            .when('/prettyThing', {templateUrl: '/skins/package1/prettyThing.html', controller: 'PrettyThingCtrl'})
          ... and so on
            .otherwise({redirectTo: '/'});
    });

So, a simple case of including in your modules (and any 3rd party ones such as ui.bootstrap here), and setting up your routes.

 

Conclusion

AngularJS is a pretty fantastic opportunity to create really modular, reusable and elegant applications, however it also seems to have a lot of people creating vast files with many functions in them. Hopefully this structure can help to alleviate that tendency, and result in cleaner code.

Bookmark the permalink.