While dealing with any Front-End framework, passing data between components is the first that needs to tackle with. Every framework has different way to handle this issue. Angular has several ways to do this. In this article, we will go through all possible ways to pass data between angular components .
In angular, components can have other components inside them, so a parent and child relation is there. Component which lies outside is parent and inside one is child. Lets look at different places where passing data is possible
Parent to child
When data stored in parent is required in child component, it can be done by following way.
Using @Input() decorator
@Input and @Output are the two property decorators which help us for parent to child or child to parent communication. In this case we need to bind @Input() decorator with some property on child component as shown in below code snippet.
//child.component.ts
import { Component, Input } from '@angular/core';
export class ChildComponent {
@Input() placeholderInChild!: string;
constructor(){}
}
<!-- child.component.html -->
<p>{{placeholderInChild}}</p>
Here, @Input decorator is applied on placeholderInChild property. So, value coming from parent will be stored in placeholderInChild property.
Now, in parent html, where the child is getting called we need to pass data stored in parent’s .ts file as described in below code snippet.
<!-- Parent.component.html -->
<div>
<app-child placeholderInChild={{valueOfPropertyInParent}}></app-child>
</div>
//Parent.component.ts
import { Component } from '@angular/core';
export class ParentComponent {
valueOfPropertyInParent = 'someValue'
constructor(){}
}
Child to parent
When a piece of data stored in child is required in parent component, it can be done by following way.
Using @Output() decorator
Before getting straight to the code snippets, lets try to understand the data flow with the help of flow diagram.
To make the above flow work, first we need to declare a event emitter property in child with EventEmitter class and a method that emits the event, like given in following code snippet.
//Child.component.ts
export class ChildComponent {
@Output() eventInChild = new EventEmitter<string>(); //eventEmmiter property
constructor(){}
public triggerEventFromChild(childInput: string): void{ //event emitting method
this.eventInChild.emit(childInput)
}
}
Of course, we don’t want the event to be triggered randomly. So we need a action like a button click or scroll or any CTA. For an example, lets consider a button click and bind it with the trigger method that we have created like mentioned in below code snippet.
<!-- child.component.html -->
<input type="text" placeholder="value to be sent to parent" #childInput>
<button (click)="triggerEventFromChild(childInput.value)">Submit</button>
Here, on click of button we are triggering the event with data in input tag. We are done with child component.
Now, We just have to listen to this triggered event in parent component. For that, all we need to do is add the custom event on child tag in parent like below.
<!-- parent.component.html -->
<div>
<app-child (eventInChild)="appendValues($event)"></app-child> <!-- custom event -->
</div>
<div>
<ul>
<li *ngFor="let item of valuesFromChild">
{{item}}
</li>
</ul>
</div>
Here, eventInChild is @Output property declared in child and appendValues is a method in parent component which will receive data sent in event i.e. Input value. Here we are appending those values in array and showing them as a list in parent html.
//parent.component.ts
export class ParentComponent {
valuesFromChild: string[] = [];
constructor(){}
appendValues(item: string): void{
this.valuesFromChild.push(item)
}
}
Using Service
Service is the most convenient way to pass data between angular components. This also has two ways to achieve this. Lets discuss them one by one.
RxJs BehaviorSubject
RxJs BehaviorSubject is the best choice of passing data when you want to share updated data among multiple components. BehaviorSubject acts as a observable and a data store which contains updated shared data. During the initialisation, BehaviorSubject needs a default initial value and on subscription of this observable, current stored value will be shared. Lets understand this from a example. Let’s start with creating a service.
//data.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DataService {
private dataSource = new BehaviorSubject<string>('Initial data');
getData(){
return this.dataSource.asObservable();
}
setData(message: string){
this.dataSource.next(message);
}
}
Here, we have created a service called DataService with a BehaviorSubject and assigned a default value to it. We have also created a method to trigger a change in data i.e. setData() and share it among all the components where this observable i.e. getData() is subscribed.
//Component A
export class AComponent implements OnInit{
data!: string
constructor(
private dataService: DataService,
){}
ngOnInit(): void {
this.dataService.setData('changed data');
}
}
Thats it, now we can change the default data by calling the setData() method in service and it will change among all the components.
//Component B
export class BComponent implements OnInit{
data!: string;
constructor(
private dataService: DataService
){}
ngOnInit(): void {
this.dataService.getData().subscribe(data => this.data = data);
}
}
Setter and getter methods
Unlike RxJs Subject, this methods comes with some limitations. It doesn’t work on child to parent relation. This method involves setting the data in service from a component which is initialised first and getting the data from same service in another component which is initialised later in the app flow. Thats why child to parent data passing is not possible. Refer to following diagram to understand in better way.
Conclusion
Through this article, you have learned different ways to pass data within angular components. If you have any doubts, please feel free to ask in comment section. Would love to help. Thank you for reading this article.