Angular
Trending

Angular Code Review Checklist.

Angular Code Review Checklist.

Introduction:

Code review is a process where developers have their work reviewed by their peers to check the code’s quality and functionality. In the case of Angular, there are specific points that we must check to ensure code effectiveness, which we will be discussing in detail in our upcoming blog post. Effective code reviews are crucial to delivering high-quality applications to end-users. This process involves a peer review of developers’ work. The main aim of this evaluation is to detect any bugs, syntax issues, and other factors that could impact the application’s performance. However, code reviews can be time-consuming. Therefore, we have created a comprehensive list of the most crucial elements to consider during your Angular code reviews.

Imports Organization:

You should group your imports by source and arrange them alphabetically, which will help keep your import section organized and tidy.

Bad Code Example:
import { Component, OnInit, Input } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AuthService } from '../services/auth.service';
import { BehaviorSubject, Observable, Subject, timer } from 'rxjs';
import { UserService } from '../services/user.service';
import { SomeOtherService } from '../services/some-other.service';
import { SomeComponent } from '../some-component/some-component.component';
import { AnotherComponent } from '../another-component/another-component.component';
import { SharedModule } from '../shared/shared.module';
import { ActivatedRoute, Router } from '@angular/router';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Component({
  selector: 'app-component',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  // ...
}

 

Good Example (Organized imports order) 

// Organize Angular modules separately
import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { FormsModule } from '@angular/forms'; 


// Organize Rxjs imports separetly 
import { BehaviorSubject, Observable, Subject, timer } from 'rxjs';

// Organize services imports separetly
import { AuthService } from '../services/auth.service';
import { UserService } from '../services/user.service';
import { SomeOtherService } from '../services/some-other.service';

import { SomeComponent } from '../some-component/some-component.component';
import { AnotherComponent } from '../another-component/another-component.component';
Service Injection:

It is recommended to review dependency injection in components and utilize TypeScript’s access modifiers (public, private, etc.).

Bad Code Example:
@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent implements OnInit {
  // ...
 constructor(
    public dialog: MatDialog,
    authService: JobService,
    userService,
    public ref: ChangeDetectorRef,

  ) {
  }
}
Good Example (With private access modifier ):
@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent implements OnInit {
  // ...
 constructor(
    private dialog: MatDialog,
    private authService: JobService,
    private userService,
    private ref: ChangeDetectorRef,

  ) {
  }
}

 

Observable Cleanup:

Use the async pipe to simplify the component code instead of etching data with observables, doing the subscribe and cleanup in ngOnDestroy.

Bad Code Example:
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { DataService } from './data.service';

@Component({
  selector: 'app-data-list',
  template: `
    <h2>Data List</h2>
    <ul>
      <li *ngFor="let item of data">{{ item }}</li>
    </ul>
  `,
})
export class DataListComponent implements OnInit, OnDestroy {
  data: string[] = [];
  private dataSubscription: Subscription;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    this.dataSubscription = this.dataService.getData().subscribe((result) => {
      this.data = result;
    });
  }

  ngOnDestroy() {
    if (this.dataSubscription) {
      this.dataSubscription.unsubscribe();
    }
  }
}
Good Example ( With async pipe):
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { DataService } from './data.service';

@Component({
  selector: 'app-data-list',
  template: `
    <h2>Data List</h2>
    <ul>
      <li *ngFor="let item of data$ | async">{{ item }}</li>
    </ul>
  `,
})
export class DataListComponent {
  data$: Observable<string[]>;

  constructor(private dataService: DataService) {
    this.data$ = this.dataService.getData();
  }
}
Property Initialization:

It is considered a best practice to set default values for properties to prevent runtime errors.

Bad Code Example ():
import { Component } from '@angular/core';

@Component({
  selector: 'app-data-grid',
  template: `
    <!-- Data grid rendering code -->
  `,
})
export class DataGridComponent {
  data; // No initialization

  constructor() {
    // Imagine some logic to populate dataArray dynamically.
  }
}
Good Example:
import { Component } from '@angular/core';

@Component({
  selector: 'app-data-grid',
  template: `
    <!-- Data grid rendering code -->
  `,
})
export class DataGridComponent {
  data: any[] = []; // Initialize with an empty array

  constructor() {
    // Logic to populate dataArray dynamically.
  }
}

 

Component Initialization:

I recommend reviewing the ngOnInit  method, don’t make it too long. Try to break it into smaller methods for better readability and maintainability.

Bad Code Example :

import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data-list',
  template: `
    <h2>Data List</h2>
    <ul>
      <li *ngFor="let item of data">{{ item }}</li>
    </ul>
  `,
})
export class DataListComponent implements OnInit {
  data: string[] = [];

  constructor(private dataService: DataService) {}

