How to Share Data Between Components in Angular Without a Parent-Child Relationship
Naz Islam
Posted on August 31, 2024
We build robust front-end applications using Angular. In Angular, the UI elements are separate components and each component can communicate to other components, thus maintaining an interactive behavior, while they keep stand-alone components. We know that in Angular, a component can contain another component in its template to maintain a parent-child relationship. However, what if the components are not connected? What if they are two separate stand-alone components without a parent-child relationship?
Let’s assume our components are configured as below.
If you check this diagram, you see that our App Component calls two separate components SelectionComponent and AccountComponent, which don’t have a parent-child relationship. Also, let’s look at how AppComponent calls them in its template.
app.component.ts
<app-selection></app-selection>
<app-account></app-account>
Angular Service comes to the rescue!
In situations like this, we can use a service to communicate the data between these components. Take a look at the below diagram.
Let’s start by creating a new Angular project to describe how this will work.
💡 Here, before we begin, it's worth mentioning that I'm using Angular v.18 as that's the most updated version as per date. You can see in the screenshot, my current Angular version is 18.2.1, and Node version is 20.17.0.Start a new Angular project with this command.
ng new angular-parent-child
Once the project is built, navigate to the project and create two components SelectionComponent and AccountComponent.
ng g c components/selection
ng g c components/account
Let’s remove everything from app.component.html
and include the below lines to call them from AppComponent.
app.component.html
<app-selection></app-selection>
<app-account></app-account>
app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { SelectionComponent } from './components/selection/selection.component';
import { AccountComponent } from './components/account/account.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, SelectionComponent, AccountComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'angular-parent-child';
}
Use SelectionComponent to select an account
In the SelectionComponent we will instantiate a button for 3 accounts, and based on the selection, we want to show the amount in these accounts.
selection.component.ts
import { Component } from "@angular/core";
import { AccountService } from "../../service/account.service";
@Component({
selector: "app-selection",
standalone: true,
imports: [],
templateUrl: "./selection.component.html",
styleUrl: "./selection.component.css"
})
export class SelectionComponent {
accountName1: string = "Account 1";
accountName2: string = "Account 2";
accountName3: string = "Account 3";
amount1: number = 100;
amount2: number = 200;
amount3: number = 300;
constructor() {}
updateAccount(accountName: string) {
}
}
selection.component.html
<p>Select account:</p>
<button (click)="updateAccount(accountName1)">{{accountName1}}</button>
<button (click)="updateAccount(accountName2)">{{accountName2}}</button>
<button (click)="updateAccount(accountName3)">{{accountName3}}</button>
Notice the function updateAccount(accountName)
takes accountName as input and it's not yet implemented. We are going to implement it in a minute.
Use AccountComponent to display the amount
In AccountComponent we define an attribute amount to capture amounts for the accounts.
account.component.ts
import { Component } from '@angular/core';
import { AccountService } from '../../service/account.service';
@Component({
selector: 'app-account',
standalone: true,
imports: [],
templateUrl: './account.component.html',
styleUrl: './account.component.css'
})
export class AccountComponent {
amount: any;
constructor() {}
ngOnInit() {
}
}
account.component.html
<p>Amount in selected account:</p>
<p>Amount: {{amount}}</p>
Now at this point, you can run the server by the command below and check in the browser, both the components are rendered properly.
ng serve -o
localhost:4200
Creating Account Service
As we have our components setup, let's go ahead and create our service using the below command, which will create AccountService in the directory src/app/services/account
.
ng g s services/account
Let's have amount
field to hold amount for accounts.
account.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AccountService {
constructor() { }
private amount = new BehaviorSubject(0);
getAmount = this.amount.asObservable();
setAmount(amount: number) {
this.amount.next(amount);
}
}
In the above code:
We have a BehaviorSubject called
amount
and we are assigning the initial value to be 0.We created an observable called
getAmount
, which our components can subscribe to get the amount.We created method
setAmount
which takes the amount and assigns it to our private variablethis.amount
.
Injecting Account Service
1. Retrieve the amount from AccountService using AccountComponent
As we have the service available, now go ahead and inject our service in AccountComponent and get the amount by subscribing to the observable getAmount
in AccountService. Once we get the amount value, let's assign it to the component variable this.amount
.
import { Component } from '@angular/core';
import { AccountService } from '../../services/account.service';
@Component({
selector: 'app-account',
standalone: true,
imports: [],
templateUrl: './account.component.html',
styleUrl: './account.component.css'
})
export class AccountComponent {
amount: any;
// injecting accountService in selection.component.ts
constructor(private accountService: AccountService) {}
ngOnInit() {
this.accountService.getAmount.subscribe(amount => {
this.amount = amount;
});
}
}
After the code is compiled, let's check what amount got retrieved in our Angular application on localhost:4200/
Check that amount 0 was retrieved. Remember we had set the default value for amount BehaviorSubject to be 0 by this code private amount = new BehaviorSubject(0);
. That's why it's retrieving this to be 0 for now.
So, now we completed a component to service communication to retrieve amount.
2. Set amount to AccountService using SelectionComponent
Go ahead and inject our service in SelectionComponent and set amount based on which button was clicked.
selection.component.ts
import { Component } from '@angular/core';
import { AccountService } from '../../services/account.service';
@Component({
selector: 'app-selection',
standalone: true,
imports: [],
templateUrl: './selection.component.html',
styleUrl: './selection.component.css'
})
export class SelectionComponent {
accountName1: string = 'Account 1';
accountName2: string = 'Account 2';
accountName3: string = 'Account 3';
amount1: number = 100;
amount2: number = 200;
amount3: number = 300;
// injecting accountService in selection.component.ts
constructor(private accountService: AccountService) {}
updateAccount(accountName: string) {
if (accountName === this.accountName1) {
this.accountService.setAmount(this.amount1);
} else if (accountName === this.accountName2) {
this.accountService.setAmount(this.amount2);
} else if (accountName === this.accountName3) {
this.accountService.setAmount(this.amount3);
}
}
}
In the code above, we are doing the following:
We inject AccountService in SelectionComponent through the constructor.
updateAccount
method takes a name and based on the account name, it sets the amount by callingsetAmount()
method in our newly created AccountService.
Now let's update the template for SelectionComponent.
selection.component.html
<p>Select account:</p>
<button (click)="updateAccount(accountName1)">{{accountName1}}</button>
<button (click)="updateAccount(accountName2)">{{accountName2}}</button>
<button (click)="updateAccount(accountName3)">{{accountName3}}</button>
Let's check our server on localhost:4200/
and check the amount. Initially this would be 0. Now let's click on any of the account buttons and check how the amount gets updated. For example, if we click on Account2 button, the amount changes to 200.
Clicking other buttons like Account1 and Account3 will update the balance to 100 and 300 respectively.
So, now we are able to share data from SelectionComponent to the AccountComponent using a service, whereas AccountComponent is not a child-component.
If you find any issues or concerns rendering the project, please check the GitHub repository to troubleshoot. The project code should be available for clone in this GitHub repository: https://github.com/nazislam/angular-parent-child
Hope you enjoyed the blog. I'm working on building a SaaS product to improve productivity for professionals and sharing everything in my discord channel. Join my Discord channel for free by clicking this link: https://discord.gg/XmbrDrnVhr
Posted on August 31, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
August 31, 2024