Data sharing between Angular components: a complete guide

Angular is a component-based Frontend framework which allows you to create multiple chunks and convert that large component into small segments. This allows us to maintain our code efficiently. Many companies rely on it for UI development because experts believe Angular is the future of enterprise web applications and could bring drastic transformation in the development sphere.

In this scenario, it’s important for communication between components. There are three scenarios, in which we can share data between two components:

  • Share data between parent component to child component
  • Share data between the child component to the parent component
  • Share data between two unrelated components

To share data between the parent component to a child component, we can use the @Input decorator. We use the @Input decorator, and the @ViewChild decorator for data sharing between the child and parent component.

To share data between two unrelated components, we will use a service with Behavior subject. That service holds the data which are sent by one component and the other components can use that data with help of that service. To use the data sent by one component and held by the service, the other components must subscribe the BehaviorSubject instance.

  • Share data between parent component to child component:

When we define a variable with @Input decorator in the child component, it allows that variable can value get changed from the parent component. The @Input decorator is defined in ‘@angular/core’.

parent.component.ts

import { Component, OnInit} from ‘@angular/core’;

@Component({

  selector: ‘app-parent’,

  template:`<app-child [childMessage]=” ParentMessage”><\app-child> `,

  styleUrls: [‘./parent.component.css’]

})

export class ParentComponent implements OnInit {

  parentMessage : “Parent Message”;

      constructor() {}

      ngOnInit() {}

}

child.component.ts

import { Component, Input, OnInit} from ‘@angular/core’;

@Component({

  selector: ‘app-child’,

  template`say{{message}}`,

  styleUrls: [‘./child.component.css’]

})

export class ChildComponent implements OnInit {

  @Input() childMessage: string;

  constructor() {}

  ngOnInit() {

  }

}

  • Share data between child component to parent component:

There are two ways to share data between the child component to the parent component. One is to use the @Output decorator and another is the @ViewChild decorator.

Here is the explanation of how to share data from the child component to the parent component using the @Output decorator.

The @Output decorator is defined in ‘@angular/core’.

In this, we create a function to receive messages from the child component.

And in the child component, we declare a variable with @Output decorator and set it to a new event emitter. Then we create a function that is triggered by a button when we click on that button and that function that calls emit on this event.

parent.component.ts:

import { Component, OnInit} from ‘@angular/core’;

@Component({

  selector: ‘app-parent’,

  template: `

    Message: {{message}}

    <app-child (messageEvent)=”receiveMessage($event)”></app-child>

  `,

  styleUrls: [‘./parent.component.css’]

})

export class ParentComponent implements OnInit {

  constructor() { }

  ngOnInit() {

  }

  message:string;

  receiveMessage($event) {

    this.message = $event

  }

}

child.component.ts:

import { Component, OnInit, Output, EventEmitter } from ‘@angular/core’;

@Component({

  selector: ‘app-child’,

  template: `

      <button (click)=”sendMessage()”>Send Message</button>

  `,

  styleUrls: [‘./child.component.css’]

})

export class ChildComponent implements OnInit {

  message: string = “Hello Parent!”

  @Output() messageEvent = new EventEmitter<string>();

  constructor() { }

  ngOnInit() {

  }

  sendMessage() {

    this.messageEvent.emit(this.message)

  }

}

But here comes the question of why use @ViewChild decorator when we have already @Output decorator. So @ViewChild decorator gives extra control to the parent component to access the child component’s events.

Here is the explanation of how to share data from the child component to the parent component using the @ViewChild decorator.

But in this, the child component will not be available until its view has been initialized. So we need to implement the AfterViewInit lifecycle hook in the parent component to receive data from the child component

parent.component.ts

import { Component, ViewChild, AfterViewInit } from ‘@angular/core’;

import { ChildComponent } from “../child/child.component”;

@Component({

  selector: ‘app-parent’,

  template: `

    Message: {{ message }}

    <app-child></app-child>

  `,

  styleUrls: [‘./parent.component.css’]

})

export class ParentComponent implements AfterViewInit {

  @ViewChild(ChildComponent) child;

  constructor() { }

  message:string;

  ngAfterViewInit() {

    this.message = this.child.message

  }

}

child.component.ts

import { Component, OnInit } from ‘@angular/core’;

@Component({

  selector: ‘app-child’,

  template: `

  `,

  styleUrls: [‘./child.component.css’]

})

export class ChildComponent implements OnInit {

  message = ‘Hello Parent!’;

  constructor() { }

  ngOnInit() {

  }

}

  • Share data between two unrelated components:

Till now we have seen that we send data from the parent component to the child component and the child component to the parent component. But what if we do not have that kind of relationship between components.

So to send data between components we create a service with BehaviorSubject. That service is hold the data sent by the one component and the other components who want to use that, those components can use that service to get data.

So basically, the service acts as Middleman by which you can send data from one component to another component that are unrelated.

We can use regular Rxjs BehaviorSubject for sharing data through the service between unrelated components.

data.service.ts

import { Injectable } from ‘@angular/core’;

import { BehaviorSubject } from ‘rxjs’;

@Injectable()

export class DataService {

  private messageSource = new BehaviorSubject(‘default message’);

  currentMessage = this.messageSource.asObservable();

  constructor() { }

  changeMessage(message: string) {

    this.messageSource.next(message)

  }

}

receiver.component.ts

import { Component, OnInit } from ‘@angular/core’;

import { DataService } from “../data.service”;

import { Subscription } from ‘rxjs’;

@Component({

  selector: ‘app-parent’,

  template: `

    {{message}}

  `,

  styleUrls: [‘./sibling.component.css’]

})

export class ParentComponent implements OnInit, OnDestroy {

  message:string;

  subscription: Subscription;

  constructor(private data: DataService) { }

  ngOnInit() {

    this.subscription = this.data.currentMessage.subscribe(message =>       this.message = message)

  }

  ngOnDestroy() {

    this.subscription.unsubscribe();

  }

}

sender.component.ts

import { Component, OnInit } from ‘@angular/core’;

import { DataService } from “../data.service”;

import { Subscription } from ‘rxjs’;

@Component({

  selector: ‘app-sibling’,

  template: `

    {{message}}

    <button (click)=”newMessage()”>New Message</button>

  `,

  styleUrls: [‘./sibling.component.css’]

})

export class SiblingComponent implements OnInit, OnDestroy {

  message:string;

  subscription: Subscription;

  constructor(private data: DataService) { }

  ngOnInit() {

    this.subscription = this.data.currentMessage.subscribe(message => this.message = message)

  }

  ngOnDestroy() {

    this.subscription.unsubscribe();

  }

  newMessage() {

    this.data.changeMessage(“Hello from Sender to Receiver”)

  }

}

Conclusion

Angular provides several methods for sending data across components, from which the user may choose the optimal one for their purposes. The service technique is preferred since it is independent of the component relationship. In this blog, we have learned three ways of data sharing, i.e., data sharing between parent component to a child component, next child to the parent component, and then data sharing between two unrelated components.