I get asked a lot about best practices in terms of Angular architecture. As a result, in this post, I want to showcase three different ways to achieve the same kind of component communication in Angular, highlighting the pros and cons of each approach.
The example we’ll be working on is the following, where two components (a main component and a dialog component) communicate to open and close a dialog:
In this first attempt, my dialog component is entirely driven by inputs and outputs. Here is the TypeScript code of that component:
export class DialogComponent {isOpen = input(false);
title = input("Title");
onClose = output();
closePopup(): void {
this.onClose.emit('Pop-up window closed');
}
}
In this example, the parent component has to set isOpen to true to open the dialog. Then, the parent component has to revert that value to false when the dialog gets closed:
(onClose)="showDialog = false">
This dialog is great
What’s good about that approach is that our dialog is fully reusable and controllable by its parent. It’s a perfect presentation component, which means we can use the optimized onPush change detection strategy on it.
The downside is that the API isn’t obvious, and having to call (onClose)=”showDialog = false” doesn’t seem natural or straightforward. Why can’t the dialog close itself, right?
You can see the code for that first approach here on Stackblitz.
In this second approach, we want to simplify the API to control the dialog, so we update our component as follows:
export class DialogComponent {isOpen = signal(false);
title = input("Title");
closePopup(): void {
this.isOpen.set(false);
}
}
What we’ve done here is remove the input/output mechanism that decides when to show/hide the dialog. How do we control the dialog from the parent component, then?
We do so with viewChild, which gives us a reference to the DialogComponent, allowing us to use all public methods and attributes from that component.
In TypeScript:
export class App {
dialog = viewChild.required(DialogComponent);
}
And in the HTML template:
This dialog is great
This works great, the only downside is that the parent component has to know exactly how the dialog component works, and that there is a signal in there to control the visibility of the dialog window.
As a result, this creates a tighter coupling between the two components.
You can see that example in action on Stackblitz here.
Signal-based components brought us the model() function, which is a signal-based 2-way data binding, like ngModel, but for any component input. With that approach, all we change in the dialog is to call model() instead of signal() when initializing isOpen:
export class DialogComponent {isOpen = model(false);
title = input("Title");
closePopup(): void {
this.isOpen.set(false);
}
}
Then, in the parent component, we’re getting back to something closer to our initial approach, except that the non-obvious onClose output is gone:
This dialog is great
Note the syntax [(isOpen)] indicating a 2-way data binding, making it clear that the value of showDialog is always going to be in sync with the value of isOpen. We still have our dialog as a presentation component, the API is both explicit and minimalistic, and the amount of code to write and maintain is optimal.
You can find the code for this third approach on Stackblitz here.
As we saw, all three options work and have their pros and cons. The third one wins because it has the least amount of code while checking all the boxes in terms of reusability, API simplicity, architecture concepts (coupling, presentation component), and compatibility with onPush (which means zoneless-ready, too).
That said, getting to that approach might not have been obvious at first, which is why keeping an eye open for new framework features and understanding how they can be used in ways that simplify our code is always a good idea.
My name is Alain Chautard. I am a Google Developer Expert in Angular and a consultant and trainer at Angular Training, where I help web development teams learn and become proficient with Angular / React / JavaScript.
If you need any help learning web technologies, feel free to get in touch!
If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to my articles and Weekly Angular Newsletter to receive helpful weekly tips and updates about Angular.