[Angular 16] Pass Data Between Components with Examples

In this comprehensive guide we will discuss how to Pass data using @Input(), send data and listen to events from child to parent using @Output(), use @ViewChild to get child component instances in parent and more advanced techniques with examples. Component-based architecture is a key feature of Angular application, which are the reusable UI blocks…

By.

min read

In this comprehensive guide we will discuss how to Pass data using @Input(), send data and listen to events from child to parent using @Output(), use @ViewChild to get child component instances in parent and more advanced techniques with examples.

Component-based architecture is a key feature of Angular application, which are the reusable UI blocks with their own purpose to serve as part of the user interface. An Angular application typically consists of a tree of nested components that generally communicate with each other to share data and trigger events between each other.

We will discuss various techniques to set up a communication channel between components for the above scenarios including:

  • Using @Input decorator to pass data from parent to child
  • Using @Outpur decorator to emit event and data from child to parent component.
  • Using @ViewChild to access the child component instance in the parent component
  • Using RxJS Subjects, BehaviourSubject and Observables to broadcast data at any level.
  • Setup LocalStorage Get and Set functions in a Service.

At last, we will discuss the best practices so that will not affect the performance of the application while setting up the communication channels across components.

 

Why Pass Data Between Components?

There could be multiple types of relations where you need to pass data between Angular components for example:

  • The parent component needs to pass data to the child component
  • The child component needs to send back data or trigger the function in the parent component.
  • Siblings of a parent need to communicate with each other.
  • Any unrelated component A, that has no direct relation with Component B wants to share data.

Next, let’s check different ways to pass data between components in Angular.

 

1. Passing Data using Input()

The easiest way for a parent component to pass data to a child component is by using the @Input() decorator. It allows the parent component to bind data to a property of the child component.

We can define an input property in the child component class by importing the Input interface from @angular/core:

@Input() childMessage: string;

Thereafter, pass the data from the parent template to the child:

<app-child [childMessage]="parentMessage"></app-child>

The child can now access the passed-in value via this.childMessage.

Example of @Input() Usage:

We will pass the parentMessage value to the child component:

 

Parent Component:

import { Component, Input } from '@angular/core'; @Component({
  //...
})
export class ParentComponent {
  parentMessage = "message from parent";
}

 

Parent Template:

<app-child [childMessage]="parentMessage"></app-child>

 

Child Component:

@Component({
  //...
})
export class ChildComponent {

  @Input() childMessage!: string;

}

This allows the parent to dynamically pass data to the child. The child can then display or process the data.

 

Detect @Input Property Change in Child

In scenarios where you need to detect the value changes in the Input property inside the Child component, you can use the ngOnChanges hook. The ngOnChanges lifecycle hook to detect when an input property changes:

ngOnChanges Lifecycle Hook

@Component({
  //...
})
export class ChildComponent {

  @Input()
  childMessage: string;

  ngOnChanges(changes: SimpleChanges) {
    if (changes['childMessage']) {
      // do something
    }
  }
}

Here, we can check changes to see which properties changed.

 

2. Passing Data using Output()

We can also pass data from a child component back to the parent using the @Output() decorator and EventEmitter.

First, import the Output and EventEmitter interfaces from @angular/core, then define an Output property in the child component class:

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

Thereafter, you can Emit an event when data needs to be sent:

addNewItem(item) {
  this.newItemEvent.emit(item);
}
<app-child (newItemEvent)="onNewItemAdded($event)"></app-child>

Example of @Output Usage

Here, we are emitting a new item from the Child into the Parent via @Output property with the help of EvenEmitter:

 

Parent Component:

@Component({
  //...
})
export class ParentComponent {

  onNewItemAdded(data) {
    console.log("New item added: ", data);
  }
}

 

Parent Template:

<app-child (newItemEvent)="onNewItemAdded($event)"></app-child>

 

Child Component:

import { EventEmitter, Output } from '@angular/core';

@Component({
  //... 
})
export class ChildComponent {

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

  addNewItem(item) {
    this.newItemEvent.emit(item);
  }

}

This allows the child component to inform the parent of any new data. Input and Output are the basic building blocks for parent-child communication. Next, let’s look at some more advanced techniques.

 

3. ViewChild Decorator

The @ViewChild decorator provides access to child components, directives and DOM elements. This allows a parent to directly access child components and their properties and methods.

Example of Using @ViewChild:

Here, we are getting the ChildComponent instance using the @ViewChild and then accessing its variables and method as shown below:

import { ViewChild } from '@angular/core';
...
@ViewChild(ChildComponent) child: ChildComponent;

// access child properties
this.child.message = 'new message'; 

// call child methods
this.child.saveMessage();

 

4. Local Storage

Another way to share and preserve the data at the same time, we can leverage the browser’s local storage space. Here we are setting and getting the data from the local storage. These methods can be placed at util service and can be fetched and called from anywhere in the application or event across multiple applications placed at the same root domain.

This type of data fetching technique is useful where we need to maintain a state where components are placed separately without any relation.

Example of Getting and Setting Data at Local Storage:

// Save to local storage 
localStorage.setItem('appData', JSON.stringify(data));

// Read from local storage
const appData = JSON.parse(localStorage.getItem('appData'));

 

5. RxJS Subjects and Observables

RxJS Subjects and Observables can be used to pass data between any components with a publish-subscribe approach.

 

Example of Using RxJS Subjects:

Define the Subject in a util service:

// Observable data stream 
data$ = new Subject();

 

Then define a subscription in Component A or any number of components on any part of application:

// Subscribe to stream
data$.subscribe(value => {
  // do something
});

 

Now, we can publish any value from Component B:

// Publish data to stream
data$.next(value);

This allows unrelated components to communicate in a decoupled way.

 

6. Shared Service

Services can also be used to share data between components. Components can inject the shared service and use it to pass data.

Example of Shared Service:

Similar to Subjects we can use BehaviourSubject, with the only difference being that it takes an initial value and triggers at least once:

@Injectable()
export class DataService {
  private data = new BehaviorSubject(null);
  
  setData(value) {
    this.data.next(value);
  }

  getData() {
    return this.data.asObservable(); 
  }
}

This provides a flexible and scalable way for any components to share data.

That covers the major techniques for passing data between Angular components. Let’s now look at some best practices when sharing data.

 

Best Practices for Sharing Data

Here are some tips to follow when passing data:

  • Use @Input() for parent-to-child data flow
  • Use @Output() for child-to-parent data flow
  • For sibling components, use a shared service
  • For unrelated components, use a shared service, RxJS streams or local storage
  • Avoid overusing services – keep components loosely coupled
  • Use lifecycle hooks like ngOnChanges to respond to input changes
  • Make sure sharing data doesn’t introduce tight coupling between components
  • Always sanitize and validate data from external sources

Proper use of data-sharing techniques allows you to build complex Angular apps with reusable components.

 

Conclusion

In this tutorial, we covered the core methods for passing data between Angular components with examples. This includes:

  • @Input() and @Output() for parent-child communication
  • ViewChild for parents accessing child properties
  • Shared services to pass data between any components
  • Local storage and RxJS to persist data across components

This sets up a solid foundation for building component-driven applications in Angular.

Leave a Reply

Your email address will not be published. Required fields are marked *