angularjs-structure-styleguide
Basic, personal, opinionated style on structuring your AngularJS, adhering to John Papa's LIFT principle.
What is LIFT?
L
ocating our code is easyI
dentify code at a glanceF
lat structure as long as we canT
ry to stay DRY (Donβt Repeat Yourself) or T-DRY
Unmaintained
This style guide is no longer maintained.
Table of Contents
1. Context
This was created to offer a more in-depth guide for AngularJS developers that want their apps to scale, maintainable, and whatever
This guide gives emphasis to components or directives inspired by ReactJS and Flux. For more information in regards to this emphasis, check this article I wrote on Medium.
Please, feel free to diverge from this guide.
2. Overview
Basically, this is how our app will be.
First-level simplification
βββ app/
βββ components/
βββ core/
βββ dist/
βββ less|sass/
βββ vendor/
Extended
βββ app/
| βββ user
| | βββ partials/
| | βββ components/
| | | βββ ThatDirective/
| | | | βββ i18n/
| | | | βββ tests/
| βββ user.create/
| βββ user.edit/
| βββ newsCategory/
| βββ newsCategory.create/
| βββ newsCategory.edit/
βββ components/
βββ core/
| βββ constants/
| βββ filters/
| βββ resources/
| βββ services/
| βββ utils/
| βββ app.js
| βββ bootstrap.js
| βββ constants.module.js
| βββ filters.module.js
| βββ components.module.js
| βββ resources.module.js
| βββ services.module.js
βββ dist/
| βββ css/
| βββ js/
| βββ images/
| βββ views/
βββ less|sass/
βββ vendor/
3. App
The app
folder contains all the app's states. And each state may contain the controllers, directives, services to be specifically used for itself or child states.
βββ app/
| βββ user/
| | βββ i18n/
| | | βββ en.js
| | | βββ kr.js
| | βββ components/
| | βββ tests/
| | βββ partials/
| | βββ user.state.js
| | βββ user.controller.js
| | βββ user.html
βββ ...
Q: What is a state?
Used to register a state, see ui-router. This also has been asked before, see this issue.
Q: What if I started having more than 1 partial, controllers, and other things?
Stop using multiple controllers, partials, services. It is more recommended to use directives for the sake of modularity and maintainability. It is only okay to use partials for chunk of non-functioning mark-up.
βββ app/
| βββ user-profile/
| | βββ components/
| | | βββ Avatar/
| | | | βββ Uploader.js
| | | | βββ Webcam/
| | | | | βββ Webcam.js
| | | | | βββ WebcamShootButton.js/
| | βββ user.state.js
| | βββ user.controller.js
| | βββ user.html
βββ ...
Q: What if I have nested states?
As much as possible, try to avoid nested directories of states. For example, we have this state hierarchy:
- main
- user
- user.create
- user.edit
- user.delete
- profile
- profile.create
- profile.edit
- profile.delete
This is how we structure our directory. Why? This way, nested states can be easily found and understood while adhering to the LIFT principle.
βββ app/
| βββ news/
| βββ news.create/
| βββ news-category/
| βββ news-category.edit/
Q: How do I indicate a url nest or a state nest?
Use .
(dot) for states of the same module. For instance, if we have the users
CRUD module, then we'd have users.create
, users.edit
, ...).
Otherwise, use -
to signify hierarchy. For instance, we have a group
module which is under the users
module, then we'd have: (users
, users.create
, ..., users-groups
, users-groups.create
, ..).
To elaborate, here's an example: an app consists of a news CRUD [url: /news/*
] and a news category CRUD [url: /news/categories/*
]. We expect it to have these states:
news (abstract state)
news.index
news.create
news.edit
news-category (abstract state)
news-category.index
news-category.create
news-category.edit
This is how we create our directory:
βββ app/
| βββ news/
| βββ news.index/
| βββ news.create/
| βββ news.edit/
| βββ news.category/
| βββ news-category.index/
| βββ news-category.create/
| βββ news-category.edit/
Q: What if my state is composed of two words?
Use camelCase
; do not separate it with a -
(dash). Do not use -
(dash) to signify that a name consists of two words. Use it to signify a nest or hierarchy. If a state consists of two separate words (e.g, soulja boy, sticky nav), use camelCase
. For example, souljaBoy
, stickyNav
.**.
Why? This allows us to properly signify and understand what a dot (.
) and dash (-
) does.
βββ user/
βββ user-awesomeName/
βββ user-anotherModule/
βββ user-maybeAnotherModule/
βββ user-thatModule/
βββ user-thatModule.index/
βββ user-thatModule.create/
Q: Where do I put my tests or i18n?
Tests and i18n should be put close to our components or state as possible.
Why? This avoids the replication of our structure for our tests; and, makes them easier to view.
Q: How do I handle each language for the i18n?
The filename of each i18n should signify only the language it is supposed to handle. If a component only has one i18n file, simply put it at the same directory it will be used with.
| βββ user
| | βββ en.js
| | βββ user.controller.js
| | βββ user.state.js
| | βββ ...
4. Core
The core
folder contains all modules
, app bootstrapper
(see angular.bootstrap
), and all common or shared files (in short, non-specific components) used in the app such as services
, constants
, controllers
, resources
, utils
, and filters
.
βββ core/
| βββ constants/
| βββ filters/
| βββ resources/
| βββ services/
| βββ utils/
| | βββ progress.config.js/
| | βββ restangular.config.js/
| βββ app.js
| βββ bootstrap.js
| βββ constants.module.js
| βββ components.module.js
| βββ filters.module.js
| βββ resources.module.js
| βββ services.module.js
core
folder do?
Q: What does each of the js file in the root of the app.js
is our application module, the highest-level module, which contains all other modules. For instance,angular.module('app', ['app.resources', 'app.directives', 'app.services', 'app.constants']);
.bootstrap.js
does nothing but bootstrap the appangular.bootstrap(document, ['app'])
.*.module.js
is the module to be respectively used for each (all constants in theconstants/
directory should useconstant.module.js
).
resources
?
Q: What are These come in handy when you frequently request an API for data. Fill in resources
with your $http-service-wrappers to wrap $http
or Restangular
methods, or store data from a server response. Otherwise, put in the service
as a normal service.
5. Components
The components
folder contains mostly general-solution|non-feature-specific directives
.
βββ components/
| βββ StickyNavThatDoesThat/
| | βββ tests/
| | βββ StickyNavThatDoesThat.spec.js
| | | βββ Hamburger.spec.js
| | | βββ Search.spec.js
| | βββ i18n/
| | | βββ en.js
| | | βββ jp.js
| | | βββ kr.js
| | βββ tests/
| | βββ StickyNavThatDoesThat.js
| | βββ Hamburger.js
| | βββ Search.js
| βββ AwesomeProgressBar/
| | βββ i18n/
| | βββ tests/
| | βββ AwesomeProgressBar.js
| βββ components.module.js
components
not directives
?
Q: Why is this named as It took me a long while to decide whether how it should be named. After some time, I preferred to use components
because our directives
are actually being used as components. Again, your preference.
Q: Why are the directives separated from other angular modules?
I had decided to separate directives because:
- Directives are way too large to be nested inside the
core
folder. - They need emphasis and will contain mostly a large part of your app
Write the file names of your directives (components) in StudlyCase.
Why? To emphasize components. In the future, I might tweak a huge part of the guide to StudyCase. camelCase does not seem to give proper emphasis to itself, so.
6. Overall
Do not forget that this is a personal, opinionated structure styleguide. Although I have been using an almost-similar structure in production, your structure will vary on your project (team size, etc) from time-to-time. Make sure to keep it simple.
It is always better to create feature-based instead of role-based structure. Because, based on my experience, role-based structure will always be harder to write, read, and maintain.
Like I said in the Context, please feel free to diverge.
Acknowledgement
angularjs-structure-styleguide Β© 2014, Kier Borromeo (srph). Released under the MIT License.
srph.github.io Β Β·Β GitHub @srph Β Β·Β Twitter @_srph
This style guide has been largely influenced by John Papa's AngularJS Style Guide (which also includes a lecture with regards to an AngularJS app structure).
Contribution
I'd suggest to issue a proposal or a question first (for discussion purposes) before submitting a pull request. This avoids unrewarded / unmerged pull requests (if ever).
If you have any questions, issues, or whatever, feel free to send me a tweet on twitter, or just submit an issue.