Introduction
In this post, we will learn about Container and Presentation components communication in Angular with sample projects. To understand Container and Presentation components, please refer to the below post.
Add Modules and Components
Prerequisites
- Visual Studio Code
- Node JS and NPM
- Angular 8 or above
- Angular CLI
We need to create below modules and components. Navigate to the project root path and run the below commands in Angular CLI.
- ng g m employee
- ng g c employee
- ng g c employee/employee-list
- ng g c employee/employee-detail
The above command will create the "employee" module and component. It also creates "employee-list" and "employee-detail" presentation components.
Create an Interface model
We need to create an "Employee" Interface model so that we can use this model in our components and service.
- export interface Employee {
- id: number;
- name: string;
- email: string;
- }
Create a Data Service
We will create an "EmployeeData" service which we will use it in components and run the below command to create data service.
- ng g s core/services/employeedata
Add the below code in the "EmployeeData" service. We have added hardcoded values for employee data and will change later to read it from another service. Also, I added the "getEmployees" method to return data.
On line 03, we have used the "Employee" interface model which we created in the above step.
- export class EmployeeDataService {
-
- employees: Employee[] = [
- {
- id: 1,
- name: 'Test Name1',
- email: 'Test Address',
- address: null
- },
- {
- id: 1,
- name: 'Test Name2',
- email: 'Test Address2',
- address: null
- },
- {
- id: 1,
- name: 'Test Name3',
- email: 'Test Address3',
- address: null
- }
- ];
-
- getEmployees(): Observable<Employee[]> {
- return of(this.employees);
- }
-
- }
Use the Data Service
We will inject the "employeeData" service in "employee.component.ts" and consume the "getEmployees" method.
- Line 03, employeedata service reference got added.
- Line 15, employeedata service injected in constructor.
- Line 18, we have created "getEmployees" local method which will get data form "employeeData" service.
- import { Component, OnInit } from '@angular/core';
- import { Employee } from '../shared/interfaces';
- import { EmployeeDataService } from '../core/services/employeedata.service';
-
- @Component({
- selector: 'app-employee',
- templateUrl: './employee.component.html',
- styleUrls: ['./employee.component.css']
- })
- export class EmployeeComponent implements OnInit {
-
- public employee: Employee;
- public employees: Employee[];
-
- constructor(private employeeService: EmployeeDataService) { }
-
- ngOnInit(): void {
- this.getEmployees();
- }
-
- getEmployees() {
- this.employeeService.getEmployees()
- .subscribe((emps: Employee[]) => this.employees = emps);
- }
- }
Update Parent component HTML
Add the below code in "employee.component.html" to reder the "employee-list" and "employee-detail" presentation components. Line:03 added "employeeSelected" which will trigger the rendering of "employee-detail" component.
- <div class="row">
- <div class="col-md-6">
- <app-employee-list [employees]="employees" (employeeSelected)='selected($event)'></app-employee-list>
- </div>
- <div class="col-md-6">
- <app-employee-detail [employee]="employee"></app-employee-detail>
- </div>
- </div>
We need to implement the "employeeSelected" event in "employee.component.ts" file as shown below.
- selected(employee: any){
- this.employee = employee;
- }
Update Presentaion components
We need to add the below code in the "employee-list.component.ts" file. we implemented the "selectEmployee" method and added the "employee" input, and "employeeSelected" output properties.
- export class EmployeeListComponent implements OnInit {
-
- @Input() public employees: Employee[];
- @Output() public employeeSelected = new EventEmitter<Employee>();
- public bsModalRef: BsModalRef;
-
- constructor(private modalService: BsModalService) {}
-
- ngOnInit(): void {
- }
-
- selectEmployee(emp: Employee) {
- this.employeeSelected.emit(emp);
- }
-
- }
Add the below code in the file, it renders the list of "employees" from the "employee" partent compontent.
- <h4>Employees</h4>
- <table class="table table-striped">
- <tr>
- <th>Name</th>
- <th>Email</th>
- </tr>
- <tr *ngFor="let emp of employees" (click)="selectEmployee(emp)">
- <td>{{ emp.name }}</td>
- <td>{{ emp.email }}</td>
- </tr>
- </table>
- <br />
We need to add the below code in "employee-detail.component.html". It simply renders the name and email properties from the "employee" parent component.
- <div>
- Name: {{employee.name}}
- <br/>
- Email: {{employee.email}}
- <br/>
- </div>
In "employee-detail.component.ts" we need to add the below "Input" Property.
- @Input() public employee: Employee;
Run and check the application
If we run the application, we can see the list of employees first.
When we click any employee row, the employee details are loaded.
Add the Modal popup
Instead of showing the employee detail inside, we will try to show it in the "Modal" popup page. Add "bootstrap" and "ngx-bootstrap" modules in our application.
After installing the bootstrap pcakges, add the below code in "styles.css" file. There are many ways to import "bootstrap" below is one of the ways.
- @import "~bootstrap/dist/css/bootstrap.css"
Then do the below changes in the "AppModule.ts" file:
- Import the "ngx-bootstrap" namespaces - "import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';" and "import { ModalModule } from 'ngx-bootstrap/modal';"
- Add this module in "imports" - ModalModule.forRoot().
- Add these "providers: [BsModalService, BsModalRef],"
In "employee-list.component.ts" add the below reference and code. Line 05-07 are important to pass the data to Modal pop-up.
- import { EmployeeDetailComponent } from '../employee-detail/employee-detail.component';
-
- selectEmployee(emp: Employee) {
- this.employeeSelected.emit(emp);
- const initialState = {
- employee: emp
- };
- this.bsModalRef = this.modalService.show(EmployeeDetailComponent, {initialState});
- this.bsModalRef.content.closeBtnName = 'Close';
- }
In "employee-detail.component.ts" file add the below reference and injector.
- import { BsModalRef } from 'ngx-bootstrap/modal';
-
- constructor(public bsModalRef: BsModalRef) { }
Now run the application and test it.
Added Below Code for Reference
AppModule.ts:
- import { BrowserModule } from '@angular/platform-browser';
- import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
- import { AppRoutingModule } from './app-routing.module';
- import { AppComponent } from './app.component';
- import { EmployeeModule } from './employee/employee.module';
- import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
- import { ModalModule } from 'ngx-bootstrap/modal';
- import { EmployeeDetailComponent } from './employee/employee-detail/employee-detail.component';
- import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
-
- @NgModule({
- declarations: [
- AppComponent
- ],
- imports: [
- ModalModule.forRoot(),
- BrowserModule,
- AppRoutingModule,
- EmployeeModule,
- BrowserAnimationsModule
- ],
- providers: [BsModalService, BsModalRef],
- schemas: [NO_ERRORS_SCHEMA],
- bootstrap: [AppComponent],
- entryComponents: [EmployeeDetailComponent]
- })
- export class AppModule { }
AppComponent.cs
- import { Component } from '@angular/core';
-
- @Component({
- selector: 'app-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css']
- })
- export class AppComponent {
- title = 'test-employee-app';
- }
AppModule.cs,
- <app-employee></app-employee>
EmployeeModule.cs,
- import { NgModule } from '@angular/core';
- import { CommonModule } from '@angular/common';
- import { EmployeeComponent } from './employee.component';
- import { EmployeeDetailComponent } from './employee-detail/employee-detail.component';
- import { EmployeeListComponent } from './employee-list/employee-list.component';
-
- @NgModule({
- declarations: [EmployeeComponent, EmployeeListComponent, EmployeeDetailComponent],
- imports: [
- CommonModule
- ],
- exports: [EmployeeComponent]
- })
- export class EmployeeModule { }
EmployeeComponent.ts,
- import { Component, OnInit } from '@angular/core';
-
- import { Employee } from '../shared/interfaces';
- import { EmployeeDataService } from '../core/services/employeedata.service';
-
- @Component({
- selector: 'app-employee',
- templateUrl: './employee.component.html',
- styleUrls: ['./employee.component.css']
- })
- export class EmployeeComponent implements OnInit {
-
- public employee: Employee;
- public employees: Employee[];
-
- constructor(private employeeService: EmployeeDataService) { }
-
- ngOnInit(): void {
- this.getEmployees();
- }
-
- getEmployees() {
- this.employeeService.getEmployees()
- .subscribe((emps: Employee[]) => this.employees = emps);
- }
- selected(employee: any){
- this.employee = employee;
- }
- }
Employeecomponent.html,
- <div class="row">
- <div class="col-md-6">
- <app-employee-list [employees]="employees" (employeeSelected)='selected($event)'></app-employee-list>
- </div>
- <!-- <div class="col-md-6">
- <app-employee-detail [employee]="employee"></app-employee-detail>
- </div> -->
- </div>
employee-list.component.ts,
- import { Component, OnInit, Input, ChangeDetectionStrategy, EventEmitter, Output } from '@angular/core';
- import { Employee } from 'src/app/shared/interfaces';
- import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
- import { EmployeeDetailComponent } from '../employee-detail/employee-detail.component';
-
- @Component({
- selector: 'app-employee-list',
- templateUrl: './employee-list.component.html',
- styleUrls: ['./employee-list.component.css'],
- changeDetection: ChangeDetectionStrategy.OnPush
- })
-
- export class EmployeeListComponent implements OnInit {
-
- @Input() public employees: Employee[];
- @Output() public employeeSelected = new EventEmitter<Employee>();
- public bsModalRef: BsModalRef;
-
- constructor(private modalService: BsModalService) {}
-
- ngOnInit(): void {
- }
-
- selectEmployee(emp: Employee) {
- this.employeeSelected.emit(emp);
- const initialState = {
- employee: emp
- };
- this.bsModalRef = this.modalService.show(EmployeeDetailComponent, {initialState});
- this.bsModalRef.content.closeBtnName = 'Close';
- }
- }
employee-list.component.html,
- <h4>Employees</h4>
- <table class="table table-striped">
- <tr>
- <th>Name</th>
- <th>Email</th>
- </tr>
- <tr *ngFor="let emp of employees" (click)="selectEmployee(emp)">
- <td>{{ emp.name }}</td>
- <td>{{ emp.email }}</td>
- </tr>
- </table>
- <br />
employee-detail.component.ts,
- import { Component, OnInit, Input, ChangeDetectionStrategy } from '@angular/core';
- import { Employee } from 'src/app/shared/interfaces';
- import { BsModalRef } from 'ngx-bootstrap/modal';
-
- @Component({
- selector: 'app-employee-detail',
- templateUrl: './employee-detail.component.html',
- styleUrls: ['./employee-detail.component.css'],
- changeDetection: ChangeDetectionStrategy.OnPush
- })
-
- export class EmployeeDetailComponent implements OnInit {
-
- @Input() public employee: Employee;
-
- constructor(public bsModalRef: BsModalRef) { }
-
- ngOnInit(): void {
- }
- }
employee-detail.component.html,
- <div class="modal-header">
- <h4 class="modal-title pull-left">Employee Detail</h4>
- <button type="button" class="close pull-right" aria-label="Close" (click)="bsModalRef.hide()">
- <span aria-hidden="true">×</span>
- </button>
- </div>
- <div class="modal-body">
- Name: {{employee.name}}
- <br/>
- Email: {{employee.email}}
- <br/>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" (click)="bsModalRef.hide()">Close</button>
- </div>
Summary
In this post, we learned about Container and Presentation component communication in Angular. Also, we added the modal popup child component.
To understand Container and Presentation components, please refer to the below post.