Angular Component Data Flows Using @Output and EventEmitter

Dhananjay Kumar / Monday, January 1, 2018

Unlike AngularJS, Angular does not have two-way data binding. When I say, Angular doesn’t have two-way data binding, it does not mean you cannot achieve that. No modern web framework can exist without support of two-way data bindingAngular provides this function using the ngModel directive. To use that, you need to import FormsModule and then you can use it in various ways, such as:

  • ngModel  
  • [ngModel] 
  • [(ngModel)] 

You may wonder why Angular does not have two-way data binding like AngularJS. AngularJS once implemented two-way data binding using digest cycle, dirty checking algorithms, and others, and these approaches had their own problems. In this article, we will focus on problems with AngularJS two-way data binding so that you understand why it is handled differently in Angular.   

Coming back to Angular, two- way data binding can be depicted as shown below: 

 

Square brackets represent property binding, small brackets represent event binding, and the combination of both represents two-way data binding. Syntax of two-way data binding is also called banana in the box syntax. Learn more about data bindings here.

Now, you have some understanding of different types of binding in Angular; let’s see how data can flow between Angular components. Data could be of primitive types, arrays, objects, or events. To flow data between components, Angular provides:  

  1. @Input decorator  
  1. @Output decorator  

Both of these decorators are part of @angular/core 

@Input() decorator marks a class field as an input property and supplies configuration metadata. It declares a data-bound input property, which Angular automatically updates during change detection. 

@Output decorator marks a class field as an output property and supplies configuration metadata. It declares a data-bound output property, which Angular automatically updates during change detection.

 

These two decorators are used to flow data between components. Keep in mind that besides these two decorators, you can also use Angular services to flow data between the components. If you have to pass data into a component use @Input decorator, and if you have to emit data from a component use @Output decorator.   

Both decoratorwork between components when they are related to each other.  For example, if you are using a component called ChildComponent inside another component called AppComponent, they are related to each other.  You can see this in the below imageand any ChildComponent property decorated with @Input decorator can receive data from the AppComponent.

To understand it better, consider ChildComponent as listed below:  

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

@Component({ 

  selector: 'app-child'

  template: ` <p>count = {{count}}</p> 

  ` 

}) 

export class ChildComponent implements OnInit { 

 

  constructor() { } 

  @Input(countnumber; 

  ngOnInit() { 

  } 

} 

If you look closely at the above code snippet, we are using @Input() decorator with count property, which implies that value of count property will be set from outside the ChildComponent.  We can use ChildComponent inside AppComponent and can use property binding to pass value for count property as shown in below listing: 

 import Component } from '@angular/core'; 

@Component({ 

  selector: 'app-root', 

  template: `<h2>{{title}}</h2> 

  <app-child [count]='count'></app-child> 

  ` 

}) 

export class AppComponent { 

   count = 9; 

} 

We are using ChildComponent inside AppComponent and using the property binding passing data in the ChildComponent Right now data is being passed in one-way directional flow to the ChildComponent Keep in mind that any time the value of an input-bound property updates, Angular runs change detector. However, you can configure how change detector should run on @Input decorator. Also each time @input decorator property updates, Angular runs ngOnChanges() life cycle hook, so you can read the updated value of input-bound property in ngOnChanges() life cycle hook.  

Now that we know how to pass data from parent component to a child component, letswitch our focus to how data and events can be emitted from a child component to a parent component.  To emit data and event out from a component, we need  

  • To decorate property with @output decorator. By doing, this property becomes an outbound data property.  
  • Create instance of EventEmitter and decorate it with @Output decorator.  

 Note in the below image that data or events go outside of a component using @Output decorator. 

 

Before I explain how EventEmitter operates in detail, please review  the below code example.  Modify ChildComponent with a button as shown in listing below:  

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

@Component({ 

selector: 'app-child', 

template: ` <p>count = {{count}}</p> 

 <button (click)='updateCount()'>update count </button> 

  ` 

}) 

export class ChildComponent implements OnInit { 

  constructor() { } 

  @Input(countnumber; 

 ngOnInit() { 

 } 

  updateCount() { 

    this.count = this.count + 1; 

  } 

} 

When you click on button in the ChildComponent, it updates count value. Super simple.  So far, click event is handled inside the ChildComponent itself.  

Now, let us tweak the requirement a bit. What if you want to execute a function of AppComponent on the click event of a button inside ChildComponent?

To do this, you will have to emit the button click event from ChildComponent. With event emit you can also send data out from the ChildComponent Letmodify ChildComponent to emit data and event to be captured in AppComponent 

import Component, OnInit, Input, Output, EventEmitterfrom '@angular/core'; 

@Component({ 

  selector: 'app-child', 

  template: ` <p>count = {{count}}</p>\ 

  <button (click)='updateCount()'>update count </button> 

  ` 

}) 

export class ChildComponent implements OnInit {  

