Home

Awesome

angularjs-structure-styleguide

Basic, personal, opinionated style on structuring your AngularJS, adhering to John Papa's LIFT principle.

What is LIFT?

Unmaintained

This style guide is no longer maintained.

Table of Contents

  1. Context
  2. Overview
  3. App
  4. Core
  5. Components
  6. Overall

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.

Back to top

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/

Back to top

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
|  |  ├── ...

Back to top

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

Q: What does each of the js file in the root of the core folder do?

Q: What are resources?

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

Q: Why is this named as components not directives?

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:

  1. Directives are way too large to be nested inside the core folder.
  2. 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.

Back to top

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.

Back to top

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.