How to communicate between components
We distinguish several ways of communication between components: parent to child, child to parent and any to any.
In this post I will show an example of the implentation of each of the ways.
Example
The application has three components nav and profile which has a child profile-edit.
1. I would like the name from the profile to be send to the profile edit.
2. When changing name in profile-edit, I would like to pass this value to the parent (profile component).
3. When changing name in profile-edit, I would like to refresh name value in the nav component.
Parent to child
Passing data from parent to child is possible thanks to @Input annotation.
The parent has a defined value name:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-profile', templateUrl: './Profile.component.html', styleUrls: ['./Profile.component.scss'] }) export class ProfileComponent implements OnInit { name = 'Pawel'; constructor() { } ngOnInit() { } }
Now you must declare input in your child:
import { Component, OnInit, Input } from '@angular/core'; @Component({ selector: 'app-profile-edit', templateUrl: './Profile-edit.component.html', styleUrls: ['./Profile-edit.component.scss'] }) export class ProfileEditComponent implements OnInit { @Input() name: string; constructor() { } ngOnInit() { } }
Then passing the instance of the profile-edit component in the parent of the name parameter looks like the following:
<app-profile-edit name="{{name}}"></app-profile-edit>
Child to parent
The @Output annotation allows us to achieve the opposite result, pass values from child to parent by using the EventEmitter.
@Output() nameChanged = new EventEmitter<string>();
EventEmitter is a generic type and we can determine what type the parameter will be. In our case – string.
To the button confirming the change of the name I pass the method that will call the event using the emit function
changeName() { this.nameChanged.emit(this.name); }
Now, to pass the method that will be triggered when the event is emitted, all you need to do is:
<app-profile-edit (nameChange)="nameChanged($event)"></app-profile-edit>
This will result in calling the nameChanged function of the parent and passing it in the form of an argument – a new name.
Child:
<div class="form-group"> <label>Name</label> <input type="text" class="form-control" [(ngModel)]="name" placeholder="name"> <button class="btn btn-success" (click)="changeName()" style="margin-top:10px;">Change name</button> </div>
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-profile-edit', templateUrl: './Profile-edit.component.html', styleUrls: ['./Profile-edit.component.scss'] }) export class ProfileEditComponent implements OnInit { @Input() name: string; @Output() nameChanged = new EventEmitter<string>(); constructor() { } ngOnInit() { } changeName() { this.nameChanged.emit(this.name); } }
Parent:
<div class="row" style="margin: 20px;"> <div class="col-md-3"> <div class="card"> <div class="card-body"> <h5 class="card-title">{{name}}</h5> <p class="card-text">Some information about ..</p> </div> </div> </div> <div class="col-md-9"> <app-profile-edit name="{{name}}" (nameChange)="nameWasChanged($event)"></app-profile-edit> </div> </div>
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-profile', templateUrl: './Profile.component.html', styleUrls: ['./Profile.component.scss'] }) export class ProfileComponent implements OnInit { name = 'Pawel'; constructor() { } ngOnInit() { } nameChanged(name: string) { this.name = name; } }
In this way we can influence the change of the name from the child to the parent.
Any to any
For communication between unrelated components we will need a injectable service which is designed to provide method or properties across to any components. For this purpose I will use the BehaviorSubject which is a type of Observable (can be subscribed to, subscribers can receive updated results and subject is a observer which mean that we can send value to it).
BehaviorSubject needs a initial value, so at first I have to create new service and create new behavior subject from ‘rxjs’.
username = new BehaviorSubject<string>('Pawel');
So the username is now unobservable of type behavior subject so set a currentUsername property as observable.
currentUsername = this.username.asObservable();
We also need to add a method that will update the username in which we call the method next on the observable object which will notify observers about the change of state.
User service:
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UserService { username = new BehaviorSubject<string>('Pawel'); currentUsername = this.username.asObservable(); constructor() { } changeName(username: string) { this.username.next(username); } }
Now in nav component we need to inject userService to subscribe to the currentUsername.
import { UserService } from './../_services/user.service'; import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-nav', templateUrl: './nav.component.html', styleUrls: ['./nav.component.scss'] }) export class NavComponent implements OnInit { name: string; constructor(private userService: UserService) { } ngOnInit() { this.userService.currentUsername.subscribe(name => this.name = name); } }
Then we need to call the changeName method with useservice when changing the name in the profile-edit component.
import { UserService } from './../_services/user.service'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-profile-edit', templateUrl: './Profile-edit.component.html', styleUrls: ['./Profile-edit.component.scss'] }) export class ProfileEditComponent implements OnInit { @Input() name: string; @Output() nameChanged = new EventEmitter<string>(); constructor(private userService: UserService) { } ngOnInit() { } changeName() { this.nameChanged.emit(this.name); this.userService.changeName(this.name); } }
Which means notifying all subscribers of a change in value.
Result:
Whole code available on github.