  ngOnInit() {
    // Fetch data from the service
    this.dataService.getData().subscribe((result) => {
      // Filter and transform the data
      const filteredData = result.filter((item) => item.length > 5);
      const transformedData = filteredData.map((item) => item.toUpperCase());

      // Sort the data
      transformedData.sort();

      // Set the component data
      this.data = transformedData;
    });
  }
}

Good Example (Breaking Down ngOnInit)

 

import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data-list',
  template: `
    <h2>Data List</h2>
    <ul>
      <li *ngFor="let item of data">{{ item }}</li>
    </ul>
  `,
})
export class DataListComponent implements OnInit {
  data: string[] = [];

  constructor(private dataService: DataService) {}

  ngOnInit() {
    this.loadData();
  }

  private loadData() {
    this.dataService.getData().subscribe((result) => {
      const filteredData = this.filterData(result);
      const transformedData = this.transformData(filteredData);
      this.data = this.sortData(transformedData);
    });
  }

  private filterData(data: string[]): string[] {
    return data.filter((item) => item.length > 5);
  }

  private transformData(data: string[]): string[] {
    return data.map((item) => item.toUpperCase());
  }

  private sortData(data: string[]): string[] {
    return [...data].sort();
  }
}
Consider Extracting Logic to Services:

If a component logic can be reused in multiple places, we can extract it into services for better code organization and reusability.

Bad Code Example:

import { Component } from '@angular/core';
import { User } from './user.model'; // Assuming User model is imported

@Component({
  selector: 'app-user-management',
  template: `
    <h2>User Management</h2>
    <button [title]="generateTooltipForEditButton(currentUser)">Edit User</button>
  `,
})
export class UserManagementComponent {
  currentUser: User;

  constructor() {
    // Initialize the currentUser based on user data retrieval
    this.currentUser = this.getUser(/* specify user ID or other criteria */);
  }

  generateTooltipForEditButton(user: User): string {
    if (user) {
      if (user.state === 'SUSPENDED') {
        return 'This user is suspended and cannot be edited';
      } else if (user.state === 'INACTIVE') {
        return 'This user is inactive and cannot be edited';
      } else if (!user.authorizedToUpdate) {
        return 'You are not authorized to edit this user';
      } else {
        return 'Edit';
      }
    }
    return 'Edit';
  }

  // Simulated method to retrieve a user, replace with actual logic
  getUser(userId: number): User {
    return {
      id: userId,
      name: 'John Doe',
      state: 'ACTIVE',
      authorizedToUpdate: true,
    };
  }
}

 

Good Example:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  // Other user-related methods and properties
// move the component fucntion to the service 
  getEditUserButtonTooltip(user: User): string {
    if (user) {
      if (user.state === 'SUSPENDED') {
        return 'This user is suspended and cannot be edited';
      } else if (user.state === 'INACTIVE') {
        return 'This user is inactive and cannot be edited';
      } else if (!user.authorizedToUpdate) {
        return 'You are not authorized to edit this user';
      } else {
        return 'Edit';
      }
    }
    return 'Edit';
  }
}




import { Component } from '@angular/core';
import { UserService, User } from '../user.service';

@Component({
  selector: 'app-user-management',
  template: `
    <h2>User Management</h2>
    <button [title]="generateTooltipForEditButton(currentUser)">Edit User</button>
  `,
})
export class UserManagementComponent {
  currentUser: User;

  constructor(private userService: UserService) {
    // Initialize the currentUser based on user data retrieval
    this.currentUser = this.userService.getUser(/* specify user ID or other criteria */);
  }

  generateTooltipForEditButton(user: User): string {
    return this.userService.generateTooltipForEditButton(user);
  }
}
Hard-Coded Styles:

It’s important to avoid using inline styles as they can be difficult to maintain. Instead, it’s recommended to define appropriate styling classes.

Bad Example (Hard-Coded Styles in Template):

<!-- bad-example.component.html -->
<div>
  <h2 style="font-size: 24px; color: red;">Welcome to our website</h2>
  <p style="font-size: 18px; color: blue;">This is some text with hard-coded styles.</p>
  <button style="background-color: green; color: white;">Click me</button>
</div>

Good Example (Separate Styles in a CSS File) :

<!-- good-example.component.html -->
<div>
  <h2>Welcome to our website</h2>
  <p>This is some text with Angular-applied styles.</p>
  <button (click)="onButtonClick()">Click me</button>
</div>

/* styles.css or component-specific styles file */
h2 {
  font-size: 24px;
  color: red;
}

p {
  font-size: 18px;
  color: blue;
}

button {
  background-color: green;
  color: white;
}

 

Angular Dropdown With Search And Multi Select.   Quiz App In Angular.

Related Articles

Back to top button