Angular Style Guide/Best Practices
1. File structure conventions
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.
2. Single responsibility
A class should have one and only one reason to change meaning that a class should have only one job.
Do define one thing such as a service or component per file
One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control.
Limiting files to 400 Lines of code.
Define small functions and Limiting to no more than 75 lines.
The small functions are easy to reuse, read and maintain.
3. Naming
Naming conventions are hugely important to maintainability and readability.
3.1 General Naming Guidelines
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.
3.2 Separate filenames with dots and dashes
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.
3.3 Symbols and file names
Do use upper camel case for class names.
Do match the name of the symbol to the name of the file.
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.
3.4 Service names
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
.
3.5 Bootstrapping
Do put bootstrapping and platform logic for the app in a file named main.ts
.
Do include error handling in the bootstrapping logic.
3.6 Component selectors
Do use dashed-case or kebab-case for naming the element selectors of components.
3.7 Component custom prefix
Do use a hyphenated, lowercase element selector value; for example, admin-users
.
Do use a prefix that identifies the feature area or the app itself.
3.8 Directive selectors
Do Use lower camel case for naming the selectors of directives.
The Angular HTML parser is case sensitive and recognizes lower camel case.
3.9 Directive custom prefix
Do use a custom prefix for the selector of directives (e.g, the prefix toh
from Tour of Heroes).
3.10 Pipe names
Do use consistent names for all pipes, named after their feature.
3.11 Unit test file names
Do name test specification files the same as the component they test.
Do name test specification files with a suffix of .spec
.
3.12 End-to-End (E2E) test file names
Do name end-to-end test specification files after the feature they test with a suffix of .e2e-spec
.
3.13 Angular NgModule names
Do append the symbol name with the suffix Module
.
Do give the filename 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
.
4. Coding conventions
4.1 Classes
Do use upper camel case, also known as PascalCase, when naming classes.
4.2 Constants
Do declare variables with const
if their values should not change during the application lifetime.
Lower camel case variable names (heroRoutes
) are easier to read and understand than the traditional UPPER_SNAKE_CASE names (HERO_ROUTES
).
4.3 Interfaces
Do name an interface using upper camel case.
Consider naming an interface without an I
prefix.
Consider using a class instead of an interface for services and declarables (components, directives, and pipes).
Consider using an interface for data models.
Interfaces are completely removed during compilation and so they will not add any unnecessary bloat to our final js code
4.4 Properties and methods
Do use lower camel case to name properties and methods.
Avoid prefixing private properties and methods with an underscore.
4.5 Import line spacing
Consider leaving one empty line between third-party imports and application imports.
Consider listing import lines alphabetized by the module.
5. Application structure and NgModules
All of the app’s code goes in a folder named src
. All feature areas are in their own folder, with their own NgModule.
5.1 LIFT
Do structure the app such that you can Locate code quickly, Identify the code at a glance, keep the Flattest structure you can, and Try to be DRY.
5.2 Locate
Do make locating code intuitive, simple and fast.
5.3 Identify
Do name the file such that you instantly know what it contains and represents.
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.
5.4 Flat
Do keep a flat folder structure as long as possible.
Consider creating sub-folders when a folder reaches seven or more files.
On the other hand, psychologists believe 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
5.5 T-DRY (Try to be DRY)
Do be DRY (Don’t Repeat Yourself).
Make sure you do not have the same code copied into the different places in the codebase extract the repeating code and use it in place of the repeated code.
5.6 Overall structural guidelines
|-- app
|-- modules
|-- home
|-- [+] components
|-- [+] pages
|-- home-routing.module.ts
|-- home.module.ts
|-- core
|-- [+] authentication
|-- [+] footer
|-- [+] guards
|-- [+] http
|-- [+] interceptors
|-- [+] mocks
|-- [+] services
|-- [+] header
|-- core.module.ts
|-- ensureModuleLoadedOnceGuard.ts
|-- logger.service.ts
|
|-- shared
|-- [+] components
|-- [+] directives
|-- [+] pipes
|-- [+] models
|
|-- [+] configs
|-- assets
|-- scss
|-- [+] partials
|-- _base.scss
|-- styles.scss
5.7 Folders-by-feature structure
Do create folders named for the feature area they represent.
A developer can locate the code and identify what each file represents at a glance. The structure is as flat as it can be and there are no repetitive or redundant names.
5.8 App root module
Do create an NgModule in the app’s root folder, for example, in /src/app
.
Makes it easier to locate and identify the root module.
5.9 Feature modules
Do create an NgModule for all distinct features in an application;
A feature module can easily be routed to both eagerly and lazily.
A feature module can easily be isolated for testing.
5.10 Shared feature module
Create a feature module named SharedModule
in a shared
folder.
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.
Not providing services in shared modules. Services are usually singletons that are provided once for the entire application or in a particular feature module.
SharedModule
will contain components, directives, and pipes that may need features from another common module; for example, ngFor
in CommonModule
.
5.11 Lazy Loaded folders
A distinct application feature or workflow may be lazy loaded or loaded on demand rather than when the application starts.
A typical lazy loaded folder contains a routing component, its child components, and their related assets and modules.
5.12 Never directly import lazy loaded folders
Avoid allowing modules in sibling and parent folders to directly import a module in a lazy loaded feature.
6. Components
6.1 Components as elements
Giving components an element selector, as opposed to attribute or class selectors.
It is easier to recognize that a symbol is a component by looking at the template’s HTML.
6.2 Extract templates and styles to their own files
Extract templates and styles into a separate file, when more than 3 lines.
Name the template file [component-name].component.html
, where [component-name] is the component name.
In most editors, syntax hints and code snippets aren’t available when developing inline templates and styles
6.3 Decorate input and output properties
Use the @Input()
and @Output()
class decorators instead of the inputs
and outputs
properties of the@Directive
and @Component
metadata:
6.4 Avoid aliasing inputs and outputs
Avoid input and output aliases except when it serves an important purpose.
Two names for the same property (one private, one public) is inherently confusing.
6.5 Member sequence
Place properties up top followed by methods
Place private members after public members, alphabetized.
Placing members in a consistent sequence makes it easy to read and helps instantly identify which members of the component serve which purpose.
6.6 Delegate complex component logic to services
limit logic in a component to only that required for the view. All other logic should be delegated to services.
Move reusable logic to services and keep components simple and focused on their intended purpose.
Keeps the component slim, trim, and focused.
6.7 Don’t prefix output properties
Name events without the prefix on
Name event handler methods with the prefix on
followed by the event name.
6.8 Put presentation logic in the component class
Put presentation logic in the component class, and not in the template.
Logic will be contained in one place (the component class) instead of being spread in two places.
7. Directives
7.1 Use directives to enhance an element
Use attribute directives when you have presentation logic without a template.
An element may have more than one attribute directive applied.
7.2 HostListener/HostBinding decorators versus host metadata
Consider preferring the @HostListener
and @HostBinding
to the host
property of the @Directive
and @Component
decorators.
The property associated with @HostBinding
or the method associated with @HostListener
can be modified only in a single place—in the directive's class. If you use the host
metadata property, you must modify both the property/method declaration in the directive's class and the metadata in the decorator associated with the directive.
Compare with the less preferred host
metadata alternative.
The host
metadata is only one term to remember and doesn't require extra ES imports.
8. Services
8.1 Services are singletons
Do use services as singletons within the same injector. Use them for sharing data and functionality.
Services are ideal for sharing methods across a feature area or an app.
Services are ideal for sharing stateful in-memory data.
8.2 Single responsibility
Create services with a single responsibility that is encapsulated by its context.
Create a new service once the service begins to exceed that singular purpose.
When a service has multiple responsibilities, it becomes difficult to test.
When a service has multiple responsibilities, every component or service that injects it now carries the weight of them all.
8.3 Providing a service
Provide a service with the app root injector in the @Injectable
decorator of the service.
When you provide the service to a root injector, that instance of the service is shared and available in every class that needs the service. This is ideal when a service is sharing methods or state.
When you register a service in the @Injectable
decorator of the service, optimization tools such as those used by the Angular CLI's production builds can perform tree shaking and remove services that aren't used by your app.
8.4 Use the @Injectable() class decorator
Use the @Injectable()
class decorator instead of the @Inject
parameter decorator when using types as tokens for the dependencies of a service.
9. Data Services
9.1 Talk to the server through a service
Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations.
10. Lifecycle hooks
Use Lifecycle hooks to tap into important events exposed by Angular.
10.1 Implement lifecycle hook interfaces
Implement the lifecycle hook interfaces.
Lifecycle interfaces prescribe typed method signatures. Use those signatures to flag spelling and syntax mistakes.
11 Other
11.1 trackBy
When using ngFor
to loop over an array in templates, use it with a trackBy
function which will return an unique identifier for each item.
When an array changes, Angular re-renders the whole DOM tree. But if you use trackBy
, Angular will know which element has changed and will only make DOM changes for that particular element.
11.2 Clean up subscriptions
When subscribing to observables, always make sure you unsubscribe from them appropriately by using operators like take
, takeUntil
, etc.
11.3 Avoid having subscriptions inside subscriptions
If you can do them all in parallel use forkJoin or
If each depends on the result of the previous use flatMap
and so on. There are many different operators to compose observables.
11.4 Avoid any; type everything;
Always declare variables or constants with a type other than any
.
11.5 Avoid logic in templates
If you have any sort of logic in your templates, even if it is a simple &&
clause, it is good to extract it out into its component.
Having logic in the template means that it is not possible to unit test it and therefore it is more prone to bugs when changing template code.
11.6 Remove unused code
It is extremely valuable to know if a piece of code is being used or not. If you let unused code stay, then in the future, it can be hard or almost impossible to be certain if it’s actually used or not. Therefore, you should make it a high priority to remove unused code.
11.7 Avoid code comments
Although there are cases for comments, you should really try to avoid them. You don’t want your comments to compensate for your failure to express the message in your code. Comments should be updated as code is updated, but a better use of time would be to write code that explains itself. Inaccurate comments are worse than no comments at all, as stated by anonymous:
Code never lies, comments do.
11.8 Clean up imports with path aliases
We can clean up these imports considerably by using aliases to reference our files, which looks something like this:
import 'reusableComponent' from '@app/shared/components/reusable.component.ts';
To be able to do this, we need to add a baseUrl
and the desired paths
inside our tsconfig.json
file:
{
"compilerOptions": {
...
"baseUrl": "src",
"paths": {
"@app:": ["@app/*"]
}
}
}
12. Appendix
Useful tools and tips for Angular.
12.1 Codelyzer
Use codelyzer to follow this guide.
Consider adjusting the rules in codelyzer to suit your needs
12.2 Use TSLint
This is a static code analysis tool we use in software development for checking if TypeScript code complies with the coding rules
12.3 Prettier with TSLint
You can combine TSLint with Prettier. Prettier is an amazing tool that enforces a consistent style by parsing your code and re-printing it, with its own rules in place. Having Prettier setup with TSLint gives you a strong foundation for your applications, as you no longer need to maintain your code-style manually. Prettier takes care of code formatting and TSLint handles the rest.
12.4 Lint with Husky
Even with these rules in place, it can be hard to maintain them. You can easily forget to run these commands before pushing your code to production, which leads to a broken result. One way to work around this problem is by using husky. Husky allows us to run custom scripts before the staged files are committed — keeping our production code clean and organized.
12.5 Angular Language Service
Angular Language Service — This extension provides a rich editing experience for Angular templates, both inline and external templates.
12.6 JSON to TS
JSON to TS isn’t Angular specific and works whenever you’re working with TypeScript. Json2ts comes handy when you have to create a TypeScript interface from a JSON object.
12.7 angular2-switcher
angular2-switcher Easily navigate to typescript(.ts)|template(.html)|style(.scss/.sass/.less/.css) in angular projects.
12.8 Auto Import
Auto Import Automatically finds, parses and provides code actions and code completion for all available imports. Works with Typescript and TSX
12.9 Path Intellisense
Path Intellisense is Visual Studio Code plugin that autocompletes filenames
12.10 SimonTest
SimonTest — Analyzes your Angular code and generates unit tests for you (can overwrite spec files generated automatically with Angular CLI).
Note: This is a paid extension. It comes with a 30 day trial. A license can be acquired at the product page: https://gumroad.com/l/SimonTest
12.11 Auto Close Tag
Auto Close Tag Automatically add HTML/XML close tag, same as Visual Studio IDE or Sublime Text.
Credits:
https://angular.io/guide/styleguide
https://itnext.io/clean-code-checklist-in-angular-%EF%B8%8F-10d4db877f74