Overview
To create modular and reusable components in Angular, component communication is crucial. Using @Input() to send data from a parent component to a child component and @Output() with an EventEmitter to return data is a typical pattern.
Actual Problem
In one of our projects, a parent component used @Input() to pass a selected item ID to a child component. Details were retrieved and displayed by the child component using that ID. However, the child didn't always show the change when the user quickly selected a different item. It occasionally didn't update at all or showed old data.
What Went Wrong
Angular's @Input() only updates when the value changes, but in certain situations (such as when the same ID is passed again after a brief delay), lifecycle hooks like ngOnChanges() failed to detect it. Additionally, ngOnInit() wasn't retriggered on each input change because the child only made one HTTP call.
Solution
We moved the API fetch logic from ngOnInit() to ngOnChanges() and added a proper check for changes:
@Input() itemId!: string;
ngOnChanges(changes: SimpleChanges): void {
if (changes['itemId'] && changes['itemId'].currentValue) {
this.loadItemDetails(changes['itemId'].currentValue);
}
}
loadItemDetails(id: string) {
this.http.get(`/api/items/${id}`).subscribe(res => {
this.itemDetails = res;
});
}
We also added a condition to prevent redundant API calls if the ID hasn’t changed.
Additional Fix
For cases where the same value might be passed again, we manually reset the input before reassigning:
this.selectedItemId = null;
setTimeout(() => this.selectedItemId = item.id, 0);
This trick forces Angular to treat the new input as a different value.
Conclusion
Angular’s @Input() and @Output() work well for component communication, but they don’t always detect subtle changes unless lifecycle hooks are handled properly. Moving logic to ngOnChanges() ensures that child components respond to dynamic data accurately.