18
JunIt is a real challenge for the angular developer to share the data between components. I have seen angular.io tutorials about data sharing and I found that the example is not so great to understand the concept of data sharing. So here in this tutorial, I have come up with a demonstration of data sharing concepts in angular with real-time scenarios, which you face in day-to-day angular development.
In an Angular application, we may have multiple components with a variety of functionality/features and while developing an application we may come across a situation where we need to share or pass the data from one component to another one, in that case, we can achieve that by using the concept of data sharing between the components, and for that in Angular, there are some provisions or ways to achieve the same that will be discussed further in this article.
Goals
Data Sharing Between Angular Components
Parent to Child: via Input
Child to Parent: via Output() and EventEmitter
Child to Parent: via ViewChild
Unrelated Components: via a Service
Specifications
In this tutorial, I have created a new angular project and demonstrated all four methods of data sharing mentioned in the goal section. So let us get started with real-time implementations.
Method1: Parent to Child via @Input
So let me put a problem statement for you then an explanation with the source code. Suppose you have one component, where you have got a list and you want to supply that list to the child component, How to do it ??? answer is @input. So what is @input: @input is a decorator which allows you to accept the input from the parent component. Input can be imported from @angular/core.
SO let us assume I have one component called the teacher and one component called the student. The teacher component has the list of all the teachers with their subject details, what they teach and we wanted to supply that list to the student component so that it can be displayed which teacher is going to what subject. The list of teachers is something like this
export class Teacher { name: string; subject:string; } export const Teachers = [ {name: 'Mr. Deep',subject:'Angular 6 in DotNet Techy YouTube Channel'}, {name: 'Mr. Gautam' ,subject:'C#, WEB API in DotNet Techy YouTube Channel'}, {name: 'Mr. DotNet Techy' ,subject:'High chart, chart js, prime ng, ag grid in DotNet Techy YouTube Channel '} ];
Now let us create a project
ng new DataSharing
Let us add parent and child component
ng g component teacher ng g component student
So here is the source code for both the component
Teacher component html
<h1> <span style="color: red;font-size:larger"> I am Teacher Component behaving as parent :) </span> </h1> <h2>{{principle}} controls {{teachers.length}} teachers</h2> <app-student *ngFor="let teacher of teachers" [teacher]="teacher" [principle]="principle"></app-student>
Teacher component ts
import { Component, OnInit } from '@angular/core'; import { Teachers } from '../model/teacher.model'; @Component({ selector: 'app-teacher', templateUrl: './teacher.component.html', styleUrls: ['./teacher.component.css'] }) export class TeacherComponent implements OnInit { teachers = Teachers; principle = 'Principle'; constructor() { } ngOnInit() { } }
Student component html
<h1> <span style="color: green;font-size:larger">I am Student Component behaving as child :)</span> </h1> <h3 style="color: yellowgreen;">{{teacher.name}} says:</h3> <p style="color:blueviolet;">I, {{teacher.name}}, I will teach you {{teacher.subject}}, {{principleName}}.</p>
Student component ts
import { Component,Input, OnInit } from '@angular/core'; import { Teacher } from '../model/teacher.model'; @Component({ selector: 'app-student', templateUrl: './student.component.html', styleUrls: ['./student.component.css'] }) export class StudentComponent implements OnInit { @Input() teacher: Teacher; @Input('principle') principleName: string; constructor() { } ngOnInit() { } }
So now if you notice student component ts then it has two @input properties.
@Input() teacher: Teacher; @Input('principle') principleName: string;
Which is getting used in teacher component HTML
<app-student *ngFor="let teacher of teachers" [teacher]="teacher" [principle]="principle"></app-student>
This is how the parent component is supplying the input to the child component
[teacher]="teacher" [principle]="principle"
So now if we go to our appcomponent HTML and render the teacher component like this
<app-teacher></app-teacher>
You will see parent-to-child communication in action. What we have in teachers is the teacher model which was discussed initially. So that's how you can achieve parent-to-child communication via @input.
Read More: Tips to Secure Your Angular Application
Method 2: Child to Parent via @Output and EventEmitter
So let us discuss what is @output, @output is a component decorator which becomes the output for the parent component and EventEmitter is something that has the capability to propagate the event from the child component to the parent component. So let us see this in action with a real-time example. So my problem statement is If I have one header component and I have a login page below the header component. Now Once the user enters the username and password in the login component and he wants to send the logged-in username to the header component at that point of time this @Output and EventEmitter work perfectly.
So here in my below example header is the parent component and login is the child component and from the login component, I would like to emit an event which will give the username to parent component i.e header component. So let us generate two components.
ng g component header ng g component Login
Now you need to write very simple html to take the username and password in login component and one button which will validate the user and emit an event to header component with logged-in user name. So here is the source code of both components.
header component html
<h1> <span style="color: red;font-size:larger"> I am Parent Component behaving as a parent for the @output and Event emitter :) </span> </h1> <div style="width: 100%;background-color: cornflowerblue"> <h2 style="color: white"> Welcome {{userName}} </h2> </div> <app-login (login)="onLogin($event)"></app-login>
header component ts
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.css'] }) export class HeaderComponent implements OnInit { constructor() { } ngOnInit() { } userName = ""; onLogin(user: string) { this.userName =user; } }
login component html
<h1> <span style="color: red;font-size:larger"> I am Login Component behaving as a child for the @output and Event emitter :) </span> </h1> <div class="container"> <form> <div class="form-group"> <label for="userName">User Name</label> <input type="text" name="userName" [(ngModel)]="userName" class="form-control" id="userName" required> <!-- <input type="text" name="userName" #ctrl="ngModel" required> --> </div> <div class="form-group"> <label for="Password">Password</label> <input type="password" class="form-control" id="Password"> </div> <button (click)="submit()">Login</button> </form> </div>
login component ts
import { Component, EventEmitter, Output,OnInit } from '@angular/core'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { userName: string = ''; @Output() login = new EventEmitter<string>(); constructor() { } ngOnInit() { } submit() { console.log(this.userName) this.login.emit(this.userName); } }
First thing to notice here in login component is
import { Component, EventEmitter, Output,OnInit } from '@angular/core';
Where we have imported for EventEmitter, Output which will support us to Emit the event and use the @OutPut.Second thing is
@Output() login = new EventEmitter<string>();
So login is going to be the event for the parent component which will receive the emitted event with string type value as mentioned in event emitter(EventEmitter<string>). Now on login button hit we are emitting the event like this
this.login.emit(this.userName);
So let us discuss the header component which is the parent component here. If you notice the header component is rendering the login component like below
<app-login (login)="onLogin($event)"></app-login>
Here login is @Output which has an event emitted from login compoent and it is calling a local method called onLogin which is setting the user name in header component.
onLogin(user: string) { this.userName =user; }
So now if you go and put this line of the code in app component HTML
<app-header></app-header>
Then you will @Output and event emitter in action.SO that's all about @Output and event emitter.
Method3: Child to Parent via @ViewChild
So here comes the question why @viewchild is needed if we have @output and event emitter to share the data from child to parent component, hold on it is needed because it gives extra control to parent component to access the child events. So in this example, ecparent component is the parent component as the name suggest and ecchild component is the child component. So here is the easily understandable code of components
ecparent component html
<h1> Election Commission Parent component. </h1> <h3>Vote Counting (via ViewChild)</h3> <button (click)="start()">Start Vote Counting</button> <button (click)="stop()">Stop Vote Counting(Lunch Break)</button> <div class="seconds">{{ seconds() }}</div> <app-ecchild></app-ecchild>
ecparent component ts
import { Component, OnInit,ViewChild } from '@angular/core'; import { EcchildComponent } from '../ecchild/ecchild.component'; @Component({ selector: 'app-ecparent', templateUrl: './ecparent.component.html', styleUrls: ['./ecparent.component.css'] }) export class EcparentComponent implements OnInit { @ViewChild(EcchildComponent) private counterComponent: EcchildComponent; seconds() { return 0; } ngOnInit() { } ngAfterViewInit() { // Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ... // but wait a tick first to avoid one-time devMode // unidirectional-data-flow-violation error setTimeout(() => this.seconds = () =>this.counterComponent.seconds, 0); } start() { this.counterComponent.start(); } stop() { this.counterComponent.stop(); } }
ecchild component html
<h1> Election commission Child compoent which will do Vote Counting </h1> <p>{{message}}</p>
ecchild component ts
import { Component, OnInit,OnDestroy } from '@angular/core'; @Component({ selector: 'app-ecchild', templateUrl: './ecchild.component.html', styleUrls: ['./ecchild.component.css'] }) export class EcchildComponent implements OnInit, OnDestroy { intervalId = 0; message = ''; seconds = 0; clearTimer() { clearInterval(this.intervalId); } ngOnInit() { this.start(); } ngOnDestroy() { this.clearTimer(); } start() { this.countDown(); } stop() { this.clearTimer(); this.message = `Holding at T-${this.seconds} seconds`; } private countDown() { this.clearTimer(); this.intervalId = window.setInterval(() => { this.seconds += 1; if (this.seconds === 0) { this.message = 'Completed counting!'; } else { if (this.seconds < 0) { this.seconds = 50; } // reset this.message = `Vote-${this.seconds} and counting going on`; } }, 1000); } }
So let us discuss the parent component first,Important line to discuss is
import { Component, OnInit,ViewChild } from '@angular/core';
So here we have imported the viewchild to provide the particular component as view child. Then next is supply the child component as view child as done in below lines
@ViewChild(EcchildComponent) private counterComponent: EcchildComponent;
And in html of a parent, we have these two important lines of the code
<button (click)="start()">Start Vote Counting</button> <button (click)="stop()">Stop Vote Counting(Lunch Break)</button>
If you notice ts then start and stop is the event of the child component.
start() { this.counterComponent.start(); } stop() { this.counterComponent.stop(); }0
Which is getting referenced by
private counterComponent: EcchildComponent;
Now it is high time to discuss the child component, If you notice it has two method as below which is getting called from parent compoent
start() { this.countDown(); } stop() { this.clearTimer(); this.message = `Holding at T-${this.seconds} seconds`; }
So if you want to see this in action, go to your app component HTML and put this line of code
<app-ecparent></app-ecparent>
So that is all about the @viewchild. Let us jump to the next method.
Method 4: Unrelated Components via a Service
So let us discuss the problem statement, suppose you have an approval flow implementation where, if one approver has approved the request it should go to next and he should take necessary actions. So here in this example, we have three components: basiccheck, advancecheck and finalcheck. Let us generate these components
ng g component basiccheck ng g component advancecheck ng g component finalcheck
And generate a service that will supply the data
ng g s approval
Now let us go and create a service that will supply the current stage of the approval to all of the above-mentioned components
So here is my service code
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class approvalService { private approvalStageMessage = new BehaviorSubject('Basic Approval is required!'); currentApprovalStageMessage = this.approvalStageMessage.asObservable(); constructor() { } updateApprovalMessage(message: string) { this.approvalStageMessage.next(message) } }
If you notice in service code we have these important things
import { BehaviorSubject } from 'rxjs';
Which is giving us the facility to use
private approvalStageMessage = new BehaviorSubject('Basic Approval is required!');
And
currentApprovalStageMessage = this.approvalStageMessage.asObservable();
Now let us jump on the code of the basic check component.
Basiccheck component html
<b>Current Message</b> -{{message}}<br>
<b>Updated approval message-</b> {{approvalText}}
<div class="container">
<form>
<div class="form-group">
<label for="approvalText">approval message</label>
<input type="text" name="approvalText" [(ngModel)]="approvalText" class="form-control" id="approvalText" >
</div>
<button (click)="submit()">Approve Basic</button>
</form>
</div>
Basiccheck component ts
import { Component, OnInit } from '@angular/core'; import { approvalService } from "../approval.service"; @Component({ selector: 'app-basiccheck', templateUrl: './basiccheck.component.html', styleUrls: ['./basiccheck.component.css'] }) export class BasiccheckComponent implements OnInit { message:string=""; approvalText:string=""; constructor(private appService:approvalService) { } ngOnInit() { this.appService.currentApprovalStageMessage.subscribe(msg => this.message = msg); } submit() { console.log(this.approvalText) this.appService.updateApprovalMessage(this.approvalText) } }
If you notice we have these important things in this
import { approvalService } from "../approval.service";
To import approval service. In ngOninit we are subscribing to the approval service and getting the current stage of the approval.
ngOnInit() { this.appService.currentApprovalStageMessage.subscribe(msg => this.message = msg); }
On click of approve basic button we are calling below method
submit() { console.log(this.approvalText) this.appService.updateApprovalMessage(this.approvalText) }
Which is updating the approval stage. Similarly, we have code in other components as well.
advancecheck component html
<b>Current Message</b> -{{message}}<br> <b>Updated approval message-<cc <div class="container"> <form> <div class="form-group"> <label for="approvalText">approval message</label> <input type="text" name="approvalText" [(ngModel)]="approvalText" class="form-control" id="approvalText" > </div> <button (click)="submit()">Approve Advance</button> </form> </div>
advancecheck component ts
import { Component, OnInit } from '@angular/core'; import { approvalService } from '../approval.service'; @Component({ selector: 'app-advancecheck', templateUrl: './advancecheck.component.html', styleUrls: ['./advancecheck.component.css'] }) export class AdvancecheckComponent implements OnInit { message:string=""; approvalText:string=""; constructor(private appService:approvalService) { } ngOnInit() { this.appService.currentApprovalStageMessage.subscribe(msg => this.message = msg) } submit() { console.log(this.approvalText) this.appService.updateApprovalMessage( this.approvalText); } }
finalcheck component html
<b>Current Message</b> -{{message}}<br> <b>Updated approval message-</b> {{approvalText}} <div class="container"> <form> <div class="form-group"> <label for="approvalText">User Name</label> <input type="text" name="approvalText" [(ngModel)]="approvalText" class="form-control" id="approvalText" > </div> <button (click)="submit()">Approve Final</button> </form> </div>
finalcheck component ts
import { Component, OnInit } from '@angular/core'; import { approvalService } from '../approval.service'; @Component({ selector: 'app-finalcheck', templateUrl: './finalcheck.component.html', styleUrls: ['./finalcheck.component.css'] }) export class FinalcheckComponent implements OnInit { message:string=""; approvalText:string=""; constructor(private appService:approvalService) { } ngOnInit() { this.appService.currentApprovalStageMessage.subscribe(msg => this.message = msg); } submit() { this.appService.updateApprovalMessage(this.approvalText); } }
So if you want to see this in action, how it works, just go app component HTML and put these lines of the code.
<!-- Sharing data via services --> <app-basiccheck></app-basiccheck> <app-advancecheck></app-advancecheck> <app-finalcheck></app-finalcheck>
Complete app component HTML is here to test all the data sharing mechanisms.
<!-- For @input, Parent to Child: via Input --> <!-- <app-teacher></app-teacher> --> <!-- For @output and event emiitter, Child to Parent: via Output() and EventEmitter --> <!-- <app-header></app-header> --> <!-- child to parent communication using @ViewChild--> <!-- <app-ecparent></app-ecparent> --> <!-- Sharing data via services --> <app-basiccheck></app-basiccheck> <app-advancecheck></app-advancecheck> <app-finalcheck></app-finalcheck>
Summary
That is all about this tutorial of data sharing between components. I hope, you will use one of the methods for sharing data between components of your Angular application.
Take our free skill tests to evaluate your skill!

In less than 5 minutes, with our skill test, you can identify your knowledge gaps and strengths.