How to Share Data Between Components in Angular Without a Parent-Child Relationship

nazislam

Naz Islam

Posted on August 31, 2024

How to Share Data Between Components in Angular Without a Parent-Child Relationship

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.

Diagram from draw.io about component structure in Angular

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>


Enter fullscreen mode Exit fullscreen mode

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.

Diagram from draw.io about component structure with service injected in Angular

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.

Angular-version-check-in-warp-terminal

Start a new Angular project with this command.



ng new angular-parent-child


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

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';
}


Enter fullscreen mode Exit fullscreen mode

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) {

  }

}


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

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() {

  }

}


Enter fullscreen mode Exit fullscreen mode

account.component.html



<p>Amount in selected account:</p>

<p>Amount: {{amount}}</p>


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

localhost:4200

Angular app rendered without service injected

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


Enter fullscreen mode Exit fullscreen mode

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);
  }
}


Enter fullscreen mode Exit fullscreen mode

In the above code:

  1. We have a BehaviorSubject called amount and we are assigning the initial value to be 0.

  2. We created an observable called getAmount, which our components can subscribe to get the amount.

  3. We created method setAmount which takes the amount and assigns it to our private variable this.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;
    });
  }

}


Enter fullscreen mode Exit fullscreen mode

After the code is compiled, let's check what amount got retrieved in our Angular application on localhost:4200/

http://localhost:4200/

Angular-app-rendeting-components-with-service-injected

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);
    }
  }

}


Enter fullscreen mode Exit fullscreen mode

In the code above, we are doing the following:

  1. We inject AccountService in SelectionComponent through the constructor.

  2. updateAccount method takes a name and based on the account name, it sets the amount by calling setAmount() 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>


Enter fullscreen mode Exit fullscreen mode

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.

Setting-amount-in-accountService-from-selectionComponent

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

💖 💪 🙅 🚩
nazislam
Naz Islam

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