Angular share data between components

An Angular component is the basic build block of an Angular framework. The component is responsible for managing a template and providing it with the data and logic it needs. Components can contain other components. This is known as composition.

Components can communicate with each other in a loosely coupled manner and in this article we will learn Angular share data between components. There are various ways Angular’s components can share data, including the following:

  1. Passing data from parent to child using an Input binding (Property Binding).
  2. Passing data from child to parent using an Output binding (Event Binding).
  3. Sharing data using service among unrelated components (Service).
  4. Child to Parent: Sharing Data via ViewChild
  5. Child to Parent: Sharing Data via ViewChildren 
  6. Content projection from Parent to Child: @ContentChild and @ContentChildren decorator

What is Property Binding and an example

Components can accept data being passed to them using the component inputs/property binding, which makes it possible for a parent component to bind data directly into a child component, which is a way to pass data down the component tree. The square bracket [] is used to represent property binding.

Demo on property binding

We will use two examples first to use property binding in the component own template and second bind data from parent to child property.

Angular share data between components

In our first bindings example, we have linked the value of the name property in our component to the value of the input field in the view.

The binding is dynamic, so, as the value of the input field changes so as property name value is also changed. The value of the input field will be
synchronized to the same value and we do not have to write any code to do that.

Example 2 on property binding to sending data from parent to child component

Data flow downwards from the parent component to child components. When you create a child component that receives data from a parent, we must explicitly tell Angular to expect that data as input, using the @Input decorator. We place the @Input decorator next to the instance variable to which the data will be injected from outside. We will learn this by creating a project and pass data from the app root component to child component hotel components.

Step 1: Create project $ng new InputDataBinding
Step 2: Create new component hotel $ng generate component hotel
Step 3: Edit app component
We will add a hotels array object containing a list of the hotel name and its location. The [hotelData] is outside the property of the current component and is the name of a property in a child component. We had to assign [hotelData] a value of each iteration of the hotel’s array object of the parent component.

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template:`
  <app-hotel *ngFor="let hotel of hotels" [hotelData]="hotel"></app-hotel>` ,
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  hotels = [
    { name: 'Taj Palace', location: 'Delhi'},
    { name: 'Leela Place', location: 'New York'},
    { name: 'Park Hyatt ', location: 'Sydney'}
    ];
}

Step 4: Data sharing from parent to child component
By default, all properties of the component are only accessible inside the components and not from outside of its component. To bind child properties to parent components we need to be explicit about which properties you want to expose to the parent.

You place the @Input decorator next to the instance variable to which the data will be injected from outside. When you pass data into a component from the outside, you pass that data into the component using input properties. To use an input() decorator we need parent/child relationships among components.

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

@Component({
  selector: 'app-hotel',
  template: `
  <div>
    <strong> {{ hotel.name }} </strong>
    <em>: {{ hotel.location }}</em>
  </div>
`,
  styleUrls: ['./hotel.component.scss']
})
export class HotelComponent implements OnInit {
  // tslint:disable-next-line: no-input-rename
  @Input('hotelData') hotel: { name: string, location: string };
  constructor() { }

  ngOnInit(): void { }

}

 When you tag a component’s member variable with @Input(), you are telling Angular that the value for that variable should come from the same-name property of the associated template tag of its is in the parent component.

In @Input() decorator we don’t need to provide an argument if the name of the property of the child component is the same as corresponds to the name of the attribute in a parent component. Sometimes we may want the name of the input property to be different from the name of the instance variable to which it will be injected.

That’s when we need to use an alias name instead of the property’s own name, which allows you to specify the input property name. The alias may be specified inside parentheses in the @Input decorator and in our case we use hotelData as an alias name.

Safe Navigation Operator (?.) is an Angular template binding operator, is not available in javascript, and is not the same as ? the ternary operator. When we are retrieving remote data in our case hotel data using HTTP requests and data may not be available at the time when we are passing data using @Input() properties.

As a result, we pass null data to the child component and when the child component attempt to display data containing the null object. Which can cause Angular to throw exceptions and not display the data when it has come back from the server. We can easily solve this by using a safe navigation operator (?.) as an example {{ hotel?.name }}

Angular share data between components with service

Angular Service contains a sharable and reusable Typescript class that contains business logic that is shared across and reused across various related and unrelated components.

In this example we’ll demonstrate Angular share data between components with service and here is a screenshot of it.

Angular share data between components

We have the two-component app and confirm, when the user enters data in the app component it will pass to service and can access in confirm component using Angular service.

Let create a user service to share data and components to send and receive data.

ng service services/user
ng g component components/confirm

To demonstrate this we have to use the ngmodel directive which is part of Angular formsModule, so we need to import this module in the app.module.ts file.

Let add the following code in service/user.service to share data among components.

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class UserService {

  private _name = '';
  private _age = '';

  get name() {
    return this._name;
  }

  set name(name: string) {
    this._name = name;
  }

  get age() {
    return this._age;
  }

  set age(age: string) {
    this._age = age;
  }
}

Now we have user service, we need to import this service in both components. Let first work on the app.component to send data to the service using a form.

import { Component, OnInit } from '@angular/core';
import { UserService } from './service/user.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  user: any;

  constructor(private userService: UserService) { }

  ngOnInit() {
    this.user = this.userService;
  }
}

In the app.component.html template, let add form and input using the ngmodel directive.

<section>
  <h4>What you are entering</h4>
  <div class="card" style="width: 18rem;">
    <h5 class="card-title">App component</h5>
    <div class="card-body">
      Name: {{ user.name }} Age: {{ user.age }}
    </div>
  </div>
  <hr>

  <form>
    <div class="form-group">
      <label for="exampleInputEmail1">User name</label>
      <input class="form-control" [(ngModel)]="user.name" name="name">
    </div>
    <div class="form-group">
      <label for="exampleInputPassword1">User Age</label>
      <input class="form-control" [(ngModel)]="user.age" name="age" type="number">
    </div>

    <button color="primary" [routerLink]="'/confirm'">Confirm</button>
  </form>
  <div>
    Name: {{ user.name }} &nbsp;&nbsp; Age: {{ user.age }}
  </div>

</section>

<router-outlet></router-outlet>

We also need to add confirm component in the router, when the user clicks on confirm button, it will open confirm page, where we can retrieve user data from the service. Add confirm route in app.routing.module.ts file.

...
const routes: Routes = [
  { path: 'confirm', component: ConfirmComponent },
];
...

Now in confirm component we can retrieve user data from the service, let import userService in the confirm component.

import { Component, OnInit } from '@angular/core';
import { UserService } from 'src/app/service/user.service';

@Component({
  selector: 'app-confirm',
  templateUrl: './confirm.component.html',
  styleUrls: ['./confirm.component.scss']
})
export class ConfirmComponent implements OnInit {
  user: any;
  
  constructor(private userService: UserService) { }

  ngOnInit() {
    this.user = this.userService;
  }
}

In the confirm component template we can now access a user object from the service.

<section>
    <div class="card">
        <h5 class="card-title">Confrim component get data from app component</h5>
        <h6 class="card-subtitle mb-2 text-muted">
            Please confirm your
        </h6>
        <div class="card-body">
            {{ user.name }} is {{ user.age }} years old
        </div>
    </div>
</section>

More information on component interaction in an angular check official documentation.

Related posts

Leave a Comment

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

Scroll to Top