Or A
Or A

Reputation: 1789

structuring AngularJS application

We have an fairly large angularJS application, where all controllers were in one file.

We are thinking of splitting each controllers to his corresponding file. is there any reason why not to do it?

The reason we want to split it is due to easier management of source control / changes in the file, and obviously, much more intuitive.

Please advise.

Upvotes: 1

Views: 1704

Answers (3)

Adam Bogdan Boczek
Adam Bogdan Boczek

Reputation: 1770

In my opinion the best way to structure a large AngularJS application is to do it by feature as proposed by Peter Bacon Darwin and Pawel Kozlowski in their book "Mastering Web Application Development with AngularJS".

Instead of the structure recommended by the AngularJS seed project (and many other like Yeoman's generator-angular) that is "technically-driven" means each element type like controllers, partials/views etc. lands in its own folder, you should use a "domain-driven" structure, which reflects the business domains in your code.

It has many advantages like:

  • makes the ownership of the code explicit and clear,
  • introduces ubiquitous language that is shared across all modules,
  • empowers (to some extend) direct communication with the business analysts,
  • eliminates potential conflicts because of the lower need for merging etc.

Here an example (just to clarify the idea) of a structure as proposed in the book mentioned above:

Top-level directories in the project:

- src: contains application source code
- test: contains accompanying automated tests
- vendor: contains third party dependencies
- build: contains build scripts
- dist: contains build results, ready to be deployed in a target environment

Inside the src folder (fixed):

- app: AngularJS-specific code, domain-driven
- common: AngularJS-specific code, boiler-plate
- assets: holds images and icons,
- less: LESS variables
- index.html - entry point to the application

Inside the app folder (domain-driven) e.g. Web-shop:

- shop
  - products
  - customers
  - orders
    - orderlists
    - orderdetails
    - ... etc.
- admin
  - users
  - ... etc.

Each folder contains all relevant code including partials, scripts etc.

Upvotes: 4

Mounir
Mounir

Reputation: 11726

From an article by Brian Ford:

Probably the biggest question with large apps is where to put all of that code. On your toolbelt of organizational tools, you've got files, directories, modules, services, and controllers. For a quick overview of good AngularJS project structure, check out the Angular Seed on Github. However I'd like to go a bit more in-depth and offer some additional advice on project structure. Let's start with directories and work our way down the list.

For example your file structure can be like this:

project/
       app.js
       controllers/ #your controllers files here
       views/  #your templates here
       services/  #your services files
       directives/ #your custom directives

Each file should have one "thing" in it, where a "thing" is a controller, directive, filter, or service. This makes for small, focused files. It also helps create a litmus test for how APIs are doing. If you find yourself flipping back and forth through files too frequently, this is an indication that your APIs are too complex. You should rethink, refactor, and simplify.

Check the article for more details.

Upvotes: 2

Christopher Armstrong
Christopher Armstrong

Reputation: 3557

There are lots of different ways you can structure your app and there is hardly no right or wrong. So no, there is no reason why not to do it and it looks like it makes sense.

I have personally decided to use following structure based on my experiences to give you an example:

I use the directory structure of the Angular Seed Project:

  • app/js -> contains all dynamic js scripts
  • app/lib/angular114 -> contains the current angularjs version (i can use multiple versions for testing purposes this way)
  • app/lib/mycompany -> contains all my companies/personal re-usable libraries that I keep in a seperate repository
  • app/partials -> contains all my html partials which I devide in to subdirectories based on the js file structure in app/js. If I have a user.js then I have a user directory.

Own Libs

In my own libraries that are located in lib/mycompany I prefix all files and also method names with my company prefix. If my company would be called Eastern Asia Ltd. then I would create a two letter prefix named "ea". This will prevent name clashes with my dynamic js scripts as names of controllers and services can be publicly available throught my app.

lib/ea/ea-validators.js

(function() {
  'use strict'
  angular.module('eaValidators', [])
    .directive('eaValidateUnique', [function() {
      // directive code goes here
    }])
    .directive('eaValidateId', [function() {
      // code goes here
    }])
    .controller('MyCtrlJustAsExample', ['$scope', function($scope) {
      // code goes here
    }]);    
})();

I group everything in to logical modules such as:

  • Validation Module
  • Global Error Handling Module
  • Authentication/Authorization Module
  • ...

The Authentication Module for example contains directives, controllers, services and so on. I distinguish on logical entities rather then distinguish between controllers, directives and so on. I find this structure to be more efficient because when I want to change for example something related to validation I know it is in my validation module.

You can find examples here: http://chstrongjavablog.blogspot.ch/

Dynamic Content

I have a similiar approach for my dynamic js files. If I have for example a user page and a vendor page I want to display then I create following files in my js directory:

  • user.js
  • vendor.js

Again these files can contain directives, controllers, services in the same file grouped under a module. So if I need to change something related to a user I know straight away that I'll find the code in user.js.

A sample user.js could look following:

js/user.js

angular.module('user', ['ngResource'])
  .controller('UserListCtrl', ['$scope', 'User', function($scope, User) {
     // code goes here
  }])
  .controller('UserNewCtrl', ['$scope', 'User', function($scope, User) {
    // code goes here
  }])
 .factory('User', ['$resource', function($resource) {
    return $resource('/api/user/:userId', {userId: '@id'});
 }]);       

Glue it all together

js/app.js

Then I use js/app.js to glue everything together and do the routing:

angular.module('myApp', ['eaValidators', 'vendor', 'user'])
  .config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/user', {templateUrl: 'partials/user/user-list.html', controller: 'UserListCtrl'});
    $routeProvider.when('/user/new', {templateUrl: 'partials/user/user-new.html', controller: 'UserNewCtrl'});
    $routeProvider.otherwise({redirectTo: '/user'});
}])
  .config(['$httpProvider', function ($httpProvider) {
    $httpProvider.defaults.headers.common['Content-Type'] = 'application/json';
    $httpProvider.defaults.headers.common['Accept'] = 'application/json';
}]);

