In the post
Angular event binding we saw how event binding allows you to listen and respond to the events sent by the host elements.
We can also bind a custom event of an Angular component that’s what we’ll see in this post how to bind custom event in Angular using
@Output
decorator.
Angular custom event binding
Using custom event binding is a great way for parent and child components to communicate where parent component is informed of any change in the child component. For example following statement in the parent template shows the event binding for the property "onUserSelected" in the child component.
Statement in parent component-
<app-user *ngFor="let user of users" [usr]="user" (onUserSelected)="showUser($event)">
Property in child component-
@Output() onUserSelected: EventEmitter<User>;
@Output decorator, $event and EventEmitter
While doing a custom event binding in Angular three things you will come across are-
- @Output decorator
- EventEmitter class
- $event object
1. @Output decorator
A property in a Component that emits a custom event is decorated with @Output decorator. For example in the statement
@Output() onUserSelected: EventEmitter<User>;
onUserSelected is decorated with @Output() which means this property is going to emit a custom event. That is why this property is of type EventEmitter.
2. EventEmitter class
EventEmitter class is used in components with the @Output to emit custom events synchronously or asynchronously. EventEmitter maintains a list of subscribing instances and register handlers for the event.
EventEmitter class has two methods-
- emit(value?: T)- Emits an event containing a given value, T signifies the value to emit.
- subscribe()- Registers handlers for events emitted by this instance.
When we assign an EventEmitter to an output, subscription of instances is automatically done by Angular so you don't need to use this method explicitly in most of the scenarios.
3. $event object
In an event binding, Angular sets up an event handler for the target event. The binding conveys information about the event. This information is encapsulated in $event and may include data values such as an event object, string, or number.
When the event is raised, the handler executes the template statement. For example in the following statement
(onUserSelected)="showUser($event)"
(onUserSelected) is the target event
showUser($event) is the template statement.
When the event onUserSelected is raised, template statement, which in our example is showUser($event) method, is executed. Argument of the showUser() method is $event which encapsulates the value passed as method parameter.
Angular custom event binding example
We have got enough information about the event binding and the parts that form the custom event binding. Let’s create an example using that knowledge now. Note than example is created using Angular 17 and Bootstrap 5.
In the example there are two child components UserComponent and UserDataComponent and AppComponent is the parent component.
Initially list of user names is displayed (Done through UserComponent), on clicking any of the user User details are displayed (Done through UserDataComponent).
user.model.ts
In the Model class there are 3 fields name, age and joinDate.
export class User { name : string; age : number; joinDate : Date; constructor(name: string, age : number, joinDate : Date) { this.name = name; this.age = age; this.joinDate = joinDate; } }
app.component.ts (Parent component)
AppComponent uses User model so that is imported. An array of type User is defined and user instances are added to that array in the Constructor. Property selectedUser stores the value of the User which is selected and used to display details for the selected User. When the custom event is raised showUser() method gets called which assigns value to the selectedUser property.
import {Component} from '@angular/core'; import { User } from './user/user.model'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { users: User[]; selectedUser!: User; constructor(){ //Adding User instances to users array this.users = [new User('Jack', 56, new Date('2005-03-25')), new User('Lisa', 32, new Date('2012-05-09')), new User('Jayesh', 28, new Date('2014-10-21')) ] ; } showUser(user: User) : void { // Setting selected user this.selectedUser = user; } }
app.component.html
<div class="container"> <h3>Click User to get details</h3> <app-user *ngFor="let user of users" [usr]="user" (onUserSelected)="showUser($event)"> </app-user> <user-data *ngIf="selectedUser" [user]="selectedUser"></user-data> </div>
Template has two selectors tags for the child templates. With in the <app-user> selector users array is iterated using ngFor directive and each user instance is bound to the usr property of the child component, onUserSelected is the output property (to emit event).
user.component.ts (Child component)
import { Component, Input, Output, EventEmitter } from '@angular/core'; import { User } from './user.model'; @Component({ selector: 'app-user', templateUrl: './user.component.html' }) export class UserComponent { @Input() usr!: User; @Output() onUserSelected: EventEmitter<User>; constructor(){ this.onUserSelected = new EventEmitter(); } userClicked() : void{ this.onUserSelected.emit(this.usr); } }
In the child component usr variable is decorated with @Input decorator indicating that parent component can bind to this property.
onUserSelected is decorated with @Output indicating that it will emit an event.
In the userClicked() method using emit method an event is emitted with current user instance as event value which will be encapsulated in $event.
User.component.html
In the template user name is displayed, there is also an even binding for click event which calls the userClicked() method. Execution of userClicked() method results in emitting the event with the user instance as value.
<div class="row"> <div class="col-xs-6"> <label>Name:</label><span (click)='userClicked()'> {{ usr.name }}</span> </div> </div>
userdata.component.ts (Child component)
import { Component, Input } from '@angular/core'; import { User } from './user.model'; @Component({ selector: 'user-data', templateUrl: './userdata.component.html' }) export class UserDataComponent{ @Input() user!: User; }
Another child component UserDataComponent has a property user which is decorated with @Input decorator indicating that parent component can bind to this property. This is property to which selected user is assigned and then used to display user details.
userdata.component.html
This template shows the user details.
<div class="mt-4 p-3 bg-light"> <h2>User Details</h2> <div class="row"> <div class="col-xs-5 px-3"> <label>Name: </label> {{ user.name }} </div> <div class="col-xs-4 px-3"> <label>Age: </label> {{ user.age }} </div> <div class="col-xs-4 px-3"> <label>Joining Date: </label> {{ user.joinDate | date:'dd/MM/yyyy'}} </div> </div> </div>
Testing the application
Initially user names are displayed.
When user name is clicked
That's all for this topic Angular Custom Event Binding Using @Output Decorator. If you have any doubt or any suggestions to make please drop a comment. Thanks!
>>>Return to Angular Tutorial Page
Related Topics
You may also like-