It’s very common in modern web development to handle the communication between the nearest components. The most obvious data flow is a parent to child. In this post, I’d like to show how to share the data between such components in both directions.
Passing data to children
Passing the data down to the children’s components is very easy. First, we need to have a parent component.
app.component.html
<p>Item clicked {{ activeElement }}</p>
<div class="box__container">
<app-box *ngFor="let box of boxCollection"
[details]="box"
[isActive]="box.name === activeElement"
>
</app-box>
</div>
In the parent component, the app-box
component is rendered with the ngFor
directive from the collection defined in the component. I created a very simple data set.
interface IBox {
id: number;
name: string;
}
boxCollection: IBox[] = [
{
id: 1,
name: 'Uruguay'
},
{
id: 2,
name: 'Mongolia'
},
{
id: 3,
name: 'Japan'
},
{
id: 4,
name: 'Moldova'
},
{
id: 5,
name: 'Rwanda'
}
];
To each of the box components, the object with details is passed using attributes in square brackets. In this example it’s[details]
. Here the [isActive]
attribute is also passed to the box component, but its usage will be shown later.
All we need right now is to receive these attributes in the Box component, using the @Input()
decorator.
box.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-box',
templateUrl: './box.component.html',
styleUrls: ['./box.component.scss']
})
export class BoxComponent {
@Input() isActive: boolean;
@Input() details;
}
Each BoxComponent
will receive the appropriate details with this decorator. Now, the data received from the parent can be used in a children’s component template. In this example, the name is displayed in the box.
box.component.html
<div class="box" [ngClass]="isActive && 'box--active'">
<h1>{{details.name}}</h1>
</div>
Passing data from children to parent
This data flow is less obvious but possible. In this case, we need the @Output()
decorator.
Each of the children component will have a button, and we want to pass the details from the box up to the parent.
First, we have to create an Output
in the box component and add a method to handle the output action.
box.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-box',
templateUrl: './box.component.html',
styleUrls: ['./box.component.scss']
})
export class BoxComponent {
@Output() boxIdEmitter = new EventEmitter<string>();
@Input() isActive: boolean;
@Input() details;
handleClick(event: string): void {
this.boxIdEmitter.emit(event);
}
box.component.html
<div class="box" [ngClass]="isActive && 'box--active'">
<h1>{{details.name}}</h1>
<button class="box__button" (click)="handleClick(details.name)">Make active</button>
</div>
The handleClick
method will be triggered on the button click. This will emit the given value from the children using EventEmitter
. Here it’s represented by the boxIdEmitter
. Now, this value has to be received in the parent component. We have to add the event binding in the parent’s template:
app.component.html
<app-box *ngFor="let box of boxCollection"
[details]="box"
(boxIdEmitter)="handleActiveClick($event)"
[isActive]="box.name === activeElement"
>
</app-box>
Notice the event binding (boxIdEmitter)="handleActiveClick($event)"
which is the core of the children - parent communication. The event name has to be the same as the EventEmitter
name in the BoxComponent
. When an event is received it will be handled by the given method. Here’s it’s a handleActiveClick($event)
.
Inside the app.component.ts
we have to define the method:
handleActiveClick(value: string): void {
this.activeElement = value;
}
The activeElement
component property is changed to the clicked box name. I added also another @Input()
to the children component here - the isActive
attribute. It evaluates to boolean and changes the styling in the BoxComponent
using ngClass
.
Attention!
Use EventEmitter
only to pass the data one level up. It’s not recommended to use the @Output()
when you have to pass the data through nested components, deeper than just one level. To handle such a case, creating a shared service is a much better option.
Conclusion
This short tutorial shows how we can handle the communication between the parent and children components in both ways. If there are more doubts about this topic, please visit the official Angular documentation page regarding this subject.