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

  1. Parent to Child Communication: Using @Input decorator.
  2. Child to Parent Communication: Using @Output decorator and EventEmitter.
  3. ViewChild and ContentChild: Accessing child components and DOM elements.
  4. 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 the parentMessage to the child component (ChildComponent) using property binding [childMessage]="parentMessage".
  • The child component receives this value through the @Input decorated property childMessage.

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 using messageEvent when the button is clicked.
  • The parent component (AppComponent) listens to this event using (messageEvent)="receiveMessage($event)" and updates its message 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 the ChildComponent.
  • 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 a BehaviorSubject to hold and share the current message.
  • Both the parent and child components inject the MessageService and subscribe to currentMessage.
  • The parent component changes the message using changeMessage, which updates the message for all subscribers.

Practical Exercises

Exercise 1: Parent to Child Communication

  1. Create a parent component that passes a list of items to a child component.
  2. 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

  1. Create a child component that emits an event when a button is clicked.
  2. 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.

© Copyright 2024. All rights reserved