Angular components communicate with each other to pass data. Several angular components can form a hierarchical structure, where data and events get passed in both directions.

In the previous article, we learned how to pass data from parent to child component.

In this article, we will learn how to pass data from child to parent component with the help of @Output and event emitter.

We will create a small example application, where we will have two components assets and the asset-chart, as shown below.

angular-event-emitter-example

The application will have inputs to enter the amount allocated for each asset category(Debt, Equity, and Mutual Funds).

Once the user enters the amount and clicks the Calculate button, we get the Pie chart of asset allocation on each investment category.

The chart displays the percentage of the total amount of allocation for each asset category.

angular-event-emitter-example

Emitting the event using @Output

Create an angular application with the name angular-output-emitter-example.

ng new angular-output-emitter-example

Add ng-bootstrap and chart.js libraries to the application.

cd angular-output-emitter-example
ng add @ng-bootstrap/ng-bootstrap
npm install chart.js --save

Create a new angular component with the name assets.

ng g c assets

Replace the assets.component.ts file with the below content.

This component will be the child component of the app component and emits the event on the click of a button.

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

export interface Asset {
  equity: number;
  debt: number;
  mutualFund: number;
}

@Component({
  selector: 'app-assets',
  templateUrl: './assets.component.html',
  styleUrls: ['./assets.component.css']
})
export class AssetsComponent {

  asset: Asset = { equity: 0, debt: 0, mutualFund: 0 };
  @Output('calculate-asset') calculateEvent: EventEmitter<Asset> = new EventEmitter<Asset>();

  constructor() { }

  drawChart() {
    this.calculateEvent.emit(this.asset);
  }
}
  • We have imported Output and EventEmitter from the angular core library.
  • We have defined an Asset interface that holds the user inputs.
  • The drawChart() method emits the event, and we are passing the asset value using EventEmitter.

Update the asset.component.html file with the below content.

<div class="input-group mb-3">
  <div class="input-group-prepend">
    <span class="input-group-text">Debt : $</span>
  </div>
  <input type="number" class="form-control" [(ngModel)]="asset.debt">
</div>
<div class="input-group mb-3">
  <div class="input-group-prepend">
    <span class="input-group-text">Equity : $</span>
  </div>
  <input type="number" class="form-control" [(ngModel)]="asset.equity" name="equity">
</div>
<div class="input-group mb-3">
  <div class="input-group-prepend">
    <span class="input-group-text">Mutual Funds : $</span>
  </div>
  <input type="number" class="form-control" [(ngModel)]="asset.mutualFund" name="mutualFund">
</div>
<button type="submit" (click)="drawChart()" class="btn btn-success">Calculate</button>

Users can input the invested amount and click on the Calculate button to get the asset allocation percentage.

Update the app.modules.ts file by adding the FormsModule.

//other imports
import { FormsModule } from '@angular/forms';

@NgModule({
//
  imports: [
    FormsModule,
  ],
//
})
export class AppModule { }

Creating the second child component

Create an angular component with the name asset-chart.

ng g c asset-chart

This component will be the child of the app component and will display the pie chart for the given Asset input.

Update the asset-chart.component.ts with the below content.

import { Component } from '@angular/core';
import { Asset } from '../assets/assets.component';
import { Chart } from 'chart.js';

@Component({
  selector: 'app-asset-chart',
  templateUrl: './asset-chart.component.html',
  styleUrls: ['./asset-chart.component.css']
})
export class AssetChartComponent {
  chart: any;

  constructor() { }

  asset: Asset;
  chartDataInPercentage = [];
  total = 0;

  percentage(num: number): number {
    let percentage = (num/this.total) * 100;
    return Math.round(percentage * 100) / 100;
  }

  refreshChart(asset: Asset) {
    this.asset = asset;
    this.total = this.asset.debt + this.asset.equity + this.asset.mutualFund;
    this.chartDataInPercentage = [this.percentage(this.asset.debt),
      this.percentage(this.asset.equity),
      this.percentage(this.asset.mutualFund)];

    this.chart = new Chart('canvas', {
      type: 'pie',
      data: {
        labels: ['Debt %', 'Equity %', 'Mutual Funds %'],
        datasets: [
          {
            data: this.chartDataInPercentage,
            borderColor: '#3cba9f',
            backgroundColor: [
              "Green",
              "Red",
              "Orange"
            ],
            fill: true
          }
        ]
      }
    });
  }
}
  • We have imported the Chart class from the chart.js library. The library will help us create a pie chart of our asset allocations.
  • The refreshChart() method draws the chart whenever invoked.

Update the asset-chart.component.html with the below content.

<div class="chart-container" style="position: relative; height:40vh; width:40vw">
  <canvas id="canvas"></canvas>
</div>

Now both of the child components are ready. We need to wire them up by making the changes to the parent component(app component).

Update the app.component.html with the below content.

<div class="container my-6">
  <div class="border border-light p-3 mb-4">
    <h2 class="text-center">Asset Allocation calculator</h2>
    <div class="d-flex align-items-center justify-content-center" style="height: 350px">
      <app-assets (calculate-asset)="assetChart.refreshChart($event)"></app-assets>
      <app-asset-chart #assetChart></app-asset-chart>
    </div>
  </div>
</div>

To pass the event data from the event emitter, we need to call refreshChart() method of the asset-chart component.

The calculate-asset is the alias used with the @Output() decorator in the assets component.

The #assetChart denotes the local template variable that helps to invoke the refreshChart() method of the asset-chart component.

Output

Run the Angular application. Enter the amounts and click on the Calculate button.

angular-output-example

Using @ViewChild to reference components

We can use the @ViewChild decorator to access the child component controller classes from a parent component.

The @ViewChild decorator finds the correct child component controller class and initializes the instance.

Update the app.component.ts with the below code.

import { ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { AssetChartComponent} from './asset-chart/asset-chart.component'
import { Asset } from './assets/assets.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'angular-output-emitter-example';

  @ViewChild(AssetChartComponent) assetChart: AssetChartComponent;

  refresh(asset : Asset){
    this.assetChart.refreshChart(asset);
  }
}

We have imported the AssetChartComponent child component controller class and created an instance.

The instance is decorated with the @ViewChild decorator.

We have also created a refresh() method that invokes the AssetChartComponent controller class’s refreshChart() method.

Update the app.component.html with the below content.

<div class="container my-6">
  <div class="border border-light p-3 mb-4">
    <h2 class="text-center">Asset Allocation calculator</h2>
    <div class="d-flex align-items-center justify-content-center" style="height: 350px">
      <app-assets (calculate-asset)="refresh($event)"></app-assets>
      <app-asset-chart></app-asset-chart>
    </div>
  </div>
</div>

Now we can directly call the App component’s refresh() method, which invokes the child component’s method to update the asset chart.

Run the Angular application. Enter the amounts and click the Calculate button. We get the same expected result as earlier.

angular-viewchild-example

Conclusion

In this article, we learned how to use the @Output angular decorator to and pass the data from a child component to the parent component.

We learned how to pass the event data into child components with the help of the template and with the help of @ViewChild angular decorator.

The example code is available on GitHub.

You may also be interested in