  constructor() { } 

  @Input(count: number; 

  @Output(countChange = new EventEmitter(); 

  ngOnInit() { 

  } 

  updateCount() { 

    this.count = this.count + 1; 

    this.countChange.emit(this.count); 

  } 

} 

Right now, we are performing the following tasks in the ChildComponent 

  1. Created instance of EventEmitter called countChange, which will be emitted to the parent component on the click event of the button.  
  1. Created a function named updateCount(). This function is called on the click event of the button, and inside the function event countChange is emitted.  
  1. While emitting countChange  event, value of count property is also sent out of component.  

Before we move ahead let us understand EventEmitter. It is used in the directives and components to emit custom events either synchronously or asynchronously.  Any handler can handle these events by subscribing to an instance of EventEmitter class.  It is different than normal HTML event, as it uses RxJS observable such that the handler can subscribe to the event.  

If you look into implementation of EventEmitter class, it extends Subject class.

   

Since EventEmitter class extends RxJs Subject class, this means it is observable and can be multicasted to many observers.  It is not the same as a DOM event, which? cannot be muticasted and observed.   

In the AppComponent, you can handle emitted event from ChildComponent as shown in the below code listing:

import Component } from '@angular/core'; 

@Component({ 

  selector: 'app-root', 

  template: `<h2>{{title}}</h2> 

  <app-child [count]='count' (countChange)=changeCount($event)></app-child> 

  ` 

}) 

export class AppComponent { 

   count 9; 

   changeCount(data) { 

      console.log(data); 

   } 

} 

Right now, we are performing the following tasks in the AppComponent class:  

  1. Using <app-child> in the template.  
  1. In the <app-child> element, using event binding to use the countChange event.  
  1. Calling the changeCount function on the countChange event.  
  1. In the changeCount function, printing the value of the count passed from the ChildComponent 

As you can see, the function of AppComponent is called on the click event of the button placed on the ChildComponent. This can be done with @Output and EventEmitter Right now data is flowing between components using the @Input() and @Output() decorators.

As you see we created two-way data binding between two components. If you look at the code, it is combination of property binding and event binding.  

  

A Real Time Example  

Lets take a real-time example to find how @Output and EventEmitter are more useful. Consider that AppComponent is rendering a list of products in tabular form as shown in the image below:

   

To create the product table above, we have very simple AppComponent class with only one function: to return a list of products.  

export class AppComponent implements OnInit { 

  products = []; 

  title = 'Products'; 

  ngOnInit() { 

    this.products = this.getProducts(); 

  } 

  getProducts() { 

    return [ 

      'id': '1', 'title': 'Screw Driver', 'price': 400'stock': 11 }, 

      'id': '2', 'title': 'Nut Volt', 'price': 200'stock': 5 }, 

      'id': '3', 'title': 'Resistor', 'price': 78'stock': 45 }, 

      'id': '4', 'title': 'Tractor', 'price': 20000'stock': 1 }, 

      'id': '5', 'title': 'Roller', 'price': 62'stock': 15 }, 

    ]; 

  } 

} 

In the ngOnInit life cycle hook, we are calling the getPrdoducts() function and assigning the returned data to the products variable so it can be used on the template. There, we are using the *ngFor directive to iterate through the array and display the products. See the code below:  

<div class="container"

  <br/> 

  <h1 class="text-center">{{title}}</h1> 

  <table class="table"> 

    <thead> 

      <th>Id</th> 

      <th>Title</th> 

      <th>Price</th> 

      <th>Stock</th> 

    </thead> 

    <tbody> 

      <tr *ngFor="let p of products"

        <td>{{p.id}}</td> 

        <td>{{p.title}}</td> 

        <td>{{p.price}}</td> 

        <td>{{p.stock}}</td> 

      </tr> 

    </tbody> 

  </table> 

</div> 

With this code, products are rendered in a table as shown in the image below: 

 

Now let’s say we want to add a new column with a button and input box as shown in the image below: 

  

Our requirements are as follows:  

  1. If the value of stock is more than 10 then the button color should be green.  
  1. If the value of stock is less than 10 then the button color should be red. 
  1. The user can enter a number in the input box, which will be added to that particular stock value.  
  1. The color of the button should be updated on the basis of the changed value of the product stock.  

