In Angular, components are the building blocks of the application. Often, these components need to communicate with each other to share data and functionality. This module will cover various ways to achieve component interaction in Angular.
Key Concepts
- Parent to Child Communication: Using
@Input
decorator. - Child to Parent Communication: Using
@Output
decorator and EventEmitter. - ViewChild and ContentChild: Accessing child components and DOM elements.
- Service-based Communication: Sharing data using services.
Parent to Child Communication
Using @Input
Decorator
The @Input
decorator allows a parent component to bind a value to a property of a child component.
Example
Parent Component (app.component.ts):
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `<app-child [childMessage]="parentMessage"></app-child>`, }) export class AppComponent { parentMessage = "Message from Parent"; }
Child Component (child.component.ts):
import { Component, Input } from '@angular/core'; @Component({ selector: 'app-child', template: `<p>{{ childMessage }}</p>`, }) export class ChildComponent { @Input() childMessage: string; }
Explanation
- The parent component (
AppComponent
) passes theparentMessage
to the child component (ChildComponent
) using property binding[childMessage]="parentMessage"
. - The child component receives this value through the
@Input
decorated propertychildMessage
.
Child to Parent Communication
Using @Output
Decorator and EventEmitter
The @Output
decorator and EventEmitter
allow a child component to emit events that a parent component can listen to.
Example
Parent Component (app.component.ts):
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `<app-child (messageEvent)="receiveMessage($event)"></app-child> <p>{{ message }}</p>`, }) export class AppComponent { message: string; receiveMessage($event: string) { this.message = $event; } }
Child Component (child.component.ts):
import { Component, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-child', template: `<button (click)="sendMessage()">Send Message</button>`, }) export class ChildComponent { @Output() messageEvent = new EventEmitter<string>(); sendMessage() { this.messageEvent.emit('Message from Child'); } }
Explanation
- The child component (
ChildComponent
) emits an event usingmessageEvent
when the button is clicked. - The parent component (
AppComponent
) listens to this event using(messageEvent)="receiveMessage($event)"
and updates itsmessage
property.
ViewChild and ContentChild
Accessing Child Components and DOM Elements
@ViewChild
and @ContentChild
decorators are used to access child components and DOM elements in the parent component.
Example
Parent Component (app.component.ts):
import { Component, ViewChild, AfterViewInit } from '@angular/core'; import { ChildComponent } from './child.component'; @Component({ selector: 'app-root', template: `<app-child></app-child> <button (click)="accessChild()">Access Child</button>`, }) export class AppComponent implements AfterViewInit { @ViewChild(ChildComponent) child: ChildComponent; ngAfterViewInit() { console.log(this.child.childMessage); } accessChild() { alert(this.child.childMessage); } }
Child Component (child.component.ts):
import { Component } from '@angular/core'; @Component({ selector: 'app-child', template: `<p>Child Component</p>`, }) export class ChildComponent { childMessage = "Hello from Child"; }
Explanation
- The parent component (
AppComponent
) uses@ViewChild
to get a reference to theChildComponent
. - The
ngAfterViewInit
lifecycle hook is used to ensure the child component is fully initialized before accessing it. - The
accessChild
method demonstrates how to interact with the child component.
Service-based Communication
Sharing Data Using Services
Services can be used to share data and functionality between components.
Example
Message Service (message.service.ts):
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class MessageService { private messageSource = new BehaviorSubject<string>('default message'); currentMessage = this.messageSource.asObservable(); changeMessage(message: string) { this.messageSource.next(message); } }
Parent Component (app.component.ts):
import { Component, OnInit } from '@angular/core'; import { MessageService } from './message.service'; @Component({ selector: 'app-root', template: `<app-child></app-child> <button (click)="newMessage()">New Message</button>`, }) export class AppComponent implements OnInit { constructor(private messageService: MessageService) {} ngOnInit() { this.messageService.currentMessage.subscribe(message => console.log(message)); } newMessage() { this.messageService.changeMessage('Hello from Parent'); } }
Child Component (child.component.ts):
import { Component, OnInit } from '@angular/core'; import { MessageService } from './message.service'; @Component({ selector: 'app-child', template: `<p>{{ message }}</p>`, }) export class ChildComponent implements OnInit { message: string; constructor(private messageService: MessageService) {} ngOnInit() { this.messageService.currentMessage.subscribe(message => this.message = message); } }
Explanation
- The
MessageService
uses aBehaviorSubject
to hold and share the current message. - Both the parent and child components inject the
MessageService
and subscribe tocurrentMessage
. - The parent component changes the message using
changeMessage
, which updates the message for all subscribers.
Practical Exercises
Exercise 1: Parent to Child Communication
- Create a parent component that passes a list of items to a child component.
- The child component should display the list of items.
Solution:
Parent Component (app.component.ts):
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `<app-child [items]="items"></app-child>`, }) export class AppComponent { items = ['Item 1', 'Item 2', 'Item 3']; }
Child Component (child.component.ts):
import { Component, Input } from '@angular/core'; @Component({ selector: 'app-child', template: `<ul> <li *ngFor="let item of items">{{ item }}</li> </ul>`, }) export class ChildComponent { @Input() items: string[]; }
Exercise 2: Child to Parent Communication
- Create a child component that emits an event when a button is clicked.
- The parent component should listen to this event and display a message.
Solution:
Parent Component (app.component.ts):
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `<app-child (notify)="onNotify()"></app-child> <p>{{ message }}</p>`, }) export class AppComponent { message: string; onNotify() { this.message = 'Notification received from Child'; } }
Child Component (child.component.ts):
import { Component, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-child', template: `<button (click)="notifyParent()">Notify Parent</button>`, }) export class ChildComponent { @Output() notify = new EventEmitter<void>(); notifyParent() { this.notify.emit(); } }
Summary
In this module, we covered various methods for component interaction in Angular:
- Parent to Child Communication: Using
@Input
decorator. - Child to Parent Communication: Using
@Output
decorator and EventEmitter. - ViewChild and ContentChild: Accessing child components and DOM elements.
- Service-based Communication: Sharing data using services.
Understanding these methods is crucial for building complex, interactive Angular applications. In the next module, we will explore directives and pipes, which are essential for manipulating the DOM and transforming data in Angular applications.
Angular 2+ Course
Module 1: Introduction to Angular
Module 2: TypeScript Basics
- Introduction to TypeScript
- TypeScript Variables and Data Types
- Functions and Arrow Functions
- Classes and Interfaces