Open In App

Component Communication in Angular

Last Updated : 28 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Angular, as a robust front-end framework, allows you to build complex and interactive web applications by creating reusable components. One of the key aspects of building such applications is effective communication between these components.

There are a lot of instances where we need to transfer data and events between angular components.

The communication can be between parent and child components or between sibling components. We will try out 3 methods to transfer data on our angular application.

Steps to create an Angular application:

Step 1: Create an angular application using the following command.

ng new angular-com

Make sure angular is already installed in your system.

Step 2: Go inside the angular project

cd angular-com

Step 3: Next, we will run the application through the below command

ng serve --open

Step 4: Create the required component to share data between them.

ng generate component comp-alpha
ng generate component comp-beta

Folder Structure:fg

The updated Dependencies in package.json file will look like:

"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
}

Now, that we have our component created, we will proceed with adding some boilerplate code. You can add code in the following components to have our initial code ready. Add the code for the app component as in the below editor

HTML
<!-- app.component.html -->

<div style="
    display: flex;
    background-color: aqua;
    padding: 1rem;
    /* height: 20rem; */
    flex-direction: column;
  ">
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="display: flex; justify-content: center">
            This is parent component
        </div>
        <div style="display: flex; justify-content: center">
            <p>
                <input type="text" name="homeText" id="homeText" [(ngModel)]="homeText" />
            </p>
        </div>
        <div style="display: flex; justify-content: center">
            Your Text - <b>{{ homeText }}</b>
        </div>
        <div style="display: flex; justify-content: center">
            <p>
                <button style="margin-right: 1rem">Pass to alpha component</button>
                <button>Pass to beta component</button>
            </p>
        </div>
    </div>
    <div style="display: flex; flex-direction: row; justify-content: center">
        <app-comp-alpha></app-comp-alpha>
        <app-comp-beta></app-comp-beta>
    </div>
</div>
HTML
<!-- comp-alpha.component.html -->

<div style="
    display: flex;
    background-color: lightgreen;
    padding: 1rem;
    height: 10rem;
    flex-direction: column;
    margin: 2rem;
  ">
    <div style="display: flex; flex-direction: row; justify-content: center">
        This is alpha component
    </div>
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            <input type="text" name="alpaText" id="alphaText" [(ngModel)]="alphaText" />
        </div>
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            Your Text - <b>{{ alphaText }}</b>
        </div>
        <div style="display: flex; flex-direction: row">
            <div style="margin-left: 0.6rem; margin-right: 0.3rem">
                <button>Pass to parent component</button>
            </div>
            <div style="margin-right: 0.6rem; margin-left: 0.3rem">
                <button>Pass to beta component</button>
            </div>
        </div>
    </div>
</div>
HTML
<!-- comp-beta.component.ts -->

<div style="
    display: flex;
    background-color: lightpink;
    padding: 1rem;
    height: 10rem;
    flex-direction: column;
    margin: 2rem;
  ">
    <div style="display: flex; flex-direction: row; justify-content: center">
        This is beta component
    </div>
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            <input type="text" name="alpaText" [(ngModel)]="betaText" id="betaText" />
        </div>
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            Your Text - <b>{{ betaText }}</b>
        </div>
        <div style="display: flex; flex-direction: row">
            <div style="margin-left: 0.6rem; margin-right: 0.3rem">
                <button>Pass to parent component</button>
            </div>
            <div style="margin-right: 0.6rem; margin-left: 0.3rem">
                <button>Pass to alpha component</button>
            </div>
        </div>
    </div>
</div>
JavaScript
//app.component.ts

import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
})
export class AppComponent {
    title = 'angular-com';
    homeText = 'Hello parent!';
}
JavaScript
//app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CompAlphaComponent } from './comp-alpha/comp-alpha.component';
import { CompBetaComponent } from './comp-beta/comp-beta.component';
import { FormsModule } from '@angular/forms';

@NgModule({
    declarations: [AppComponent, CompAlphaComponent, CompBetaComponent],
    imports: [BrowserModule, AppRoutingModule, FormsModule],
    providers: [],
    bootstrap: [AppComponent],
})
export class AppModule { }
JavaScript
//comp-alpha.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'app-comp-alpha',
    templateUrl: './comp-alpha.component.html',
    styleUrls: ['./comp-alpha.component.css'],
})
export class CompAlphaComponent implements OnInit {
    constructor() { }

    alphaText = 'Hello alpha!';

