Cleaning Code Recommendations: Angular

You can evaluate good software by reading some of the project’s code. Developers will undoubtedly enjoy working on code that is simple to read and modify. Nobody wants to continue working on a project with bad or disorganised code, which is a regular occurrence in development.  

Due to a tight deadline, developers occasionally refrain from developing clean code. They attempt to move more quickly but really move more slowly. As a result, they have to go back and fix more flaws in the same piece of code. Compared to the time spent writing the code, this procedure takes substantially longer.

Writing clean code is comparable to art or cooking in certain ways. Whether you are a new or seasoned programmer, you should always strive to improve as a programmer. One of your primary priorities as a developer should be to strengthen your coding abilities.

Here are the finest recommendations for writing clean code in Angular projects to maintain the best possible quality for your software.

1. Descriptive Function and Variable Names

Reading good, clear code is simple. When naming functions and variables, they should be clear and descriptive.

Avoid this:

function sum(a, b)) {  
const val = a + b;  
return val;  
}

Try this instead:

getTotalVehicals(car, suv)) {
const totalVehicals = car + suv;
return totalVehicals;
}

2. Use Path Aliases

The import statements are kept neat and understandable by referencing these highly nested files using path alliances.

Avoid this:

import 'commonComponent' from '../../../common/components/common.component.ts';

Try this instead:

import 'commonComponent' from '@app/common/components/common.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/*"]
    }
  }
}

3. Write Small Pure Functions

It is simpler to test and maintain smaller functions.

Avoid this:

insertOrModifyData(data: Data, status: boolean) {
  if (status) {
    return this.http.post<Data>(url, data)
      .pipe(this.catchHttpErrors());
  }
  return this.http.put<Data>(`${url}/${data.id}`, data)
    .pipe(this.catchHttpErrors());
  }
}

Try this instead:

insertData(data: Data) {
  return this.http.post<Data>(url, data)
    .pipe(this.catchHttpErrors());
}
modifyData(data: Data) {
  return this.http.put<Data>(`${url}/${data.id}`, data)
    .pipe(this.catchHttpErrors());
}

4. Pipeable Operators

Pipeable operators are usually Tree-shakeable, and during the build process, unused modules will not be included in the bundle. This makes it very simple to identify unnecessary operators included in the files.

Avoid this:

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

Try this instead:

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

5. Clear Code, Not Comments

Code should not be duplicated in comments; instead, comments should be used to improve code. Code should generally be self-explanatory and should probably be rewritten for clarity.

Avoid this:

if (b < 5) {

 a+5; //add five to a

} // if

Try this instead:

if (totalVehicals < 5) {

 totalVehicals + 5;  

}

6. Avoid Nested Subscriptions

You might need to use data from many observable streams in some circumstances. It can be challenging to identify and correctly close these resources when there are several nested subscriptions. You might consequently have memory leaks as a result. SwitchMap, forkJoin, and combineLatest are better ways to write clean code in Angular and make things simpler to read.

Avoid this:


this.returnsObservable1(...)

 .subscribe(

   success => {

     this.returnsObservable2(...)

       .subscribe(

         success => {

           this.returnsObservable3(...)

             .subscribe(

               success => {

                  ...

               },

Try this instead:

this.returnsObservable1(...)

 .pipe(

   flatMap(success => this.returnObservable2(...),

   flatMap(success => this.returnObservable3(...)

 )

 .subscribe(success => {...});

6. Avoid Memory Leaks

When using the async pipe, Angular handles unsubscribing; if we attempt to handle this on our own, the situation quickly degenerates into chaos. As long as the observable stream is left open, failing to unsubscribe will cause memory leaks.

The solution is to employ a subject that emits a value when the component is destroyed and construct our subscription with the takeUntil operator. The stream will unsubscribe as a result of this finishing the observable-chain.

Avoid this:

this.itemService.findItems()
  .pipe(
    map((items: Item[]) => items),
  ).subscribe()

Try this instead:

private unsubscribe$: Subject<void> = new Subject<void>();
  ...
   this.itemService.findItems()
    .pipe(
       map(value => value.item)
       takeUntil(this._destroyed$)
     )
    .subscribe();
  ...
  public ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
    this.unsubscribe$.unsubscribe();
  }

7. Reduce Bundle Size

Bundle sizes have a significant impact on how well the application performs. It’s not just about download speed; before it can be executed, all of the JavaScript that we ship to the browser must be processed and built. It can be challenging to keep our bundle in check, but it’s much simpler when we can identify the source of the bloat. We can see what’s in the production build thanks to the webpack-bundle-analyzer plugin.

8. State Management

You might start thinking about include a state management library when developing large, complicated applications with a lot of data coming from and going to the database and where state is shared across numerous components. Utilizing a state management library allows you to centralise the application’s state, reducing communication between components and maintaining your app’s predictability and clarity.

9. Use of Constructor vs. OnInit

The constructor should only be used to start class members, while ngOnInit() should be used to carry out the necessary actions as soon as the class is instantiated.

The reason for this is because when Angular calls ngOnInit, creating the component DOM, dependency injections and input bindings are complete. Thus, it is the ideal place to begin performing actual “work.”

10. Business Logic in Services Only

Components are constructed with presentation in mind. It deals with the function of the vision. Where appropriate, each piece of business logic must be separated into its own service, separating it from view logic and allowing the service to be unit-tested and reused.

Leave a Reply

Your email address will not be published. Required fields are marked *