Worst Practices In Angular – Common Mistakes to Avoid for Better Code Quality

Introduction:

Angular is a popular and powerful framework for building web applications, but like any technology, it has its share of bad practices. As a developer, it’s important to be aware of these worst practices so you can avoid them and write code that is maintainable, performant, and scalable.

In this article, we’ll explore some of the most common worst practices in Angular development and offer solutions and best practices for addressing them.
So let’s dive in and learn how to avoid these common mistakes in Angular development

Please take your breath and be patient.

1. Don’t clear your subscription:

It’s a very common practice which can lead to memory leak 🤯🤯

constructor(private userService:UserService){}

onInit(){
this.userService.user$.subscribe(user=>{...})
}

When you subscribe to an Observable in Angular, you are creating a connection between the Observable and the subscriber.
The Observable will continue to emit values until you unsubscribe from it, even if the component that subscribed to it is no longer in use.

To solve this simply use one of this:

  • takeUntil()
  • take()
  • takeUntilDestroy()
  • subscription.unsubscribe()
  • async Pipe

2. Use type “any” in every where

When you use the “any” type, you lose the benefits of TypeScript’s strong typing system, which helps catch errors and provides better code completion and documentation. Additionally, using “any” can make your code more difficult to understand and debug.

/** using any **/
user:any

/** using specifc type **/
interface User{
id:string;
name:string;
age:number;
}
user:User

By using specific types, you can ensure that your code is type-safe and easier to understand and maintain.
So please use TypeScript not JavaScritp 🙏🙏🙏

3. Binding static string as attribute to Html element

Sometimes you may need to provide a static value to a Html element in Angular. One way to do this is by binding a static string as an input using the square bracket notation, like this:

<input [placeholder]="'hello'">

While this will work as expected, it can cause unnecessary performance issues.
When you use square brackets to bind an input, Angular treats the input as a dynamic property that needs to be checked in every change detection cycle. This means that Angular will perform additional work to check the input, even though the value will never change.

To avoid this issue, you can simply pass the static value as a regular input without using the square bracket notation, like this:

<input placeholder="hello" >

4. Using Hostlistner or EventBinding on “scroll || mousemove…” event

It’s important to note that triggering change detection on every event can also lead to performance issues, especially if you’re listening to events that fire frequently, such as mousemove or scroll events

<div (scroll)="onscroll()">....</div>

To fix That you can use the NgZone service to run event listeners outside of Angular’s change detection cycle and manual change detection.

/** template **/ 
<div #el>...</div>

/** component **/
@ViewChild('el',{static:true})el:ElementRef;

constructor(private zone:NgZone,prviate cdr:ChangeDetectorRef){}

OnInit(){
 this.zone.runOutsideAngular(()=>{
  fromEvent(this.el.natvieElement,'scroll').subscribe((e)=>{...
    if(...){
     ...
     this.cdr.detectChange()  
     }
   })
 })
}

Another way to optimize the performance of your application when dealing with event listeners is to use RxAngular, which is a library for reactive programming in Angular that provides several features for optimizing performance. One of the features of RxAngular is zone-patched operators, which allow you to run code outside of Angular’s change detection cycle.

5. Importing shared module in app module

When you import the SharedModule in the AppModule, all of the components, directives, and pipes in the SharedModule are included in the main bundle, even if they’re not used in the AppModule. This can cause the bundle size to increase unnecessarily, which can lead to longer load times and slower performance.

@NgModule(
{...
imports:[SharedModule,...]
})
export class AppModule { }

Simply fix that by avoid importing it in the AppModule or any nested module inside it. Instead, you should only import the SharedModule in feature modules that need to use its shared components.

Note: using standalone components can be a good practice in Angular development

6. importing the CoreModule in multiple modules

The CoreModule is a module that contains services, providers, and other global configurations that are used throughout your application. It’s typically imported only once in the AppModule and should not be imported in any other modules.
If you import the CoreModule in multiple modules, you’re essentially creating multiple instances of the same services and providers, which can lead to unexpected behavior and errors. This can also cause circular dependencies, where modules depend on each other in a way that can’t be resolved.

To avoid this issue, you should only import the CoreModule once in the AppModule. If you need to use the services or providers from the CoreModule in other modules, you can import them directly from the CoreModule without importing the CoreModule itself.

7. Using setInterval or RxJS operators like interval and timer

setInterval is a JavaScript function that executes a given function at a set interval. Similarly, interval and timer are RxJS operators that emit values at a set interval. While these operators can be useful in some cases, using them in Angular can cause issues because they can trigger change detection frequently and impact performance.

OnInit(){
/* JavaScript function*/
setInterval(()=>{
  ...
  },1000)
/*Rxjs oprators*/
const subscription=interval(1000).subscribe(()=>{...})
}

Like eventBinding to fix this you need to run you code outside of zone and manual change detection.

8. Don’t split your code into multiple component

When you don’t split your code into multiple components, you may end up with a large, monolithic component that does too many things. This can make it harder to maintain and reuse the code, and can also lead to performance issues if the component becomes too complex.

To avoid this bad practice, you should split your code into multiple components whenever possible. Each component should be designed to handle a specific piece of functionality or user interface element, and should be reusable and maintainable.

9.Using default change detection strategy

The Default change detection strategy is the most commonly used strategy in Angular, and it works well for most applications. When using the Default strategy, Angular will compare the current values of a component’s inputs and internal state with their previous values, and then update the view if necessary.

default change detection

While the Default change detection strategy works well for most applications, it can be less performant in certain situations. For example, if a component has many inputs that change frequently, this can trigger unnecessary change detection cycles and impact the performance of the application.

onpush change detection

The OnPush change detection strategy is a way to optimize change detection by only checking a component’s inputs when they change or when an event is fired. This can help reduce the number of unnecessary change detection cycles and improve the performance of your application.

10. Using ShareReplay with all requset

shareReplay is an RxJS operator that allows you to share the results of an observable with subscribers, so that they don’t have to re-execute the observable. While shareReplay can be useful for caching and optimizing performance, using it with all requests can cause issues because it can fill up memory with cached data.

constructor(private listService:ListService){
  this.list$=this.listService.getList().pipe(shareReplay())
}

It’s important to use it correctly to avoid these issues.
Here are some best practices to follow:
1. Use shareReplay only when necessary
2. Use a cache timeout
3. Use a limited cache size
4. Using refCount to clear the cache when there are no subscriptions on

Finally :

Remember to be patient with yourself and the framework as you work through challenges and find solutions. Don’t rush through your code or make quick fixes without fully understanding the implications, as this can lead to unexpected bugs and performance issues.

By taking the time to understand Angular’s best practices and avoiding its worst practices, you can build robust and scalable applications that meet your users’ needs. So take a deep breath, stay calm, and keep learning!”