Saturday, January 13, 2024

Angular @Input and @Output Example

In the post Angular Example to Render Multiple Rows we saw an example demonstrating the use of @Input decorator in Angular and property binding. In this post we’ll see an example showing the use of both @Input and @Output decorator in Angular.

@Input decorator marks a class field as an input property. The input property is bound to a DOM property in the template. During change detection, Angular automatically updates the data property with the DOM property's value. In our component we want to pass data to the user property thus it is decorated with @Input decorator.

@Output decorator with EvenEmitter is used to emit an event from the component. You’ll have more clarity as we progress through the example.

Steps in Angular @Input and @Output example

The requirement is to show a list of user names. When a user name is clicked details of that particular users are displayed. Screens for the example would be something like this-

First user names are displayed

Angular @Input and @Output decorator

Display user details for the selected User

Angular @Output with EvenEmitter

What is needed

  1. You need a model class for User fields.
  2. There will be one component for showing user names and another component for showing User details. So there will be two child component of app.component and the interaction from parent to child and vice versa will happen using @Input and @Output decorators.

Creating a User class

Create a Type script class user.model.ts to define a User class. If you are aware of MVC (Model View Controller) pattern then this class is the Model. There are 3 fields name, age and joinDate in the User class.

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;
  }
}

Creating user.component class

Create a Type script class user.component.ts with properties decorated with @Input and @Output decorators.

import { 
    Component,
    EventEmitter,
    Input, 
    Output
 } from '@angular/core';
import { User } from './user.model';
@Component({
  selector: 'app-user',
  templateUrl: './user.component.html'
})
export class UserComponent{
  @Input() user: User;
  @Output() onUserSelected: EventEmitter<User>;
  constructor(){
    this.onUserSelected = new EventEmitter();
  }
  userClicked() : void{
    this.onUserSelected.emit(this.user);
  }
}

In this component we want to get data in the user property thus it is decorated with @Input decorator. Because of using @Input decorator with user property Angular uses user as an input binding. So you can say that we’ll pass User instance from parent component app.component.ts to the child component user.component.ts and field marked with @Input decorator gets that user instance.

OnUserSelected property in user.component.ts is decorated with @Output decorator and of type EventEmitter<User> that means it will emit User instance (using emit function) which should be received by the parent component.

Creating user.component.html template

<div class="container">
  <div class="row">
    <div class="col-xs-6">
      <label>Name: </label><span (click)='userClicked()'> {{ user.name }}</span>
    </div>
  </div>
</div>

This template displays user names and you can see there is also an event binding for click event calling the userClicked() function in the .ts class when any user name is selected. In the user.component.ts class we have already seen with in the userClicked() function that particular user instance is emitted.

app.component.ts class

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 {
  //username : String;
  users: User[];
  currentUser : User;
  constructor(){
    // Adding user instances in the 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 to currentUser
    this.currentUser = user;
    //console.log('User: ', user );
  }

  // Function used to determine the selected user
  // so that its color can be changed 
  isSelected(user: User): boolean {
    if (!user || !this.currentUser) {
      return false;
    }
    return user.name === this.currentUser.name;
  }
}

In the class there is an array of type User, in the constructor user instances are created and stored in this array.

There is also a showUser(user: User) function, this is the method which stores the emitted user instance (from child component user.component).

isSelected() function determines which user name is selected, this helps in styling the selected user name.

app.component.html template

<div class="container">
  <h3>Click user name to see user details</h3>
  <app-user *ngFor="let user of users" 
      [user]="user"
      (onUserSelected)="showUser($event)"
      [class.alert-primary] = "isSelected(user)">
  </app-user>
</div>

<div class="container">
  <user-data *ngIf="currentUser" [user]="currentUser">
  </user-data>
</div>

ngFor iterates over the User array and each user instance is assigned to a user variable [user]="user", this expression also passes the value stored in user variable to the user property decorated with @Input decorator in user.component.ts class. Just keep in mind that the square brackets are used for input binding.

(onUserSelected)="showUser($event)" is for handling output. You have configured (onUserSelected) output to listen to the event and call function showUser() when data is emitted.

[class.alert-primary] = "isSelected(user)" is used to set css class conditionally based on the bollean values returned by the associated function. If isSelected() function returns true then the alert-primary (from Bootstrap) css class is added.

There is another div section in the template.

<div class="container">
    <user-data *ngIf="currentUser" [user]="currentUser">
    </user-data>
</div>

This is used for displaying user details. Using ngIf it is checked if currentUser has any value or not. If there is any assigned value then only it is assigned to the local variable user which also is an input binding for user property in userdata.component.

userdata.component.ts class

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;
    constructor(){
    }
}

As you can see in this type script class too there is a user property which is decorated using @Input decorator. This user property gets user data from the second div section of the app.component.html shown above.

userdata.component.html template

<div class="jumbotron">
  <div class="container">
    <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>
</div>

This template shows user details for the selected user. Note that, for date, DatePipe is used to format the date.

Changes in app module to register created components

If you have not created new component using Angular CLI then you need to manually import the components in app.module.ts file.

Add import statements.

import { UserComponent } from './user/user.component';
import { UserDataComponent } from './user/userdata.component';

Add to declarations array within @NgModule decorator.

@NgModule({
  declarations: [
    AppComponent,
    UserComponent,
    UserDataComponent
  ],
  imports: [
 ..
 ..

That's all for this topic Angular @Input and @Output Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Angular Tutorial Page


Related Topics

  1. Angular ngClass Directive With Examples
  2. Angular Custom Two-Way Data Binding
  3. Angular Project Structure With File Description
  4. Angular Application Bootstrap Process
  5. Creating New Component in Angular

You may also like-

  1. Angular First App - Hello world Example
  2. Angular One-Way Data Binding Using String Interpolation
  3. Injector Hierarchy and Service Instances in Angular
  4. Angular ngIf Directive With Examples
  5. Java String Interview Questions And Answers
  6. Can we Start The Same Thread Twice in Java
  7. BigDecimal in Java
  8. Sending Email Using Spring Framework Example