How do you pass data between components?
Angular applications often consist of multiple components that need to share information. Understanding how to effectively pass data between these components is fundamental for building robust and interactive applications. This guide covers the most common techniques for inter-component communication in Angular.
Parent to Child Communication (@Input())
To send data from a parent component to a child component, the @Input() decorator is used. The child component declares an input property, which the parent can then bind to using property binding in its template. This is a common and straightforward method for one-way data flow.
/* child.component.ts */
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template: `<h2>Child Component</h2><p>Received from parent: {{ message }}</p>`
})
export class ChildComponent {
@Input() message: string = '';
}
/* parent.component.html */
<app-child [message]="'Hello from Parent!'"></app-child>
Child to Parent Communication (@Output() and EventEmitter)
When a child component needs to send data or notify its parent component about an event, the @Output() decorator combined with EventEmitter is used. The child component emits events, and the parent component listens for these events using event binding.
/* child.component.ts */
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<h2>Child Component</h2>
<button (click)="sendMessage()">Send to Parent</button>
`
})
export class ChildComponent {
@Output() messageEvent = new EventEmitter<string>();
sendMessage() {
this.messageEvent.emit('Hello from Child!');
}
}
/* parent.component.html */
<app-child (messageEvent)="receiveMessage($event)"></app-child>
<p>Received from child: {{ receivedChildMessage }}</p>
/* parent.component.ts */
import { Component } from '@angular/core';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html'
})
export class ParentComponent {
receivedChildMessage: string = '';
receiveMessage(message: string) {
this.receivedChildMessage = message;
}
}
Communication via Services (for Unrelated Components)
For communication between components that do not have a direct parent-child relationship (e.g., sibling components, or components deeply nested in different branches of the component tree), a shared service is the recommended approach. Services can hold shared data and provide methods for components to interact with that data, often utilizing RxJS Observables (like Subject or BehaviorSubject) to broadcast changes.
/* data.service.ts */
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DataService {
private messageSource = new BehaviorSubject<string>('Default Message');
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message);
}
}
/* component-a.component.ts (Sender) */
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-component-a',
template: `
<h3>Component A</h3>
<button (click)="newMessage()">Send Message</button>
`
})
export class ComponentAComponent {
constructor(private dataService: DataService) { }
newMessage() {
this.dataService.changeMessage('Message from Component A');
}
}
/* component-b.component.ts (Receiver) */
import { Component, OnInit, OnDestroy } from '@angular/core';
import { DataService } from './data.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-component-b',
template: `
<h3>Component B</h3>
<p>Received: {{ message }}</p>
`
})
export class ComponentBComponent implements OnInit, OnDestroy {
message: string = '';
subscription!: Subscription;
constructor(private dataService: DataService) { }
ngOnInit() {
this.subscription = this.dataService.currentMessage.subscribe(message => this.message = message);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Summary Table of Communication Methods
| Method | Relationship | Description | Pros | Cons |
|---|---|---|---|---|
| @Input() | Parent to Child | Child component declares an input property; parent binds data to it. | Simple, direct, one-way data flow. | Only works for direct parent-child. |
| @Output() and EventEmitter | Child to Parent | Child emits events; parent listens and reacts. | Event-driven, clear separation of concerns. | Only works for direct parent-child. |
| Shared Service (with Observables) | Any (Unrelated, Sibling) | Service holds shared data (e.g., BehaviorSubject) and components subscribe to it. | Flexible, scalable for complex apps, supports multiple subscribers. | Can add complexity, requires careful subscription management. |