    ngOnInit(): void { }
}
JavaScript
//comp-beta.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'app-comp-beta',
    templateUrl: './comp-beta.component.html',
    styleUrls: ['./comp-beta.component.css'],
})
export class CompBetaComponent implements OnInit {
    constructor() { }

    betaText = 'Hello beta!';

    ngOnInit(): void { }
}

Output:

demo-page-2

Demo page


The blue background represents the parent component with two child components – alpha and beta. Next, we will be working on how to pass values and events through parent and child components and in between child components.

1. Passing data from parent to child through input binding

In angular, we use input bindings to pass data from parent to child components. In our example code above, we will pass data from the app component to the alpha component by using an input decorator. Consider the following code changes –

HTML
<!--comp-alpha.component.html-->

<div style="
    display: flex;
    background-color: lightgreen;
    padding: 1rem;
    height: 20rem;
    flex-direction: column;
    margin: 2rem;
  ">
    <div style="display: flex; flex-direction: row; justify-content: center">
        This is alpha component
    </div>
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            <input type="text" name="alpaText" id="alphaText" [(ngModel)]="alphaText" />
        </div>
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            Your Text - <b>{{ alphaText }}</b>
        </div>
        <div style="display: flex; flex-direction: row">
            <div style="margin-left: 0.6rem; margin-right: 0.3rem">
                <button>Pass to parent component</button>
            </div>
            <div style="margin-right: 0.6rem; margin-left: 0.3rem">
                <button>Pass to beta component</button>
            </div>
        </div>
        <div style="display: flex; flex-direction: column; padding: 0.5rem">
            <div style="padding-top: 0.5rem; padding-bottom: 0.5rem">
                <b>Whatever you see below is coming from parent component</b>
            </div>
            <div>{{ parentText }}</div>
        </div>
    </div>
</div>
HTML
<!--app.component.html-->


<div style="
    display: flex;
    background-color: aqua;
    padding: 1rem;
    /* height: 20rem; */
    flex-direction: column;
  ">
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="display: flex; justify-content: center">
            This is parent component
        </div>
        <div style="display: flex; justify-content: center">
            <p>
                <input type="text" name="homeText" id="homeText" [(ngModel)]="homeText" />
            </p>
        </div>
        <div style="display: flex; justify-content: center">
            Your Text - <b>{{ homeText }}</b>
        </div>
        <div style="display: flex; justify-content: center">
            <p>
                <button style="margin-right: 1rem">Pass to alpha component</button>
                <button>Pass to beta component</button>
            </p>
        </div>
    </div>
    <div style="display: flex; flex-direction: row; justify-content: center">
        <app-comp-alpha [textFromParent]="homeText"></app-comp-alpha>
        <app-comp-beta></app-comp-beta>
    </div>
</div>
JavaScript
//comp-alpha.component.ts

import { Component, Input, OnInit } from '@angular/core';

@Component({
    selector: 'app-comp-alpha',
    templateUrl: './comp-alpha.component.html',
    styleUrls: ['./comp-alpha.component.css'],
})
export class CompAlphaComponent implements OnInit {
    constructor() { }

    alphaText = 'Hello alpha!';

    ngOnInit(): void { }

    @Input('textFromParent') parentText = '';
}


Output:

My-Movie-(1)

Passing values from parent to child component


Here, we are using @Input() decorator to bind the property we want to pass from parent to child. In the comp-alpha.component.ts, we have added the following code

  @Input('textFromParent') parentText = '';

And, in the app.component.html, we are passing the value to this property –

 <app-comp-alpha [textFromParent]="homeText"></app-comp-alpha>

2. Listening to events from the child component in the parent component

In the previous method, we passed values from parent to child. To listen to any event happening in the child component like – button-click, saving completion, page load, etc., we will use an event listener to accomplish this.

Consider below changes in code –

HTML
<!--comp-beta.component.html -->

<div style="
    display: flex;
    background-color: lightpink;
    padding: 1rem;
    height: 20rem;
    flex-direction: column;
    margin: 2rem;
  ">
    <div style="display: flex; flex-direction: row; justify-content: center">
        This is beta component
    </div>
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            <input type="text" name="alpaText" [(ngModel)]="betaText" id="betaText" />
        </div>
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            Your Text - <b>{{ betaText }}</b>
        </div>
        <div style="display: flex; flex-direction: row">
            <div style="margin-left: 0.6rem; margin-right: 0.3rem">
                <button (click)="passToParent()">Pass to parent component</button>
            </div>
            <div style="margin-right: 0.6rem; margin-left: 0.3rem">
                <button>Pass to alpha component</button>
            </div>
        </div>
    </div>
