📜 ⬆️ ⬇️

The best of creating clean and fast Angular applications.

The writing of this article encouraged the search for JavaScript front-end developers in their company in Stavropol. Because for a long time it was not possible to find an intelligent programmer and then we decided to launch an internship program with a large amount of teaching material on Angular & JS.

This is a translation of the Vamsi Vempati article about his experience with a large-scale application for Trade Me written in Angular.


')
To date, I have worked for a couple of years on Angular’s ​​large-scale application at Trade Me. Over the past few years, our team has been refining our application in terms of standards for writing code and performance in order to keep it in the best possible condition.

The article outlines the methods we use in our project. The text is mostly associated with Angular, TypeScript, RxJs and @ ngrx / store.

In addition, there will be considered some general guidelines for writing code that will help make the application more “clean” and readable.

1) trackBy


When using ngFor to loop the array in patterns, refer to the trackBy function, which will return a unique identifier for each element.

Why?


When the array is changed, Angular redraws the DOM tree completely. But if you use the trackBy function, Angular will understand which element has changed, and then make changes to the DOM only for that particular element.

Note: a more detailed description can be found in the article by Natanel Bazal. [eng]

Before


 <li *ngFor="let item of items;">{{ item }}</li> 


After


 //   <li *ngFor="let item of items; trackBy: trackByFn">{{ item }}</li> //   trackByFn(index, item) { return item.id; // unique id corresponding to the item } 

2) const or let?


When declaring variables, use const if they are not assigned again.

What for?


Using let and const where appropriate makes the reason for declaring variables more comprehensible. It can also help identify problems when a value is randomly reassigned to a constant due to a compile-time error. It also improves the readability of the code.

Before


 let car = 'ludicrous car'; let myCar = `My ${car}`; let yourCar = `Your ${car}`; if (iHaveMoreThanOneCar) { myCar = `${myCar}s`; } if (youHaveMoreThanOneCar) { yourCar = `${youCar}s`; } 

After


 //  car  ,      (const) const car = 'ludicrous car'; let myCar = `My ${car}`; let yourCar = `Your ${car}`; if (iHaveMoreThanOneCar) { myCar = `${myCar}s`; } if (youHaveMoreThanOneCar) { yourCar = `${youCar}s`; } 

3) Pipe-like operators


Use pipe-like operators when working with RxJs operators.
Pipe-like operators include when importing only the code that needs to be executed.

It also makes it easier to search for unused statements in files.

Note: Need Angular 5.5 and up.

Before


 import 'rxjs/add/operator/map'; import 'rxjs/add/operator/take'; iAmAnObservable .map(value => value.item) .take(1); 

After


 import { map, take } from 'rxjs/operators'; iAmAnObservable .pipe( map(value => value.item), take(1) ); 

4) Sign in the template


Avoid subscribing to observable objects from components — instead subscribe to them from a template.

Why?


async channels are automatically unsubscribed, simplifying the code and eliminating the need for manual subscription management. It also reduces the risk of accidental unsubscribe in the component, resulting in a memory leak. This is fixable using the lint rule for detecting unsigned observable objects.

In addition, it prevents components from remaining static and adds errors when data changes outside the subscription.

Before


 //  <p>{{ textToDisplay }}</p> //  iAmAnObservable .pipe( map(value => value.item), takeUntil(this._destroyed$) ) .subscribe(item => this.textToDisplay = item); 

After


 //  <p>{{ textToDisplay$ | async }}</p> //  this.textToDisplay$ = iAmAnObservable .pipe( map(value => value.item) ); 

5) Clean up subscriptions


When subscribing to monitored objects, always check that you then unsubscribe from them accordingly, using operators such as take , takeUntil , etc.

Why?


Errors of unsubscribing from observable objects will lead to an unwanted memory leak, since the observed stream remains open, probably even after the component is removed or when the user moves to another page.

Moreover, create a lint rule to detect observable objects from which there is no reply.

Before


 iAmAnObservable .pipe( map(value => value.item) ) .subscribe(item => this.textToDisplay = item); 


After


 private _destroyed$ = new Subject(); public ngOnInit (): void { iAmAnObservable .pipe( map(value => value.item) //     iAmAnObservable   ,     / takeUntil(this._destroyed$) ) .subscribe(item => this.textToDisplay = item); } public ngOnDestroy (): void { this._destroyed$.next(); this._destroyed$.complete(); } 

Use takeUntil to listen for changes until another watched object returns the value:

 iAmAnObservable .pipe( map(value => value.item), take(1), takeUntil(this._destroyed$) ) .subscribe(item => this.textToDisplay = item); 

Note the use of takeUntil together with take in the example above. This avoids memory leaks caused by the fact that no value was assigned to the subscription before the component was removed.

Without takeUntil subscription will still be suspended, as it were, until it receives its first value, but since the component has already been deleted, it will never receive a value, and this will lead to a memory leak.

6) Lazy loading


If possible, try to load modules in the Angular application only when they are used.

Why?


This will reduce the size of the loaded application and reduce its download time.

Before


 // app.routing.ts { path: 'not-lazy-loaded', component: NotLazyLoadedComponent } 

After


 // app.routing.ts { path: 'lazy-load', loadChildren: 'lazy-load.module#LazyLoadModule' } // lazy-load.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { LazyLoadComponent } from './lazy-load.component'; @NgModule({ imports: [ CommonModule, RouterModule.forChild([ { path: '', component: LazyLoadComponent } ]) ], declarations: [ LazyLoadComponent ] }) export class LazyModule {} 

Continuation of the translation in the next article. If anyone does not want to wait, here is the link to the original article.

Source: https://habr.com/ru/post/447042/


All Articles