Angular Component Output Properties

Angular Component Output Properties

In this article, I am going to discuss how to pass the data from the nested component to the container component using the Angular Component Output Properties.

At the end of this article, you will have a very good understanding of the below things.

  1. How to pass the user actions or user entered values or selections from the child component to the parent component using output properties.
  2. How to create custom events using angular EventEmitter class
  3. What are ng-container directive and its use

This is a continuation part of our previous article, so please read our previous article before proceeding to this article as we are going to work with the same example as shown below.

Angular Component Output Properties

At the moment when we click any of the radio buttons, then nothing is happening. Let first discuss what we want to do here.

When All(6) radio button is clicked then we need to display all the students in the table. When Male(4) radio button is clicked then we need to display only the 4 Male students in the table similarly when the Female(2) radio button is clicked then we only need to display the 2 Female students in the table

To achieve this here we are going to make use of the Angular component Output Properties. First, let’s discuss the changes that are required in the nested component i.e. StudentCountComponent. 

Changes Required in StudentCountComponent

The changes required in StudentCount.Component.ts are self-explained so please go through the comments.

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

@Component({
    selector: 'student-count',
    templateUrl: 'app/Student/StudentCount.Component.html',
    styleUrls: ['app/Student/StudentCount.component.css']
})

export class StudentCountComponent {
    @Input()
    all: number;

    @Input()
    male: number;

    @Input()
    female: number;

    // This variable holds the selected value of the radio button
    selectedRadioButtonValue: string = 'All';

    // The Output decorator makes the property of an Component as an Output property
    // The EventEmitter class in Angular is used to create the custom event
    // When the radio button selection changes, the selected radio button
    // value which is a string gets passed to the event handler method.
    // Hence, the event payload is string.
    @Output()
    countRadioButtonSelectionChanged: EventEmitter<string> =
        new EventEmitter<string>();

    // This method raises the custom event. We will bind this
    // method to the change event of all the 3 radio buttons
    onRadioButtonSelectionChange() {
        this.countRadioButtonSelectionChanged
            .emit(this.selectedRadioButtonValue);
    }
}

The following are the changes that are required in the view template of StudentCountComponent i.e. StudentCount.Component.html. Notice here we have made 3 changes on each radio button.

  1. The value attribute is set to (All, Male or Female)
  2. We implemented the 2-way data-binding using the ngModel directive. Notice the ngModel is bound to selectedRadioButtonValue property of the component class. This 2-way data-binding ensures whenever the radio button selection changes, the selectedRadioButtonValue property is updated with the value of the selected radio button.
  3. The onRadioButtonSelectionChange() method is binded to the “change” event of the radio button. That means whenever, the selection of the radio button changes, onRadioButtonSelectionChange() method raises the custom event “countRadioButtonSelectionChanged”. We defined this custom event using Angular EventEmitter class.
<span class="radioClass">Show : </span>

<input name='options' type='radio' value="All"
       [(ngModel)]="selectedRadioButtonValue"
       (change)="onRadioButtonSelectionChange()">
<span class="radioClass">{{'All(' + all + ')'}}</span>

<input name="options" type="radio" value="Male"
       [(ngModel)]="selectedRadioButtonValue"
       (change)="onRadioButtonSelectionChange()">
<span class="radioClass">{{"Male(" + male + ")"}}</span>

<input name="options" type="radio" value="Female"
       [(ngModel)]="selectedRadioButtonValue"
       (change)="onRadioButtonSelectionChange()">
<span class="radioClass">{{"Female(" + female + ")"}}</span>
Changes in StudentListComponent 

Now let’s have a look at the changes that are required in the parent component i.e. StudentListComponent

The following are the changes that are required in the StudentListComponent class i.e. StudentList.Component.ts type script file. The changes are commented and self-explained.

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

@Component({
    selector: 'student-list',
    templateUrl: 'app/Student/StudentList.Component.html',
    styleUrls: ['app/Student/StudentList.Component.css']
})

export class StudentListComponent {

    // This property will keep track of the radio button which is selected
    // We have set the default value to All, so all the students
    // are displayed in the table by default
    selectedStudentCountRadioButton: string = 'All';
    