</div>
HTML
<!--app.component.html -->

<div style="
    display: flex;
    background-color: aqua;
    padding: 1rem;
    /* height: 20rem; */
    flex-direction: column;
  ">
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="display: flex; justify-content: center">
            This is parent component
        </div>
        <div style="display: flex; justify-content: center">
            <p>
                <input type="text" name="homeText" id="homeText" [(ngModel)]="homeText" />
            </p>
        </div>
        <div style="display: flex; justify-content: center">
            Your Text - <b>{{ homeText }}</b>
        </div>
        <div style="display: flex; justify-content: center">
            <p>
                <button style="margin-right: 1rem">Pass to alpha component</button>
                <button>Pass to beta component</button>
            </p>
        </div>
    </div>
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="display: flex; justify-content: center">Child event Log:</div>
        <div *ngFor="let ev of evList" style="display: flex; justify-content: center">
            {{ ev }}
        </div>
    </div>
    <div style="display: flex; flex-direction: row; justify-content: center">
        <app-comp-alpha [textFromParent]="homeText"></app-comp-alpha>
        <app-comp-beta (childButtonClicked)="childButtonClicked($event)"></app-comp-beta>
    </div>
</div>
JavaScript
//comp-beta.component.ts

import { Component, EventEmitter, OnInit, Output } from '@angular/core';

@Component({
    selector: 'app-comp-beta',
    templateUrl: './comp-beta.component.html',
    styleUrls: ['./comp-beta.component.css'],
})
export class CompBetaComponent implements OnInit {
    constructor() { }

    betaText = 'Hello beta!';

    ngOnInit(): void { }

    @Output() childButtonClicked = new EventEmitter<string>();

    passToParent() {
        this.childButtonClicked.emit('Button clicked in beta component');
    }
}
JavaScript
//app.component.ts

import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
})
export class AppComponent {
    title = 'angular-com';
    homeText = 'Hello parent!';

    evList: string[] = [];

    childButtonClicked(event: string) {
        this.evList.push(event);
    }
}

To make this work, we first added an event emitter with @Output() decorator in comp-beta.component.ts. We added a method passToParent() to emit an event every time the button is clicked

  @Output() childButtonClicked = new EventEmitter<string>();

passToParent() {
this.childButtonClicked.emit('Button clicked in beta component');
}

Next, in the app.component.html, we added a method to listen to the emitted event

 <app-comp-beta (childButtonClicked)="childButtonClicked($event)"></app-comp-beta>

Every time, the event is emitted in the beta component, we add an array to string to display the event.

  evList: string[] = [];
childButtonClicked(event: string) {
this.evList.push(event);
}

Output:

child-to-parent

Event listening in a parent-from-child component

3. Using Services to pass data between sibling component

Services can be used to pass data and components between parent and child components or between sibling components. We will take up an example to showcase how to pass values using services between child components.

We will create a service component to pass values. In the terminal, execute the following command –

ng generate service data-connect

It will create a service class file named data-connect.service.ts

Consider the following code changes –

HTML
<!--comp-alpha.component.html -->

<div style="
    display: flex;
    background-color: lightgreen;
    padding: 1rem;
    height: 20rem;
    flex-direction: column;
    margin: 2rem;
  ">
    <div style="display: flex; flex-direction: row; justify-content: center">
        This is alpha component
    </div>
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            <input type="text" name="alpaText" id="alphaText" [(ngModel)]="alphaText" />
        </div>
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            Your Text - <b>{{ alphaText }}</b>
        </div>
        <div style="display: flex; flex-direction: row">
            <div style="margin-left: 0.6rem; margin-right: 0.3rem">
                <button>Pass to parent component</button>
            </div>
            <div style="margin-right: 0.6rem; margin-left: 0.3rem">
                <button (click)="passToBeta()">Pass to beta component</button>
            </div>
        </div>
        <div style="display: flex; flex-direction: column; padding: 0.5rem">
            <div style="padding-top: 0.5rem; padding-bottom: 0.5rem">
                <b>Whatever you see below is coming from parent component</b>
            </div>
            <div>{{ parentText }}</div>
        </div>
        <div style="display: flex; flex-direction: column; padding: 0.5rem">
            <div style="padding-top: 0.5rem; padding-bottom: 0.5rem">
                <b>Whatever you see below is coming from beta component</b>
            </div>
            <div>{{ dataService.getValue("alpha") }}</div>
        </div>
    </div>
</div>
HTML
<!-- comp-beta.component.html -->

