Angular File Structure

By: Ryan Wong at

There’s is lots of different ways to organize your angular project.

If your a small project, it doesn’t matter how you do it as long as it gets done fast.

Some people do feature based and some do a traditional MVC folder structure. I will show you what I do after.

Feature based

Feature based means htat you organize your folders by feature in your project. So Home page, Admin, Search and etc.

Advantage

  • it’s very clear how to find your code

Disadvantage

  • once project gets really big, there will be 100 folders at your base level directory and you start getting confuse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/app
main.js
/features
/home
HomeController.js
home.html
home.js
home.scss
/search
SearchController.js
search.html
search.js
search.scss
... //eventually there will be 100s of folders
/components
/modal
ModalDirective.js
/navbar
NavBarDirective.js
...

MVC

People who are familiar with MVC style development will understand this very well and be right at home

Advantage

  • very clear organization where code is (service is here, controller is here)

Disadvantage

  • gets really bulky very fast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/app
/model
UserFactory.js
...
/services
UserService.js
...
/controllers
/home
HomeController.js
/search
SearchController.js
... (gets bulky)
/views
/home
home.html
/search
search.html
... (gets bulky)
/scss
home.scss
search.scss
...

Module Based

I group all code that are in one module together and create common folder for things everyone shares.
I also use browserify to concatenate all files into 1 file.
The code is broken down into classes so it is easy to migrate to angular 2.
It is a mix of both MVC style and feature based.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/app
entry.js //load in all modules
/scss
main.scss //concatenate all non underscore scss
/marketing
_home.scss
marketing.scss // concatenate all _ scss
...

/common
/model
/services
index.js //bind all classes in services into index.js
/http
HttpService.js //Class
/utility
Utility.js //Class
index.js //bind all classes in utility into index.js
/uicomponents
index.js //bind all classes in uicomponent into index.js
/spinner
Spinner.js //Class
...
/marketing
index.js //bind all modules into index.js
routes.js //array of all routes
/models
UserModel.js //Class
index.js //bind all classes in model into index.js
/services
index.js //bind all classes in services into index.js
/user
UserService.js //Class
...
/views
/directives
index.js //bind all classes in directives into index.js
/somewidget
SomeWidget.js //Class
/modals
index.js //bind all classes in modals into index.js
/somemodal
SomeModal.js //class
/pages
index.js //bind all classes in page into index.js
/home
HomeController.js
home.html
/admin
// same as marketing
/member

So what I did was make every Controller, directive, service into a javascript class which you can DI angular properties.

1
2
3
4
5
6
7
8
9
10
11
12
13
var SomeController = function ($scope, $state, $log) {
vm._scope = $scope;
vm._state = $state;
vm._log = $log;
};

SomeController.prototype = {
somefunction: (){
//do something
}
};

module.exports = SomeController;

Then in your index.js file in the upper level folder

1
2
3
4
var SomeController = require('app/marketing/views/pages/someController/SomeController');

angular.module('marketing.pages', []).run(function(){})
.controller('someController',['$scope', '$state', '$log', SomeController]);

By doing this, we remove our codes dependancy on angular making it reusable by in the future. $scope can be injected in
as something else. You can also test this class individually now without needing angular.

The index.js file in the high level module would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var routes = require('app/marketing/route');

require('app/marketing/service');
require('app/marketing/view/modals');
require('app/marketing/view/directives');
require('app/marketing/view/pages');
angular.module('marketing', [
'ui.router',
'marketing.services',
'marketing.modals',
'marketing.pages',
'marketing.directives',
]).run([function(){}])
.config(function ($stateProvider) {
for(var i = 0; i < routes.length; i++){
$stateProvider.state(routes[i].stateName, routes[i]);
}
});

The routes file would look like this:

1
2
3
4
5
6
7
8
9
module.exports = [
{
stateName: 'app.marketing',
url: '/',
templateUrl: 'app/marketing/views/pages/home/home.html',
controller: 'someController',
controllerAs: 'vm',
}
]

The entry.js file would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require('core');
require('marketing');
require('admin');
require('member');

var app = angular.module('app', [
'ui.router',
'ui.bootstrap',
'ngSanitize',
'app.templates',
'core',
'marketing',
'admin',
'member'
])
.config(['$stateProvider',
'$urlRouterProvider',
'$locationProvider',
function ($stateProvider,
$urlRouterProvider,
$locationProvider)
{

$locationProvider.html5Mode({
enabled: true,
requireBase: false
}).run(function(){});