70 KiB
@title Style Guide
@intro Write Angular with style.
@description Looking for an opinionated guide to Angular syntax, conventions, and application structure? Step right in! This style guide presents preferred conventions and, as importantly, explains why.
{@a toc}
Contents
- Single responsibility
- Naming
- Coding conventions
- App structure and Angular modules
- Components
- Directives
- Services
- Data services
- Lifecycle hooks
- Appendix
Style vocabulary
Each guideline describes either a good or bad practice, and all have a consistent presentation.
The wording of each guideline indicates how strong the recommendation is.
**Do** is one that should always be followed.
_Always_ might be a bit too strong of a word.
Guidelines that literally should always be followed are extremely rare.
On the other hand, you need a really unusual case for breaking a *Do* guideline.
**Consider** guidelines should generally be followed.
If you fully understand the meaning behind the guideline and have a good reason to deviate, then do so. Please strive to be consistent.
**Avoid** indicates something you should almost never do. Code examples to *avoid* have an unmistakeable red header.
File structure conventions
Some code examples display a file that has one or more similarly named companion files.
For example, hero.component.ts
and hero.component.html
.
The guideline uses the shortcut hero.component.ts|html|css|spec
to represent those various files. Using this shortcut makes this guide's file structures easier to read and more terse.
{@a single-responsibility}
Single responsibility
Apply the single responsibility principle (SRP) to all components, services, and other symbols. This helps make the app cleaner, easier to read and maintain, and more testable.
Rule of One
Style 01-01
**Do** define one thing, such as a service or component, per file.
**Consider** limiting files to 400 lines of code.
The key is to make the code more reusable, easier to read, and less mistake prone.
The following negative example defines the AppComponent
, bootstraps the app,
defines the Hero
model object, and loads heroes from the server all in the same file.
Don't do this.
It is a better practice to redistribute the component and its supporting classes into their own, dedicated files.
As the app grows, this rule becomes even more important. Back to top
Small functions
Style 01-02
**Do** define small functions
**Consider** limiting to no more than 75 lines.
Naming
Naming conventions are hugely important to maintainability and readability. This guide recommends naming conventions for the file name and the symbol name.
General Naming Guidelines
Style 02-01
**Do** use consistent names for all symbols.
**Do** follow a pattern that describes the symbol's feature then its type. The recommended pattern is `feature.type.ts`.
Separate file names with dots and dashes
Style 02-02
**Do** use dashes to separate words in the descriptive name.
**Do** use dots to separate the descriptive name from the type.
**Do** use consistent type names for all components following a pattern that describes the component's feature then its type. A recommended pattern is `feature.type.ts`.
**Do** use conventional type names including `.service`, `.component`, `.pipe`, `.module`, and `.directive`.
Invent additional type names if you must but take care not to create too many.
Symbols and file names
Style 02-03
**Do** use consistent names for all assets named after what they represent.
**Do** use upper camel case for class names.
**Do** match the name of the symbol to the name of the file.
**Do** append the symbol name with the conventional suffix (such as `Component`,
`Directive`, `Module`, `Pipe`, or `Service`) for a thing of that type.
**Do** give the filename the conventional suffix (such as `.component.ts`, `.directive.ts`,
`.module.ts`, `.pipe.ts`, or `.service.ts`) for a file of that type.
<th>
Symbol Name
</th>
<th>
File Name
</th>
<td>
<code-example>
@Component({ ... })
export class AppComponent { }
</code-example>
</td>
<td>
app.component.ts
</td>
<td>
<code-example>
@Component({ ... })
export class HeroesComponent { }
</code-example>
</td>
<td>
heroes.component.ts
</td>
<td>
<code-example>
@Component({ ... })
export class HeroListComponent { }
</code-example>
</td>
<td>
hero-list.component.ts
</td>
<td>
<code-example>
@Component({ ... })
export class HeroDetailComponent { }
</code-example>
</td>
<td>
hero-detail.component.ts
</td>
<td>
<code-example>
@Directive({ ... })
export class ValidationDirective { }
</code-example>
</td>
<td>
validation.directive.ts
</td>
<td>
<code-example>
@NgModule({ ... })
export class AppModule
</code-example>
</td>
<td>
app.module.ts
</td>
<td>
<code-example>
@Pipe({ name: 'initCaps' })
export class InitCapsPipe implements PipeTransform { }
</code-example>
</td>
<td>
init-caps.pipe.ts
</td>
<td>
<code-example>
@Injectable()
export class UserProfileService { }
</code-example>
</td>
<td>
user-profile.service.ts
</td>
Service names
Style 02-04
**Do** use consistent names for all services named after their feature.
**Do** suffix a service class name with `Service`.
For example, something that gets data or heroes
should be called a `DataService` or a `HeroService`.
A few terms are unambiguously services. They typically
indicate agency by ending in "-er". You may prefer to name
a service that logs messages `Logger` rather than `LoggerService`.
Decide if this exception is agreeable in your project.
As always, strive for consistency.
<th>
Symbol Name
</th>
<th>
File Name
</th>
<td>
<code-example>
@Injectable()
export class HeroDataService { }
</code-example>
</td>
<td>
hero-data.service.ts
</td>
<td>
<code-example>
@Injectable()
export class CreditService { }
</code-example>
</td>
<td>
credit.service.ts
</td>
<td>
<code-example>
@Injectable()
export class Logger { }
</code-example>
</td>
<td>
logger.service.ts
</td>
Bootstrapping
Style 02-05
**Do** put bootstrapping and platform logic for the app in a file named `main.ts`.
**Do** include error handling in the bootstrapping logic.
**Avoid** putting app logic in `main.ts`. Instead, consider placing it in a component or service.
Directive selectors
Style 02-06
**Do** Use lower camel case for naming the selectors of directives.
Custom prefix for components
Style 02-07
**Do** use a hyphenated, lowercase element selector value (e.g. `admin-users`).
**Do** use a custom prefix for a component selector.
For example, the prefix `toh` represents from **T**our **o**f **H**eroes and the prefix `admin` represents an admin feature area.
**Do** use a prefix that identifies the feature area or the app itself.
Custom prefix for directives
Style 02-08
**Do** use a custom prefix for the selector of directives (e.g, the prefix `toh` from **T**our **o**f **H**eroes).
**Do** spell non-element selectors in lower camel case unless the selector is meant to match a native HTML attribute.
Pipe names
Style 02-09
**Do** use consistent names for all pipes, named after their feature.
<th>
Symbol Name
</th>
<th>
File Name
</th>
<td>
<code-example>
@Pipe({ name: 'ellipsis' })
export class EllipsisPipe implements PipeTransform { }
</code-example>
</td>
<td>
ellipsis.pipe.ts
</td>
<td>
<code-example>
@Pipe({ name: 'initCaps' })
export class InitCapsPipe implements PipeTransform { }
</code-example>
</td>
<td>
init-caps.pipe.ts
</td>
Unit test file names
Style 02-10
**Do** name test specification files the same as the component they test.
**Do** name test specification files with a suffix of `.spec`.
<th>
Symbol Name
</th>
<th>
File Name
</th>
<td>
Components
</td>
<td>
heroes.component.spec.ts hero-list.component.spec.ts hero-detail.component.spec.ts
</td>
<td>
Services
</td>
<td>
logger.service.spec.ts hero.service.spec.ts filter-text.service.spec.ts
</td>
<td>
Pipes
</td>
<td>
ellipsis.pipe.spec.ts init-caps.pipe.spec.ts
</td>
End-to-End (E2E) test file names
Style 02-11
**Do** name end-to-end test specification files after the feature they test with a suffix of `.e2e-spec`.
<th>
Symbol Name
</th>
<th>
File Name
</th>
<td>
End-to-End Tests
</td>
<td>
app.e2e-spec.ts heroes.e2e-spec.ts
</td>
Angular NgModule names
Style 02-12
**Do** append the symbol name with the suffix `Module`.
**Do** give the file name the `.module.ts` extension.
**Do** name the module after the feature and folder it resides in.
**Do** suffix a _RoutingModule_ class name with `RoutingModule`.
**Do** end the filename of a _RoutingModule_ with `-routing.module.ts`.
<th>
Symbol Name
</th>
<th>
File Name
</th>
<td>
<code-example>
@NgModule({ ... })
export class AppModule { }
</code-example>
</td>
<td>
app.module.ts
</td>
<td>
<code-example>
@NgModule({ ... })
export class HeroesModule { }
</code-example>
</td>
<td>
heroes.module.ts
</td>
<td>
<code-example>
@NgModule({ ... })
export class VillainsModule { }
</code-example>
</td>
<td>
villains.module.ts
</td>
<td>
<code-example>
@NgModule({ ... })
export class AppRoutingModule { }
</code-example>
</td>
<td>
app-routing.module.ts
</td>
<td>
<code-example>
@NgModule({ ... })
export class HeroesRoutingModule { }
</code-example>
</td>
<td>
heroes-routing.module.ts
</td>
Coding conventions
Have a consistent set of coding, naming, and whitespace conventions.
Classes
Style 03-01
**Do** use upper camel case when naming classes.
Constants
Style 03-02
**Do** declare variables with `const` if their values should not change during the application lifetime.
**Consider** spelling `const` variables in lower camel case.
**Do** tolerate _existing_ `const` variables that are spelled in UPPER_SNAKE_CASE.
Interfaces
Style 03-03
**Do** name an interface using upper camel case.
**Consider** naming an interface without an `I` prefix.
**Consider** using a class instead of an interface.
Properties and methods
Style 03-04
**Do** use lower camel case to name properties and methods.
**Avoid** prefixing private properties and methods with an underscore.
Import line spacing
Style 03-06
**Consider** leaving one empty line between third party imports and application imports.
**Consider** listing import lines alphabetized by the module.
**Consider** listing destructured imported symbols alphabetically.
Application structure and Angular modules
Have a near-term view of implementation and a long-term vision. Start small but keep in mind where the app is heading down the road.
All of the app's code goes in a folder named src
.
All feature areas are in their own folder, with their own Angular module.
All content is one asset per file. Each component, service, and pipe is in its own file.
All third party vendor scripts are stored in another folder and not in the src
folder.
You didn't write them and you don't want them cluttering src
.
Use the naming conventions for files in this guide.
Back to top
LIFT
Style 04-01
**Do** structure the app such that you can **L**ocate code quickly,
**I**dentify the code at a glance,
keep the **F**lattest structure you can, and
**T**ry to be DRY.
**Do** define the structure to follow these four basic guidelines, listed in order of importance.
Locate
Style 04-02
**Do** make locating code intuitive, simple and fast.
Identify
Style 04-03
**Do** name the file such that you instantly know what it contains and represents.
**Do** be descriptive with file names and keep the contents of the file to exactly one component.
**Avoid** files with multiple components, multiple services, or a mixture.
It may be advantageous to deviate from the _one-thing-per-file_ rule when
you have a set of small, closely-related features that are better discovered and understood
in a single file than as multiple files. Be wary of this loophole.
Flat
Style 04-04
**Do** keep a flat folder structure as long as possible.
**Consider** creating sub-folders when a folder reaches seven or more files.
**Consider** configuring the IDE to hide distracting, irrelevant files such as generated `.js` and `.js.map` files.
On the other hand,
<a href="https://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two" target="_blank">psychologists believe</a>
that humans start to struggle when the number of adjacent interesting things exceeds nine.
So when a folder has ten or more files, it may be time to create subfolders.
Base your decision on your comfort level.
Use a flatter structure until there is an obvious value to creating a new folder.
T-DRY (Try to be DRY)
Style 04-05
**Do** be DRY (Don't Repeat Yourself).
**Avoid** being so DRY that you sacrifice readability.
Overall structural guidelines
Style 04-06
**Do** start small but keep in mind where the app is heading down the road.
**Do** have a near term view of implementation and a long term vision.
**Do** put all of the app's code in a folder named `src`.
**Consider** creating a folder for a component when it has multiple accompanying files (`.ts`, `.html`, `.css` and `.spec`).
{@a file-tree} Here is a compliant folder and file structure:
<project root><aio-folder>
src
<aio-folder>
app
<aio-folder>
core
<aio-file>
core.module.ts
</aio-file>
<aio-file>
exception.service.ts|spec.ts
</aio-file>
<aio-file>
user-profile.service.ts|spec.ts
</aio-file>
</aio-folder>
<aio-folder>
heroes
<aio-folder>
hero
<aio-file>
hero.component.ts|html|css|spec.ts
</aio-file>
</aio-folder>
<aio-folder>
hero-list
<aio-file>
hero-list.component.ts|html|css|spec.ts
</aio-file>
</aio-folder>
<aio-folder>
shared
<aio-file>
hero-button.component.ts|html|css|spec.ts
</aio-file>
<aio-file>
hero.model.ts
</aio-file>
<aio-file>
hero.service.ts|spec.ts
</aio-file>
</aio-folder>
<aio-file>
heroes.component.ts|html|css|spec.ts
</aio-file>
<aio-file>
heroes.module.ts
</aio-file>
<aio-file>
heroes-routing.module.ts
</aio-file>
</aio-folder>
<aio-folder>
shared
<aio-file>
shared.module.ts
</aio-file>
<aio-file>
init-caps.pipe.ts|spec.ts
</aio-file>
<aio-file>
text-filter.component.ts|spec.ts
</aio-file>
<aio-file>
text-filter.service.ts|spec.ts
</aio-file>
</aio-folder>
<aio-folder>
villains
<aio-folder>
villain
<aio-file>
...
</aio-file>
</aio-folder>
<aio-folder>
villain-list
<aio-file>
...
</aio-file>
</aio-folder>
<aio-folder>
shared
<aio-file>
...
</aio-file>
</aio-folder>
<aio-file>
villains.component.ts|html|css|spec.ts
</aio-file>
<aio-file>
villains.module.ts
</aio-file>
<aio-file>
villains-routing.module.ts
</aio-file>
</aio-folder>
<aio-file>
app.component.ts|html|css|spec.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
app-routing.module.ts
</aio-file>
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
<aio-file>
...
</aio-file>
</aio-folder>
<aio-file>
node_modules/...
</aio-file>
<aio-file>
...
</aio-file>
While components in dedicated folders are widely preferred,
another option for small apps is to keep components flat (not in a dedicated folder).
This adds up to four files to the existing folder, but also reduces the folder nesting.
Whatever you choose, be consistent.
Folders-by-feature structure
Style 04-07
**Do** create folders named for the feature area they represent.
**Do** create an Angular module for each feature area.
App root module
Style 04-08
**Do** create an Angular module in the app's root folder,
for example, in `/src/app`.
**Consider** naming the root module `app.module.ts`.
Feature modules
Style 04-09
**Do** create an Angular module for all distinct features in an application;
for example, a `Heroes` feature.
**Do** place the feature module in the same named folder as the feature area;
for example, in `app/heroes`.
**Do** name the feature module file reflecting the name of the feature area
and folder; for example, `app/heroes/heroes.module.ts`.
**Do** name the feature module symbol reflecting the name of the feature
area, folder, and file; for example, `app/heroes/heroes.module.ts` defines `HeroesModule`.
Shared feature module
Style 04-10
**Do** create a feature module named `SharedModule` in a `shared` folder;
for example, `app/shared/shared.module.ts` defines `SharedModule`.
**Do** declare components, directives, and pipes in a shared module when those
items will be re-used and referenced by the components declared in other feature modules.
**Consider** using the name SharedModule when the contents of a shared
module are referenced across the entire application.
**Do** not provide services in shared modules. Services are usually
singletons that are provided once for the entire application or
in a particular feature module.
**Do** import all modules required by the assets in the `SharedModule`;
for example, `CommonModule` and `FormsModule`.
**Do** declare all components, directives, and pipes in the `SharedModule`.
**Do** export all symbols from the `SharedModule` that other feature modules need to use.
**Avoid** specifying app-wide singleton providers in a `SharedModule`. Intentional singletons are OK. Take care.
<aio-folder>
app
<aio-folder>
shared
<aio-file>
shared.module.ts
</aio-file>
<aio-file>
init-caps.pipe.ts|spec.ts
</aio-file>
<aio-file>
text-filter.component.ts|spec.ts
</aio-file>
<aio-file>
text-filter.service.ts|spec.ts
</aio-file>
</aio-folder>
<aio-file>
app.component.ts|html|css|spec.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
app-routing.module.ts
</aio-file>
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
...
Core feature module
Style 04-11
**Consider** collecting numerous, auxiliary, single-use classes inside a core module
to simplify the apparent structure of a feature module.
**Consider** calling the application-wide core module, `CoreModule`.
Importing `CoreModule` into the root `AppModule` reduces its complexity
and emphasizes its role as orchestrator of the application as a whole.
**Do** create a feature module named `CoreModule` in a `core` folder (e.g. `app/core/core.module.ts` defines `CoreModule`).
**Do** put a singleton service whose instance will be shared throughout the application in the `CoreModule` (e.g. `ExceptionService` and `LoggerService`).
**Do** import all modules required by the assets in the `CoreModule` (e.g. `CommonModule` and `FormsModule`).
**Do** gather application-wide, single use components in the `CoreModule`.
Import it once (in the `AppModule`) when the app starts and never import it anywhere else. (e.g. `NavComponent` and `SpinnerComponent`).
**Avoid** importing the `CoreModule` anywhere except in the `AppModule`.
**Do** export all symbols from the `CoreModule` that the `AppModule` will import and make available for other feature modules to use.
<aio-folder>
app
<aio-folder>
core
<aio-file>
core.module.ts
</aio-file>
<aio-file>
logger.service.ts|spec.ts
</aio-file>
<aio-folder>
nav
<aio-file>
nav.component.ts|html|css|spec.ts
</aio-file>
</aio-folder>
<aio-folder>
spinner
<aio-file>
spinner.component.ts|html|css|spec.ts
</aio-file>
<aio-file>
spinner.service.ts|spec.ts
</aio-file>
</aio-folder>
</aio-folder>
<aio-file>
app.component.ts|html|css|spec.ts
</aio-file>
<aio-file>
app.module.ts
</aio-file>
<aio-file>
app-routing.module.ts
</aio-file>
</aio-folder>
<aio-file>
main.ts
</aio-file>
<aio-file>
index.html
</aio-file>
...
`AppModule` is a little smaller because many app/root classes have moved to other modules.
`AppModule` is stable because you will add future components and providers to other modules, not this one.
`AppModule` delegates to imported modules rather than doing work.
`AppModule` is focused on its main task, orchestrating the app as a whole.
Prevent re-import of the core module
Style 04-12
Only the root AppModule
should import the CoreModule
.
**Do** guard against reimporting of `CoreModule` and fail fast by adding guard logic.
Lazy Loaded folders
Style 04-13
A distinct application feature or workflow may be lazy loaded or loaded on demand rather than when the application starts.
**Do** put the contents of lazy loaded features in a *lazy loaded folder*.
A typical *lazy loaded folder* contains a *routing component*, its child components, and their related assets and modules.
Never directly import lazy loaded folders
Style 04-14
**Avoid** allowing modules in sibling and parent folders to directly import a module in a *lazy loaded feature*.
Components
Component selector names
Style 05-02
**Do** use _dashed-case_ or _kebab-case_ for naming the element selectors of components.
Components as elements
Style 05-03
**Do** give components an _element_ selector, as opposed to _attribute_ or _class_ selectors.
Extract templates and styles to their own files
Style 05-04
**Do** extract templates and styles into a separate file, when more than 3 lines.
**Do** name the template file `[component-name].component.html`, where [component-name] is the component name.
**Do** name the style file `[component-name].component.css`, where [component-name] is the component name.
**Do** specify _component-relative_ URLs, prefixed with `./`.
Decorate input and output properties
Style 05-12
**Do** use the `@Input()` and `@Output()` class decorators instead of the `inputs` and `outputs` properties of the
`@Directive` and `@Component` metadata:
**Consider** placing `@Input()` or `@Output()` on the same line as the property it decorates.
Avoid aliasing inputs and outputs
Style 05-13
**Avoid** _input_ and _output_ aliases except when it serves an important purpose.
Member sequence
Style 05-14
**Do** place properties up top followed by methods.
**Do** place private members after public members, alphabetized.
Delegate complex component logic to services
Style 05-15
**Do** limit logic in a component to only that required for the view. All other logic should be delegated to services.
**Do** move reusable logic to services and keep components simple and focused on their intended purpose.
Don't prefix output properties
Style 05-16
**Do** name events without the prefix `on`.
**Do** name event handler methods with the prefix `on` followed by the event name.
Put presentation logic in the component class
Style 05-17
**Do** put presentation logic in the component class, and not in the template.
Directives
Use directives to enhance an element
Style 06-01
**Do** use attribute directives when you have presentation logic without a template.
HostListener/HostBinding decorators versus host metadata
Style 06-03
**Consider** preferring the `@HostListener` and `@HostBinding` to the
`host` property of the `@Directive` and `@Component` decorators.
**Do** be consistent in your choice.
Compare with the less preferred host
metadata alternative.
Services
Services are singletons
Style 07-01
**Do** use services as singletons within the same injector. Use them for sharing data and functionality.
Single responsibility
Style 07-02
**Do** create services with a single responsibility that is encapsulated by its context.
**Do** create a new service once the service begins to exceed that singular purpose.
Providing a service
Style 07-03
**Do** provide services to the Angular injector at the top-most component where they will be shared.
Use the @Injectable() class decorator
Style 07-04
**Do** use the `@Injectable()` class decorator instead of the `@Inject` parameter decorator when using types as tokens for the dependencies of a service.
Data Services
Talk to the server through a service
Style 08-01
**Do** refactor logic for making data operations and interacting with data to a service.
**Do** make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations.
A data service encapsulates these details. It's easier to evolve these
details inside the service without affecting its consumers. And it's
easier to test the consumers with mock service implementations.
Lifecycle hooks
Use Lifecycle hooks to tap into important events exposed by Angular. Back to top
Implement lifecycle hook interfaces
Style 09-01
**Do** implement the lifecycle hook interfaces.
Appendix
Useful tools and tips for Angular. Back to top
Codelyzer
Style A-01
**Do** use [codelyzer](https://www.npmjs.com/package/codelyzer) to follow this guide.
**Consider** adjusting the rules in codelyzer to suit your needs.
File templates and snippets
Style A-02
**Do** use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs.
**Consider** using [snippets](https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2) for [Visual Studio Code](https://code.visualstudio.com/) that follow these styles and guidelines.
<a href="https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2">
<img src="https://github.com/johnpapa/vscode-angular2-snippets/raw/master/images/use-extension.gif" width="80%" alt="Use Extension">
</a>
**Consider** using [snippets](https://atom.io/packages/angular-2-typescript-snippets) for [Atom](https://atom.io/) that follow these styles and guidelines.
**Consider** using [snippets](https://github.com/orizens/sublime-angular2-snippets) for [Sublime Text](http://www.sublimetext.com/) that follow these styles and guidelines.
**Consider** using [snippets](https://github.com/mhartington/vim-angular2-snippets) for [Vim](http://www.vim.org/) that follow these styles and guidelines.