Posted by & filed under Content - Highlights and Reviews, Programming & Development.

code A guest post by Christopher Hiller, the author of Developing an AngularJS Edge and a Software Architect for Decipher, Inc. He enjoys gaming, coding, and gleaming the cube. He can be reached on GitHub or as @misterhiller on Twitter.

AngularJS is not a silver bullet. It doesn’t just magically generate maintainable and extensible apps. You might get lucky with a small app and write it from scratch elegantly and efficiently, using each of AngularJS’ features correctly, and maybe you even have 100% unit test coverage (read my post on writing tests and stomping bugs). Your app might do one thing, and do it flawlessly.

This series, however, is not about those applications.

This series is about applications that:

  • May be giant, frothing behemoths, yet…
  • Might be delicate flowers, whose petals shed at the slightest breeze
  • Have been written by multiple developers, but you didn’t write it
  • Integrate closely with other non-AngularJS apps, or have non-AngularJS apps fully embedded into it
  • Have lackluster unit tests or no unit tests whatsoever
  • Use a scaffolding tool designed for much smaller applications
  • Don’t have anyone on the team that fully understands the implementation
  • Take many weeks or even months to fully grasp to a new team member

And finally, if you’re 90% through developing a simple feature for this application and think, “you know what? I should have finished this a week ago,” and you still have 90% left to go, this post is for you!

There are steps you can take to improve your application and get it back in shape for easy development. That means quicker bug fixes, less bugs, and the faster implementation of new features. Taking some time to knock these out will save you from experiencing a lot of pain down the road.

Modularization

In an AngularJS context, modularization is organization by function instead of type. To compare, given arrays time = [60, 60, 24, 365] and money = [1, 5, 10, 25, 50], both are of the same type, but their functions are completely different.

That means your components (controllers, filters, directives) will live in modules instead of wherever they live now.

The benefits of modularization includes the following:

  • Encapsulation. Developers access the modules you create by including them within other modules. If you want the whole enchilada, you can still get it, but if you only want a bite, it’s easy to take. If your module relies on nothing but itself, you can easily make that module available to other applications.
  • Context. Each module provides a context. Given horn.service.js and horn.directive.js, if they belong to modules instrument and head respectively, you get a hint of their purposes. Without a module, there is no context and no meaning. This is especially important to developers new to the application, who will find it easier to connect the dots.
  • Testability. If you have to include your entire application every time you write a unit test, that’s not particularly unit-y, now is it? With modules, you will still need to include the module itself (or the test can’t access your component), but this is leagues better than including everything and potentially having to stub out run() and config() blocks of other modules.

When to Modularize

If your directory structure looks like this (and your app is large) you should modularize:

Or even if it looks like this…

Then you need to modularize. You have everything grouped by component type, which is fine in smaller applications because there aren’t many components. But soon those directories bloat. The locations of the files have nothing to do with what subsystem of your app they live in, and even the filename has no context. This is not helping you. Let’s create modules.

How to Modularize

