AngularJS helped open up new challenges while I spent the last year developing with it. Some of the following main challenges occurred:
- Too many components such as directives, controller, factories, services, and filters are created within one script file. The many lines of code cause a maintainability issue.
- Components are applicable across modules but the question would come on how to organize them.
Some people who work with Angular will resort to Angular’s seed repository which groups components into their own file. For example, all controllers will going into one controller.js file. I find this is suitable for less complex application’s where filters, for example, crammed into one file but the lines of codes are at a minimum. Instead of the seed approach, I decided to take Brian Ford’s approach on modularizing your code. His blog post states to separate each item of each component group to its own file. So, say you create a new filter that title case’s text called “titleCase”. Then, you would create a file called titleCase.js. To add to this, you would want to place this file in a folder called “filters” just as Brian’s blog shows. All filters, controllers, services, factories, and directives are added using the angular.module() without the second string array parameter that specifies dependencies.
/*The following would be how you add the 'titleCase' filter to the
'myApp' module without chaining. Notice the lack of
second parameters for module dependencies. */
angular.module("myApp").filter("titleCase",function(){...});
Brian’s post describes creating the module as a file in the root level of the javascript folder.
/*Creating a new module call myApp with a dependency to another module, someOtherModule.*/
angular.module ("myApp",["someOtherModule"]);
I found serious benefits in this approach:
- You will be dealing with less lines of codes per file.
- If you follow the stack trace, the isolation of errors are clearer because you have both a file name and line number of a specific component versus a line number and file name of a consolidate file of multiple types of the same component (e.g., ten different controllers in one controller.js).
However, what if we can expand on the isolation of code. When I used this method, I found that one “filter” folder can have many filter files but not all of them will necessary share the same module. So, I added to this convention by making a common module with the following for use by all modules. The following shows all of the common module’s components under the app folder (located in the js folder). This module could be considered the core functionality in which your app’s code chooses to take what it needs from it
.
I used the underscore prefix to designate it as the base foundation module for my application. Anything I consider to have a common use and is not coupled to a specific business use case will be placed inside these folders. Also, at this directory level, I will place my index.js file (this naming convention is my preference) that defines my main module (e.g., myApp). I do not bootstrap my app in this file because I leave ngApp to do it or explicitly do it in a code block below the references to these files. This is a matter of preference.
Now, that we have this foundation module completed, I can add more page or business logic-specific modules. I designate these modules to a specific context. For example, if I had a page for managing users of a website, I would create a module for that call Users. My above image shows two modules: moduleA and ModuleB. Each module, like the root module, will have an index.js that defines the module. These modules will actually have a dependency on the the root module. This was done so that each module can reuse components provided by the base module. So, if we were to look at moduleA’s index.js code, we would see something like.
/*definition of moduleA*/
angular.module('moduleA',['myApp']);
Now, moduleA will have access to the base module myApp’s components. The same can be said for moduleB. However, both moduleA and ModuleB are unaware of each other.
Notice one important thing: each module has its own component folders! This isolates these component solely to the module they share the parent folder with.
From this point forward, copy one of the module folders along with its components and modify it for a new module. This convention can help in a recursive file search for any concatenation/minification/obfuscation tools like ASP.NET MVC 4’s Bundler or Ruby on Rails asset pipeline. An example structure can be seen at my github repo. You can use it to guide you on this convention.