Suggested Videos
Part 19 - Angular custom pipe | Text | Slides
Part 20 - Angular 2 container and nested components | Text | Slides
Part 21 - Angular component input properties | Text | Slides
In this video we will discuss
This is continuation to Part 21, so please watch Part 21 from Angular 2 tutorial before proceeding. We will be working with the same example, we started in Part 20.
At the moment when we click the radio buttons, nothing happens. Here is what we want to do.
To achieve this we are going to make use of component output properties. First let's look at the changes required in the nested component i.e EmployeeCountComponent.
The changes required in employeeCount.component.ts are commented and self-explanatory
The following are the changes required in the view template of EmployeeCountComponent i.e employeeCount.component.html. Notice we have made 3 changes on each radio button
Now let's look at the changes required in the parent component i.e EmployeeListComponent
The following are the changes required in the EmployeeListComponent class i.e employeeList.component.ts. The changes are commented and self-explanatory
The following are the changes required in the view template of EmployeeListComponent i.e employeeList.component.html.
1. onEmployeeCountRadioButtonChange($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 (onEmployeeCountRadioButtonChange()) in the component class updates the property "selectedEmployeeCountRadioButton". This property is then used along with *ngIf structural directive to decide which employee objects to display in the table.
2. On the <tr> element, we are using "ngIf" directive along with selectedEmployeeCountRadioButton property which controls the employee 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.
The above line of code raises the following error
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *.
employeeList.component.html
At this point, run the application and test. Notice, the correct set of employees are displayed based on the selection of the radio button.
Part 19 - Angular custom pipe | Text | Slides
Part 20 - Angular 2 container and nested components | Text | Slides
Part 21 - Angular component input properties | Text | Slides
In this video we will discuss
- How to pass user actions or user entered values or selections from the child component to the parent component using output properties.
- Along the way we will discuss creating custom events using angular EventEmitter class
- Finally what is ng-container directive and it's use
This is continuation to Part 21, so please watch Part 21 from Angular 2 tutorial before proceeding. We will be working with the same example, we started in Part 20.
At the moment when we click the radio buttons, nothing happens. Here is what we want to do.
User Action | What should happen |
---|---|
All(6) radio button is clicked | Display all the employees in the table |
Male(4) radio button is clicked | Display the 4 Male employees in the table |
Female(2) radio button is clicked | Display the 2 Female employees in the table |
To achieve this we are going to make use of component output properties. First let's look at the changes required in the nested component i.e EmployeeCountComponent.
The changes required in employeeCount.component.ts are commented and self-explanatory
// Import
Output and EventEmitter
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'employee-count',
templateUrl: 'app/employee/employeeCount.component.html',
styleUrls: ['app/employee/employeeCount.component.css']
})
export class
EmployeeCountComponent {
@Input()
all: number;
@Input()
male: number;
@Input()
female: number;
// Holds the
selected value of the radio button
selectedRadioButtonValue: string = 'All';
// The Output
decorator makes the property an Output property
// EventEmitter
class 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);
}
}
- value attribute is set to (All, Male or Female)
- Implemented 2 way data-binding using the ngModel directive. Notice ngModel is bound to selectedRadioButtonValue property in 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.
- onRadioButtonSelectionChange() method is binded to "change" event of the radio button. So this 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>
Now let's look at the changes required in the parent component i.e EmployeeListComponent
The following are the changes required in the EmployeeListComponent class i.e employeeList.component.ts. The changes are commented and self-explanatory
import { Component } from '@angular/core';
@Component({
selector: 'list-employee',
templateUrl: 'app/employee/employeeList.component.html',
styleUrls: ['app/employee/employeeList.component.css']
})
export class
EmployeeListComponent {
employees: any[];
// This
property keeps track of which radio button is selected
// We have set
the default value to All, so all the employees
// are
displayed in the table by default
selectedEmployeeCountRadioButton: string = 'All';
constructor() {
this.employees = [
{
code: 'emp101', name: 'Tom', gender: 'Male',
annualSalary: 5500,
dateOfBirth: '6/25/1988'
},
{
code: 'emp102', name: 'Alex', gender: 'Male',
annualSalary: 5700.95,
dateOfBirth: '9/6/1982'
},
{
code: 'emp103', name: 'Mike', gender: 'Male',
annualSalary: 5900,
dateOfBirth: '12/8/1979'
},
{
code: 'emp104', name: 'Mary', gender: 'Female',
annualSalary: 6500.826,
dateOfBirth: '10/14/1980'
},
{
code: 'emp105', name: 'Nancy', gender: 'Female',
annualSalary: 6700.826,
dateOfBirth: '12/15/1982'
},
{
code: 'emp106', name: 'Steve', gender: 'Male',
annualSalary: 7700.481,
dateOfBirth: '11/18/1979'
},
];
}
getTotalEmployeesCount(): number {
return this.employees.length;
}
getMaleEmployeesCount(): number {
return this.employees.filter(e
=> e.gender === 'Male').length;
}
getFemaleEmployeesCount(): number {
return this.employees.filter(e
=> e.gender === 'Female').length;
}
// Depending on
which radio button is selected, this method updates
//
selectedEmployeeCountRadioButton property declared above
// This method
is called when the child component (EmployeeCountComponent)
// raises the
custom event - countRadioButtonSelectionChanged
// The event
binding is specified in employeeList.component.html
onEmployeeCountRadioButtonChange(selectedRadioButtonValue: string): void {
this.selectedEmployeeCountRadioButton = selectedRadioButtonValue;
}
}
The following are the changes required in the view template of EmployeeListComponent i.e employeeList.component.html.
1. onEmployeeCountRadioButtonChange($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 (onEmployeeCountRadioButtonChange()) in the component class updates the property "selectedEmployeeCountRadioButton". This property is then used along with *ngIf structural directive to decide which employee objects to display in the table.
2. On the <tr> element, we are using "ngIf" directive along with selectedEmployeeCountRadioButton property which controls the employee 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 employee of employees;"
*ngIf="selectedEmployeeCountRadioButton=='All'
||
selectedEmployeeCountRadioButton==employee.gender">
The above line of code raises the following error
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *.
employeeList.component.html
<employee-count [all]="getTotalEmployeesCount()"
[male]="getMaleEmployeesCount()"
[female]="getFemaleEmployeesCount()"
(countRadioButtonSelectionChanged)="onEmployeeCountRadioButtonChange($event)">
</employee-count>
<br /><br />
<table>
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Gender</th>
<th>Annual
Salary</th>
<th>Date of
Birth</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let employee of employees;">
<tr *ngIf="selectedEmployeeCountRadioButton=='All' ||
selectedEmployeeCountRadioButton==employee.gender">
<td>{{employee.code | uppercase}}</td>
<td>{{employee.name | employeeTitle:employee.gender }}</td>
<td>{{employee.gender}}</td>
<td>{{employee.annualSalary | currency:'USD':true:'1.3-3'}}</td>
<td>{{employee.dateOfBirth | date:'dd/MM/y'}}</td>
</tr>
</ng-container>
<tr *ngIf="!employees || employees.length==0">
<td colspan="5">
No employees to display
</td>
</tr>
</tbody>
</table>
At this point, run the application and test. Notice, the correct set of employees are displayed based on the selection of the radio button.
Very nice explained. I tried to run this code, but there is an error. Any one can help on this error!!
ReplyDeleteUnhandled Promise rejection: Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'. ("
input name='options' type='radio' value="All"
[ERROR ->][(ngModel)]="selectedRadioButtonValue"
(change)="onRadioButtonSelectionChange()
span clas"): ng:///app/employee/employeeCount.component.html@18:7
Can't bind to 'ngModel' since it isn't a known property of 'input'. ("
Hello Kumar,
DeleteIt seems to be you didn't import FormsModule. Please follow below steps and give a try
1) import { FormsModule } from '@angular/forms'; in module and
2) include it in the imports array.
e.g --> imports: [
BrowserModule,
routes,
FormsModule
],
please let me know if it didn't work.
Please Add FormsModule in app.madule.ts
DeleteI too get that error instead of (change)--->change it to ngModelchange so that as soon as selectedRadioButtonValue changes it emit in ngModelchange
DeletePlease go through Part-15. "Two way data binding in angular 2" before proceeding to this lesson. Your error will be solved.
ReplyDeleteHi
ReplyDeleteinput change event is not working,Please help me , i am stuck here.
great sir i try I tried to run this code, but it only show the employeeCount.component (table heade) and not show me the emp.list.component (the table data)
ReplyDeleteRadiobutton Selection Event not getting fired & list not getting updated.
ReplyDeleteI have less knowledge about emit method. Can you pls explain more to understand it.
ReplyDeleteHI Sir, i am able to see only header not the table data and after clicking the radio button it is not modifying the table data. please help me.
ReplyDeleteimport { NgModule } from '@angular/core';
ReplyDeleteimport { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { EmployeeComponent } from './employee/employee.component';
import { EmployeeListComponent } from './employee/employeeList.component';
import { EmployeeTitlePipe } from './employee/employeeTitle.pipe';
import { EmployeeCountComponent } from './employee/employeeCount.component';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [AppComponent, EmployeeComponent, EmployeeListComponent, EmployeeTitlePipe, EmployeeCountComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
Please copy all the content to app.module.ts
great sir, I run this code It's working fine for me. Very nice explanation.
ReplyDeletesir
ReplyDeleteall table data is showing but change method not working please help
not convinced how that rows are filtering based on keywords
ReplyDelete