The power of forwardRef to fix circular dependencies 🔄 in Angular

Rmag Breaking News

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 and the angular codebase.

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.

@Component(
selector: app-parent,
templateUrl: parent.component.html,
standalone: true,
imports: [ChildComponent],
)
class ParentComponent {
@Input() fromChild!: boolean;
}
<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.

@Component({
selector: app-child,
templateUrl: child.component.html,
standalone: true,
imports: [
//👇 Here is where the magic happens
forwardRef(() => ParentComponent)],
})
export class ChildComponent {}
<div class=“child-container”>
<h2>Child component</h2>
<app-parent [fromChild]=“true”/>
</div>

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

ERROR TypeError: Cannot read properties of undefined

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

Thank you for exploring this topic with me. Feel free to connect with me on LinkedIn or GitHub and share your thoughts.

Happy Coding! 🍻

Leave a Reply

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