To achieve this task, let us create a new child component called StockStausComponent. Essentially, in the template of StockStatusComponent, there is one button and one numeric input box. In StockStatusComponent: 

  1. We need to read the value of stock passed from AppComponnet. For this, we need to use @Input 
  1. We need to emit an event so that a function in AppComponent can be called on the click of the StockStatusComponent button. For this, we need to use @Output and EventEmitter 

 

Consider the code below: 

stockstatus.component.ts

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

@Component({ 

    selector: 'app-stock-status', 

    template: `<input type='number' [(ngModel)]='updatedstockvalue'/> <button class='btn btn-primary' 

     [style.background]='color' 

     (click)="stockValueChanged()">Change Stock Value</button> ` 

}) 

export class StockStatusComponent implements OnChanges { 

 

    @Input() stock: number; 

    @Input(productId: number; 

    @Output(stockValueChange = new EventEmitter(); 

    color = ''; 

    updatedstockvalue: number; 

    stockValueChanged() { 

        this.stockValueChange.emit({ id: this.productIdupdatdstockvalue: this.updatedstockvalue }); 

        this.updatedstockvalue = null; 

    } 

    ngOnChanges() { 

 

        if (this.stock > 10) { 

            this.color = 'green'; 

        } else { 

            this.color = 'red'; 

        } 

 

    } 

} 

Let’s explore the above class line by line.  

  1. In the first line we are importing everything required: @Input, @Output etc.  
  1. In the template, there is one numeric input box which is bound to the updatedStockValue property using [(ngModel)]. We need to pass this value with an event to the AppComponent 
  1. In the template, there is one button. On the click event of the button, an event is emitted to the AppComponent 
  1. We need to set the color of the button on the basis of the value of product stock. So, we must use property binding to set the background of the button. The value of the color property is updated in the class.  
  1. We are creating two @Input() decorated properties - stock and productId because value of these two properties will be passed from AppComponent 
  1. We are creating an event called stockValueChange. This event will be emitted to AppComponent on the click of the button.  
  1. In the stockValueChanged function, we are emitting the stockValueChange event and also passing the product id to be updated and the value to be added in the product stock value.  
  1. We are updating the value of color property in the ngOnChanges() life cycle hook because each time the stock value gets updated in the AppComponent, the value of the color property should be updated.

Here we are using the @Input decorator to read data from AppComponent class, which happens to be the parent class in this caseTo pass data from the parent component class to the child component class, use @Input decorator.  

In addition, we are using @Output with EventEmitter to emit an event to AppComponent. So to emit an event from the child component class to the parent component class, use EventEmitter with @Output() decorator.

ThereforeStockStatusComponent is using both @Input and @Output to read data from AppComponent and emit an event to AppComponent 

Modify AppComponent to use StockStatusComponent 

Let us first modify the template. In the template, add a new table column. Inside the column, the <app-stock-status> component is used.  

<div class="container"

  <br/> 

  <h1 class="text-center">{{title}}</h1> 

  <table class="table"

    <thead> 

      <th>Id</th> 

      <th>Title</th> 

      <th>Price</th> 

      <th>Stock</th> 

    </thead> 

    <tbody> 

      <tr *ngFor="let p of products"> 

        <td>{{p.id}}</td> 

        <td>{{p.title}}</td> 

        <td>{{p.price}}</td> 

        <td>{{p.stock}}</td> 

        <td><app-stock-status [productId]='p.id' [stock]='p.stock' (stockValueChange)='changeStockValue($event)'></app-stock-status></td> 

      </tr> 

    </tbody> 

  </table> 

</div> 

We are passing the value to productId and stock using property binding (remember, these two properties are decorated with @Input() in StockStatusComponent) and using event binding to handle the stockValueChange event (remember, this event is decorated with @Output() in StockStatusComponent) 

Next, we need to add changeStockValue  function in the AppComponentAdd the following code in the AppComponent class: 

productToUpdate: any; 

changeStockValue(p) { 

    this.productToUpdate = this.products.find(this.findProducts, [p.id]); 

    this.productToUpdate.stock = this.productToUpdate.stock + p.updatdstockvalue; 

  } 

  findProducts(p) { 

    return p.id === this[0]; 

  } 

In the function, we are using the JavaScript Array.prototype.find method to find a product with matched productId and then updating the stock count of the matched product.  When you run the application, you’ll get the following output:  

When you enter a number in the numeric box and click on the button, you perform a task in the child component that updates the operation value in the parent component. Also, on the basis of the parent component value, the style is being changed in the child component. All this is possible using Angular @Input, @Output, and EventEmitter.  

Stay tuned for future articles where we go into more depth on other features of Angular! 

Ignite UI for Angular benefits