Here is a 13 step guide to modularization in AngularJS:

  1. Unless you’re on pre-1.0 code, you probably already have a module declared for your main application. If not, declare one in its own file:

    …and upgrade. It will be painful, but not as painful as rewriting your app from scratch, because you should be able to save most of your code. Modularize while you upgrade, because you’re going to have to reorganize into modules anyway. (Upgrade to the newest version. You might even be able to retain the this syntax in your controllers.)
  2. Bring up your app in the browser. Define sections or subsystems or whatever you want to call them. If you need a hint, peek at the markup or even the controller hierarchy; it says a lot about your structure.
  3. Each section of your app is a bucket, so look at your files (all front-end files; not just .js or AngularJS files) and toss them into buckets. If you find that one file belongs in multiple buckets, one of two things are happening:
    • You have common functionality. It’s OK to create a bucket explicitly for this, but tread lightly as you might want to create a bucket within a bucket instead.
    • You have functionality that needs splitting up. Make a note of this; we’ll deal with it later.

    If you can put a bucket within another bucket, now’s the time to do it.

  4. Name your buckets if you haven’t already. Create directories (in the filesystem; we’re talking about real life now) for each bucket and subdirectories for, well, subbuckets.
  5. Move all of your files into their proper buckets. You may want to keep images in an images/ directory or CSS/LESS files in their own place; it’s your decision. At a minimum, this should be any AngularJS-specific .js file or a partial.
  6. In each of those directories, create a .js file named corresponding to its directory. If you have a naming scheme (like in the second directory tree above), use it. For example, if you have a bucket named nonsense, you may have nonsense/nonsense.module.js.
  7. In each of these files, write:

    where myModule is your bucket/directory/file name. The second example above now looks like this:

  8. Modify your files. A search/replace across many .js files would probably do it. You used to have every controller, filter, service, and so on, belonging to the myApp module. Eventually, myapp.module.js should be small, and it will serve mainly as a namespace. Find occurrences of your myApp module name, and see if you can’t replace them (within the proper directory) with myApp.myModule.
  9. If you have any files that need to be manually split because they belong in two modules, do it now.
  10. Partials may need special attention because to load one you need a path, and that path just changed. Find where you reference your partials and change as necessary. What I like to do first is this:

    Then, if you need the directory path for your module, inject myModuleConst into component, and put it on your controller’s $scope if the view needs it. If you need to reorganize again in the future, this will save time, because of “DRY”.

  11. Much like the above, your paths have changed, so update whatever loads your .js files and change those paths too. First, load angular.js, then you will want to explicitly load the *.module.js files and all component files thereafter, or a component will throw an exception because it can’t find it’s mommy/module. If you have a naming convention, this is easier, because you know modules have the suffix .module.js. RequireJS can help you with this sort of thing, but that’s beyond the scope here.
  12. Because AngularJS’ module system (as of this writing) is rather rudimentary, all you need to do to include everything is to modify your root myApp module and include all of your submodules. The myapp.module.js above might look like:

    …in addition to whatever third-party modules you may require.

  13. Finally, take anything out of your main application module (myapp.module.js here) that you can, and put that stuff into a proper module in its module definition file (*.module.js in the example). run(), config(), constant(), and value() blocks all commonly live in a module file. You can also put these blocks in separate files to ease unit testing. Application-wide settings should still live in your main module file.

Conclusion

That’s the 13-step guide to modularization. The next article in this series will discuss adding (more) unit tests to a large AngularJS application. Happy Angularing! Don’t forget your license.

For more details about AngularJS, see the resources below or explore our AngularJS library on Safari.

Safari has the content you need

Developing an AngularJS Edge is intended for intermediate JavaScript programmers. No attempt has been made to explain the JavaScript syntax used (except in the cases where AngularJS may introduce a peculiarity), nor do we explain concepts such as closures, function chaining, callbacks, or other common patterns. What we do explain are basic AngularJS concepts, components, and their applications. We provide examples along the way, answer questions, and correct common misconceptions. Together, we’ll build a working single-page weblog application using AngularJS, which will help you become proficient with using AngularJS to go out and create your own applications.
Develop smaller, lighter web apps that are simple to create and easy to test, extend, and maintain as they grow. AngularJS is a hands-on guide that introduces you to AngularJS, the open source JavaScript framework that uses Model–view–controller (MVC) architecture, data binding, client-side templates, and dependency injection to create a much-needed structure for building web applications.
Instant AngularJS Starter is designed to get you ramped up on AngularJS as quickly and efficiently as possible. By the end of this book, you’ll possess all of the knowledge you need to make full-featured, real-life applications with AngularJS. The code samples are reusable, and specifically intended to give you a head start on your next project. This book will transform your curiosity about AngularJS into a set of production-ready AngularJS skills, through a broad overview of the framework and deep dives into its key features.

Tags: AngularJS, Christopher Hiller, Encapsulation, Framework, Javascript, Modularization, RequireJS,

3 Responses to “13 Steps to AngularJS Modularization”

  1. bl4de

    What about such functionality, like for example models and collections used in many modules? For example, I’ve got some front-end logic to manipulate objects such as ‘User’, ‘Customer’ and even the whole collections of them.

    Should I create separate bucket ( e.g. models/, collections/ or something) and then include them something like this way:

    angular.module(“MyApp.Users”, ["MyApp.models.userModel", "MyApp.collections.userCollection"]);
    angular.module(“MyApp.Customers”, ["MyApp.models.customerModel"]);

    angular.module(“MyApp”, ["MyApp.Users", "MyApp.Customers"]);

    Am I thinking right?

  2. Christopher Hiller

    bl4de, it kind of depends what the xCollection and xModel modules consist of. If I were setting up something like this I’d probably define a MyApp.User or (or MyApp.Users) module and that would house all components corresponding to users. I’m not understanding the need to break it up into “collection” and “model” submodules?

  3. Smajl

    Good article. This is very close to how I modularize our enterprise-level app these days. ;)