Then I just hook the files within the main index.html file:

index.html

<!doctype html>
<html lang="en" ng-app="myApp">
<head>
  <meta charset="utf-8">
  <title>YourApp</title>
  <link rel="stylesheet" href="css/app.css"/>
  <link rel="stylesheet" href="css/style.css"/>
  <link rel="stylesheet" href="css/ea-validators.css"/>
</head>
<body>

  <!-- Display partial pages -->
  <div ng-view></div>

  <!-- Include all the js files. In production use min.js should be used -->
  <script src="lib/jquery203/jquery-2.0.3.js"></script>
  <script src="lib/angular114/angular.js"></script>
  <script src="lib/angular114/angular-resource.js"></script>
  <script src="lib/ea/ea-validators.js"></script>
  <script src="js/app.js"></script>
  <script src="js/user.js"></script>
  <script src="js/vendor.js"></script>
</body>
</html>

The benefit of this structure is that I can copy for example the user.js and user partials over in to another project and re-use most of the code.

Also I have my global validation handlers, error handlers, authentication handlers that i can just checkout from my repository in to the lib directory.

I found this structure to be the most efficient up to now.

Another approach

You could also use YeoMan that does a lot for the structure for you. It however does add a lot of dependencies and when I tried to use it i dripped over some issues with dependency clashes during installation. My personal rule of thumb is that if I can't get something to work within 1 day I leave my hands away from it as I will loose time spending on code. Another rule of thumb I set myself is that if there is a framework with dependencies patched together that are developed by individuals with different objectives I try to avoid it if possible. Please note that these are personal rules I set myself and may not be the right strategy for other persons. Out of these reasons I decided to not use it. But there seems to be a quite big community already.

Production

For production purposes you could then use UglyfyJS or Google Closure Compiler to create one compressed js file with all the code or each file compressed seperatly.

I hope this description is useful for you.

Upvotes: 1

Related Questions