Skip to main content

Coding guidelines

Use Angular CLI

This command line interface helps you scaffold your code in minutes. It references components into their own modules and complies with naming conventions as well.

Refer: Angular CLI


Folder structure

Split your app into core, shared and multiple feature modules.

[ project root ]
|--- src
|--- app
|--- core
|--- [+] guard
|--- [+] interceptor
|--- [+] service
|--- [+] model
|--- shared
|--- component
|--- [+]spinner
|--- services
|--- validation.service.ts|spec.ts
|--- [+] pipes
|--- [+] directives
|--- shared.module.ts
|--- layout
|--- [+] auth-layout
|--- [+] content-layout
|--- [+] footer
|--- [+] header
|-- user-profile
|--- [+] edit-profile
|--- [+] view-profile
|--- user-profile.component.ts|html|scss|spec.ts
|--- user-profile.module.ts
|--- user-profile-routing.module.ts
|--- app.component.ts|html|scss|spec.ts
|--- app.module.ts
|--- app-routing.module.ts
|--- main.ts
|--- [+] environments

Core module

This module is for classes used by app.module. Resources which are always loaded such as route guards, HTTP interceptors, and application level services, such as the AuthenticationService and logging belongs to this directory.

Shared module

Shared module can have components, directives and pipes that will be shared across multiple modules and components, but not the entire app necessarily.

We already have a Angular boilerplate that follows the aforementioned rules and can be used for new projects.

FEED Angular Boilerplate


Naming Conventions

TypeSyntaxExample
File NameDashed Case/Kebab Case
feature(.component/.pipe/.module/.directive).ts
user-list.component.ts, logger.service.ts
Class NameUpper Camel Case/Pascal CaseTrainingListComponent, ValidationDirective
Component Selector NameDashed Case/Kebab Case'admin-users', 'fd-title-bar' (fd stands for FEED Docs)

Single Responsibility

Redistribute the component and its supporting classes into their own, dedicated files. A class should have only one job.


Lazy load feature module

A feature module shouldn’t be loaded initially, but when only you decide to initiate it. Thereby making your Angular app load faster. Here is an example on how to initiate a lazy loaded feature module via app-routing.module.ts file.

const routes: Routes = [
{
path: 'dashboard',
loadChildren: 'app/dashboard/dashboard.module#DashboardModule',
component: CoreComponent,
},
];

Use tree-shakable providers

@Injectable({
providedIn: 'root'
})

Please note that root can be replaced with any module where you wish to provide your service with.


Create aliase for imports

Use aliases for imports that are several levels deep.

import {LoaderService} from '../../../loader/loader.service';

This can be configured in tsconfig.json file.

{
"compileOnSave": false,
"compilerOptions": {
removed for brevity,
"paths": {
"@app/*": ["app/*"],
"@env/*": ["environments/*"]
}
}
}

And resulting code will look like:

import { LoaderService } from '@app/loader/loader.service';
import { environment } from '@env/environment’;

Use interfaces

Use interface for data models. Interfaces are completely removed during compilation and so they will not add any unnecessary bloat to your final code.


Set up ENV variables

Use environment files for storing app keys/secrets. Create an example environment file with placeholders to provide information about fields required and push it to source control . Its also good practice to maintain separate environment files for develop, staging, and prod environments.

Do not check in the actual environment file to source control and create a security vulnerability!


Use directives

Use directives when same behavior is repeated for HTML elements. Example - hover animations.


Use trackby*

When using ngFor to loop over an array in templates, use it with a trackByfunction 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.


Prevent null exceptions

Use the safe navigation operator while accessing an object's property from a template. This helps in avoiding exceptions.

<div class="col-md-3">
{{user?.name}}
</div>

Preserve immutability to avoid suprises

The easiest way to avoid messing up objects and arrays unintentionally is by using the ES6 spread operator (...).

Original object

this.user = {
name: 'Dzon',
age: 25,
addr

Cloned object

let updatedUser = {
...this.user,
name: 'Peter',
};

Prevent memory leaks

  • When subscribing to observables, always make sure you unsubscribe from them appropriately by using operators like take, takeUntil, etc.
  • Make use of async pipe wherever applicable.

Avoid logic in template

Avoid complex logic in templates as it is harder to debug and less maintainable. Extract complex logic to functions in the component file instead.


Keep it tidy

  • Limit files to 400 Lines of code.
  • Try to be DRY.
  • Define small functions (no more than 75 lines).

Member sequence

  • Place properties up top followed by methods.
  • Place private members after public members, alphabetized.

Cheetsheet

DescriptionAvoidRecommended Usage
Component selectorsselector: 'fdTitleBar'selector: 'fd-title-bar'
Component custom prefixselector: 'title'selector: 'fd-title'
Directive selectorsselector: '[validate]'selector: '[fdValidate]'
Pipe names (init-caps.pipe.ts)@Pipe({ name: 'init-caps' })@Pipe({ name: 'initCaps' })
Components as elements<div fdTitleBar></div><fd-title-bar></fd-title-bar>
Decorate input and output propertiesinputs: ['label']@Input() label: string;
outputs: ['titleChange']@Output() titleChange = new EventEmitter<any>();
Dont prefix output properties@Output() onSavedTheDay = new EventEmitter<boolean>();@Output() savedTheDay = new EventEmitter<boolean>();
Put presentation logic in the component classAverage power: {{totalPowers / items.length}}Average power: {{avgPower}}
In Component,
get avgPower() { return this.totalPowers / this.items.length;}
Use the @Injectable() class decorator@Inject(HttpClient) private http: HttpClient@Injectable()
export class HeroArena {constructor(private http: HttpClient) {}}

Further reading

Official Angular style guide

AuthorIndu Rani
Updated byReshma S Raveendran
On21/04/2020