    students: any[] = [
        {
            ID: 'std101', FirstName: 'Pranaya', LastName: 'Rout',
            DOB: '12/8/1988', Gender: 'Male', CourseFee: 1234.56
        },
        {
            ID: 'std102', FirstName: 'Anurag', LastName: 'Mohanty',
            DOB: '10/14/1989', Gender: 'Male', CourseFee: 6666.00
        },
        {
            ID: 'std103', FirstName: 'Priyanka', LastName: 'Dewangan',
            DOB: '7/24/1992', Gender: 'Female', CourseFee: 6543.15
        },
        {
            ID: 'std104', FirstName: 'Hina', LastName: 'Sharma',
            DOB: '8/19/1990', Gender: 'Female', CourseFee: 9000.50
        },
        {
            ID: 'std105', FirstName: 'Sambit', LastName: 'Satapathy',
            DOB: '4/12/1991', Gender: 'Male', CourseFee: 9876.54
        },

        {
            ID: 'std106', FirstName: 'Tarun', LastName: 'Mallick',
            DOB: '6/10/1992', Gender: 'Male', CourseFee: 1278.55
        }

    ];

    getTotalStudentsCount(): number {
        return this.students.length;
    }

    getMaleStudentsCount(): number {
        return this.students.filter(std => std.Gender === 'Male').length;
    }

    getFemaleStudentsCount(): number {
        return this.students.filter(std => std.Gender === 'Female').length;
    }

    // Depending on the radio button which is selected, this method updates
    // the selectedStudentCountRadioButton property
    // This method is going to called when the child component (StudentCountComponent)
    // raises the custom event - countRadioButtonSelectionChanged
    // The event binding is specified in StudentList.component.html
    onStudentCountRadioButtonChange(selectedRadioButtonValue: string): void {
        this.selectedStudentCountRadioButton = selectedRadioButtonValue;
    }
}
Changes Required in StudentList.Component.html

The following are the changes that are required in the view template of StudentListComponent i.e. StudentList.Component.html. 

The onStudentCountRadioButtonChange($event) method is bound to the custom event – countRadioButtonSelectionChanged. The $event object will have the selected radio button value as that is what is passed as the event payload from the nested component. The event handler method (onStudentCountRadioButtonChange()) in the component class updates the property “selectedStudentCountRadioButton”. This property is then used along with the *ngIf structural directive to decide which student objects to display in the table.

On the <tr> element, we are using “ngIf” directive along with selectedStudentCountRadioButton property which controls the student objects to display.

Notice, just above the <tr> element, we have introduced <ng-container> element and the “ngFor” directive is placed on this element. If you are wondering why we have done this, Angular does not allow multiple structural directives to be placed on one element as shown below.  

<tr *ngFor=”let student of students;”

    *ngIf=”selectedStudentCountRadioButton==’All’   || selectedStudentCountRadioButton==student.Gender”>

The above code will raises the following error
Can’t have multiple template bindings on one element. Use only one attribute named ‘template’ or prefixed with *. 

StudentList.Component.html 

<student-count [all]="getTotalStudentsCount()"
               [male]="getMaleStudentsCount()"
               [female]="getFemaleStudentsCount()"
               (countRadioButtonSelectionChanged)="onStudentCountRadioButtonChange($event)">
</student-count>
<br/><br/>
<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Gender</th>
            <th>DOB</th>
            <th>Course Fee</th>
        </tr>
    </thead>
    <tbody>
        <ng-container *ngFor="let student of students;">
            <tr *ngIf="selectedStudentCountRadioButton=='All' ||
                       selectedStudentCountRadioButton==student.Gender">
                <td>{{student.ID | uppercase}}</td>
                <td>{{student.FirstName | studentTitle:student.Gender}}</td>
                <td>{{student.Gender}}</td>
                <td>{{student.DOB | date:'dd/MM/y'}}</td>
                <td>{{student.CourseFee | currency:'USD':true:'1.2-2'}}</td>
            </tr>
        </ng-container>

        <tr *ngIf="!students || students.length==0">
            <td colspan="10">
                No Students to display
            </td>
        </tr>
    </tbody>
</table>

That’s it. We have done with our implementation. Now save all the changes and then run the application and you will see based on the selected radio button, the students are displayed in the table.

Angular Component Output Properties

In the next article, I will discuss the Interfaces in Angular with examples.

SUMMARY:

In this article, I try to explain how to pass the data from the nested component to the container component using the Angular Component Output Properties with an example. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.

No HTML was returned.

Leave a Reply

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