Angular Component Communication
Implement patterns for component communication in Angular.
By EMEPublished: February 20, 2025
angularcomponentscommunicationparent childservices
A Simple Analogy
Component communication is like a phone system. Data flows through channels: parents to children, siblings through services.
Why Communication?
- Reusable: Components work together
- Encapsulation: Each component has responsibility
- Testing: Easier to test isolated components
- Scalability: Build complex UIs from simple parts
- Maintainability: Changes isolated to components
Parent to Child (@Input)
// Child component
@Component({
selector: 'app-card',
template: `<div>{{ title }}</div>`
})
export class CardComponent {
@Input() title: string;
}
// Parent component
@Component({
selector: 'app-parent',
template: `<app-card [title]="'Hello World'"></app-card>`
})
export class ParentComponent {}
Child to Parent (@Output)
// Child component
@Component({
selector: 'app-button-group',
template: `<button (click)="onSelect('save')">Save</button>`
})
export class ButtonGroupComponent {
@Output() selected = new EventEmitter<string>();
onSelect(action: string) {
this.selected.emit(action);
}
}
// Parent component
@Component({
selector: 'app-dialog',
template: `<app-button-group (selected)="handleAction($event)"></app-button-group>`
})
export class DialogComponent {
handleAction(action: string) {
console.log('Action:', action);
}
}
Sibling Communication (Service)
// Shared service
@Injectable({ providedIn: 'root' })
export class MessageService {
private messageSubject = new Subject<string>();
public message$ = this.messageSubject.asObservable();
sendMessage(msg: string) {
this.messageSubject.next(msg);
}
}
// Component A (sender)
@Component({
selector: 'app-sender'
})
export class SenderComponent {
constructor(private msgService: MessageService) {}
send() {
this.msgService.sendMessage('Hello sibling');
}
}
// Component B (receiver)
@Component({
selector: 'app-receiver'
})
export class ReceiverComponent implements OnInit {
constructor(private msgService: MessageService) {}
ngOnInit() {
this.msgService.message$.subscribe(msg => {
console.log('Received:', msg);
});
}
}
ViewChild Access
@Component({
selector: 'app-parent',
template: `<app-child #child></app-child>`
})
export class ParentComponent implements AfterViewInit {
@ViewChild('child') childComponent: ChildComponent;
ngAfterViewInit() {
this.childComponent.doSomething();
}
}
Best Practices
- One-way binding: Keep data flow simple
- Services: For complex shared state
- RxJS: Use observables for multiple updates
- Immutability: Don't mutate parent data
- Testing: Mock services for unit tests
Related Concepts
- Change detection
- Zone management
- State management (NgRx)
- Dependency injection
Summary
Communicate between Angular components using @Input/@Output for parent-child, services with RxJS for siblings.