Component Communication in Angular: Mastering @Input and @Output
In a modern Angular application, the user interface is built using a tree of components. To make these components dynamic and interactive, they need to talk to each other. This is known as Component Communication. The most common pattern is communication between a Parent component and its Child component using the @Input and @Output decorators.
Understanding the Parent-Child Relationship
Before diving into the code, let's visualize how data flows. In Angular, data flows down from parent to child and events flow up from child to parent.
[ Parent Component ]
|
| (Data Flow Down via @Input)
v
[ Child Component ]
|
| (Event Flow Up via @Output)
v
[ Parent Component ]
1. Passing Data Down: The @Input Decorator
The @Input() decorator is used to define a property in a child component that can receive values from its parent. Think of it like a function parameter for a component.
Example: Child Component
In the child component (e.g., user-profile.component.ts), we define a property decorated with @Input().
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-user-profile',
template: '<p>User Name: {{ username }}</p>'
})
export class UserProfileComponent {
@Input() username: string = '';
}
Example: Parent Component
In the parent component's HTML, we use Property Binding to pass data to the child.
<app-user-profile [username]="'John Doe'"></app-user-profile>
2. Sending Data Up: The @Output Decorator
The @Output() decorator allows a child component to raise an event to notify the parent about a change or an action (like a button click). This is used in combination with the EventEmitter class.
Example: Child Component
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child-button',
template: '<button (click)="notifyParent()">Click Me</button>'
})
export class ChildButtonComponent {
@Output() statusChange = new EventEmitter<string>();
notifyParent() {
this.statusChange.emit('Button was clicked in child!');
}
}
Example: Parent Component
The parent listens for the custom event using Event Binding.
<app-child-button (statusChange)="handleStatus($event)"></app-child-button>
// In Parent TypeScript:
handleStatus(message: string) {
console.log(message);
}
Real-World Use Case: Shopping Cart
Imagine a ProductListComponent (Parent) and a ProductItemComponent (Child). The parent passes the product details to the child using @Input. When the user clicks "Add to Cart" inside the child component, it notifies the parent via @Output so the total price can be updated in the main dashboard.
Common Mistakes to Avoid
- Forgetting the Decorator: Beginners often forget to add the
@Input()or@Output()parentheses. - Wrong Import: Ensure
Input,Output, andEventEmitterare imported from@angular/core. - Naming Conflicts: Using the same name for an
@Inputproperty and a local variable can lead to confusion. - Not Using $event: When catching an
@Output, the data sent by the child is always contained in the$eventvariable in the HTML template.
Interview Notes
- What is @Input? It is a decorator that marks a class field as an input property and supplies configuration metadata. It allows data to flow from parent to child.
- What is @Output? It is a decorator that marks a class field as an output property and provides an instance of
EventEmitterto send data from child to parent. - Can we use @Input for complex objects? Yes, you can pass strings, numbers, arrays, or complex JSON objects.
- What is the difference between Property Binding and @Input? Property binding is the mechanism in the template, while
@Inputis the decorator in the TypeScript class that enables that binding.
Summary
Component communication is the backbone of Angular applications. Use @Input to push data into a component and @Output with EventEmitter to signal actions back to the parent. For more complex scenarios involving deeply nested components, you might want to look into Services and RxJS (which we will cover in Topic 12) to avoid "Prop Drilling."
In the next lesson, we will explore Component Lifecycle Hooks to understand how to perform actions when these inputs change.