<div style="
    display: flex;
    background-color: lightpink;
    padding: 1rem;
    height: 20rem;
    flex-direction: column;
    margin: 2rem;
  ">
    <div style="display: flex; flex-direction: row; justify-content: center">
        This is beta component
    </div>
    <div style="display: flex; flex-direction: column; justify-content: center">
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            <input type="text" name="alpaText" [(ngModel)]="betaText" id="betaText" />
        </div>
        <div style="
        display: flex;
        margin-top: 1rem;
        margin-bottom: 1em;
        justify-content: center;
      ">
            Your Text - <b>{{ betaText }}</b>
        </div>
        <div style="display: flex; flex-direction: row">
            <div style="margin-left: 0.6rem; margin-right: 0.3rem">
                <button (click)="passToParent()">Pass to parent component</button>
            </div>
            <div style="margin-right: 0.6rem; margin-left: 0.3rem">
                <button (click)="passToAlpha()">Pass to alpha component</button>
            </div>
        </div>
        <div style="display: flex; flex-direction: column; padding: 0.5rem">
            <div style="padding-top: 0.5rem; padding-bottom: 0.5rem">
                <b>Whatever you see below is coming from alpha component</b>
            </div>
            <div>{{ dataService.getValue("beta") }}</div>
        </div>
    </div>
</div>
JavaScript
//data-connect.service.ts

import { Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root',
})
export class DataConnectService {
    constructor() { }

    alphaValue: string = 'This is default alpha value';
    betaValue: string = 'This is default beta value';

    setValue(comp: string, _value: string) {
        if (comp === 'alpha') {
            this.alphaValue = _value;
        } else if (comp === 'beta') {
            this.betaValue = _value;
        }
    }
    getValue(comp: string) {
        if (comp === 'alpha') {
            return this.alphaValue;
        } else if (comp === 'beta') {
            return this.betaValue;
        }
        return 'No component given';
    }
}
JavaScript
//comp-alpha.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { DataConnectService } from '../data-connect.service';

@Component({
    selector: 'app-comp-alpha',
    templateUrl: './comp-alpha.component.html',
    styleUrls: ['./comp-alpha.component.css'],
})
export class CompAlphaComponent implements OnInit {
    constructor(public dataService: DataConnectService) { }

    alphaText = 'Hello alpha!';

    ngOnInit(): void { }

    @Input('textFromParent') parentText = '';

    passToBeta() {
        this.dataService.setValue('beta', "Hey there! I'm alpha component");
    }
}
JavaScript
//comp-beta.component.ts

import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { DataConnectService } from '../data-connect.service';

@Component({
    selector: 'app-comp-beta',
    templateUrl: './comp-beta.component.html',
    styleUrls: ['./comp-beta.component.css'],
})
export class CompBetaComponent implements OnInit {
    constructor(public dataService: DataConnectService) { }

    betaText = 'Hello beta!';

    ngOnInit(): void { }

    @Output() childButtonClicked = new EventEmitter<string>();

    passToParent() {
        this.childButtonClicked.emit('Button clicked in beta component');
    }
    passToAlpha() {
        this.dataService.setValue('alpha', "Hey There! I'm beta");
    }
}

In the data-connect.service.ts, we have created two variables to store values from the alpha and beta components respectively. Additionally, we have added two methods to get and set values of these two variables –

 alphaValue: string = 'This is default alpha value';
betaValue: string = 'This is default beta value';

setValue(comp: string, _value: string) {
if (comp === 'alpha') {
this.alphaValue = _value;
} else if (comp === 'beta') {
this.betaValue = _value;
}
}
getValue(comp: string) {
if (comp === 'alpha') {
return this.alphaValue;
} else if (comp === 'beta') {
return this.betaValue;
}
return 'No component given';
}

We are injecting the service inside the alpha and beta components using the class constructor –

  constructor(public dataService: DataConnectService) {}

We are calling the setter event when the button is clicked to set the value of the service variable as below –

  passToBeta() {
this.dataService.setValue('beta', "Hey there! I'm alpha component");
}

Correspondingly, in the HTML component, we are getting the value using the following code –

<div>{{ dataService.getValue("alpha") }}</div>

The service we have declared is injected at the root level as declared in the component decorator meta-data.

@Injectable({
providedIn: 'root',
})

This means there is only one instance of service created at the root level and it is shared across all components. So any change in the service variable is reflected at all the places where the service is injected. So, this way, we can utilize the service for passing the data.

Output:

child-to-child-tranfer

Passing data in between child components



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads