The power of forwardRef to fix circular dependencies 🔄 in Angular
Tags: angular, circulardependency, javascript, webdev
Published: 2024-04-01 20:08:44


## Introduction

Circular dependencies often pose a challenge in software development, including Angular projects. While standalone components have helped alleviate many issues, circular dependencies can still arise, presenting obstacles to efficient development.

Before delving into the solution, it's essential to acknowledge that **refactoring** code can often mitigate *circular dependencies*. However, refactoring may **not** always be straightforward or feasible in certain scenarios.


That's where the `forwardRef` comes to the rescue!

According to Angular's documentation on `forwardRef`:

> [...] `forwardRef` is used when the `token` which we need to refer to for the purposes of DI is declared, but not yet defined. It is also used when the `token` which we use when creating a query is not yet defined. 
>
>`forwardRef` is also used to break circularities in standalone components imports.

_For more detailed information, I strongly recommend to have a look at [angular documentation](https://angular.io/api/core/forwardRef) and the [angular codebase](https://github.com/angular/angular/blob/0c20c4075af753d8e5eaecb5f7114a48ced5ad96/packages/core/src/di/forward_ref.ts#L69)._

 
## Under the Hood: The Mechanism of `forwardRef`

The concept of `forwardRef` is closely related to JavaScript closures. Both rely on **Deferred Resolutions** of values (such as services or components) until a later point in execution. While closures use captured variables as placeholders, `forwardRef` employs tokens for similar purposes.

## Circular dependencies between components

Consider resolving circular dependencies between standalone components – a common scenario in Angular development. Suppose we have a Parent component injecting a Child component and vice versa.

<!-- parent.component.ts -->
```typescript
@Component(
  selector: 'app-parent',
  templateUrl: 'parent.component.html',
  standalone: true,
  imports: [ChildComponent],
)
class ParentComponent {
  @Input() fromChild!: boolean;
}
```

<!-- parent.component.html -->
```html
<div class="parent-container">
  <h2>Parent Component</h2>
  @if (!fromChild) {
    <app-child/>
  }
</div>
```

To prevent infinite loop rendering, the Parent component includes an @Input called `fromChild`, indicating whether it's called from a child component. If so, it avoids re-rendering the child component to prevent further recursion.

<!-- child.component.ts -->
```typescript
@Component({
  selector: 'app-child',
  templateUrl: 'child.component.html',
  standalone: true,
  imports: [
    //👇 Here is where the magic happens
    forwardRef(() => ParentComponent)], 
})
export class ChildComponent {}
```

<!-- child.component.html -->
```html
<div class="child-container">
  <h2>Child component</h2>
   <app-parent [fromChild]="true"/>
</div>
```

Without utilizing the `forwardRef` mechanism, an undefined error would occur:

> <span style="color: red;">ERROR TypeError: Cannot read properties of undefined</span>

Using `forwardRef` ensures the reference of the component instantiation is fetched during runtime, resolving any undefined errors that would otherwise occur without it.

I hope this explanation provides clarity on the usage of `forwardRef`, but it's crucial to exercise caution to avoid overuse or misuse, which can potentially deteriorate the codebase.

Explore a live demonstration on StackBlitz for a hands-on experience: [Working Example](https://stackblitz.com/edit/stackblitz-starters-aix3yc?file=src%2Fparent%2Fparent.component.html)

Thank you for exploring this topic with me. Feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/loukaskotas/) or [GitHub](https://github.com/loukas-kotas) and share your thoughts.

Happy Coding! 🍻