What is LIFT?
Locating our code is easy
Identify code at a glance
Flat structure as long as we can
Try to stay DRY (Don’t Repeat Yourself) or T-DRY
This style guide is no longer maintained.
Table of Contents
This was created to offer a more in-depth guide for AngularJS developers that want their apps to scale, maintainable, and whatever
Please, feel free to diverge from this guide.
Basically, this is how our app will be.
├── app/ ├── components/ ├── core/ ├── dist/ ├── less|sass/ ├── vendor/
├── 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/
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?
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?
.(dot) for states of the same module. For instance, if we have the
users CRUD module, then we'd have
- to signify hierarchy. For instance, we have a
group module which is under the
users module, then we'd have: (
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?
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,
Why? This allows us to properly signify and understand what a dot (
.) and dash (
├── 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 | | ├── ...
core folder contains all
angular.bootstrap), and all common or shared files (in short, non-specific components) used in the app such as
├── 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
Q: What does each of the js file in the root of the
core folder do?
app.jsis 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.jsdoes nothing but bootstrap the app
*.module.jsis the module to be respectively used for each (all constants in the
constants/directory should use
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
Restangular methods, or store data from a server response. Otherwise, put in the
service as a normal service.
components folder contains mostly general-solution|non-feature-specific
├── 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
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
- 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.
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.
angularjs-structure-styleguide © 2014, Kier Borromeo (srph). Released under the MIT License.
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.