Container Components in Angular

Introduction

 
In this post, we will learn about Container and Presentation components in Angular with a sample project.
 
Prerequisites
  • Visual Studio Code
  • Node JS and NPM
  • Angular 8 or above
  • Angular CLI 

About Container Components

 
Container components are similar to Parent-Child components. A container component acts as a Parent and the "Presentation component" is like a child component. Container components will manage and retrieve object/model states, whereas Presentation components are just present or render the state.
  • The container component will share data/state to the Presentation component using "Input" properties.
  • The presentation component will communicate to the Container component using "Output" properties.
  • You need to set a "Change detection strategy" to push state changes to the Container component.
  • The presentation/Child component should not change the state/data.

Create an Angular Application

 
We need to run the below command to create an Angular Application:
  1. ng new test-employee-app  
 Once the application is created, run the below command to make sure that it's created correctly. 
  1. cd test-employee-app  
  2. ng serve  
We can see the command results with application endpoint URL:
 
Container Components In Angular 
 
If we run the application, we can see the below Test page. 
 
Container Components In Angular 
 
Add Modules and Components
To add modules and components run the below commands. we will create a "employee" module and component with the employeedetail child component. 
  1. ng g m employee  
  2. ng g c employee  
  3. ng g c employee/employeedetail  
Add Parent(Container) and child(presentation) component codes
 
We need to add below code in "employee.component.ts".  We have added "employeeData" property and assigned a test data. (Line:02 and 05 to 08). 
  1. export class EmployeeComponent implements OnInit {  
  2.   public employeeData: any;  
  3.   constructor() { }  
  4.   ngOnInit(): void {  
  5.     this.employeeData = {  
  6.       name : 'Test EmployeeName',  
  7.       email: 'Test1@testemail.com'  
  8.     };  
  9.     console.log(this.employeeData.name);  
  10.   }  
  11. }  
In "employee.component.html", we need to add the "app-employee-detail" tag and "[employeedata]" property binding. The employee detail child component html content will be placed in this placeholder. 
  1. <p>Employee</p>  
  2. <app-employee-detail [employeeData]="employeeData"></app-employee-detail>  
 Add the below code in "employee-detail.component.ts". In Line 02 we added the "@Input" which will inject input property from Parent controller.
  1. export class EmployeeDetailComponent implements OnInit {  
  2. @Input() public employeeData: any;  
  3.   constructor() { }  
  4.   ngOnInit(): void {  
  5.     console.log(this.employeeData.name);  
  6.   }  
  7. }  
 Also add the below code in "" html file to display the values in UI. 
  1. Name: {{employeeData.name}}  
  2. <br/>  
  3. Email: {{employeeData.email}}  
Now run the application to see the output. Now, we have successfully passed the data from Parent to child component using "Input" properties.
 
Container Components In Angular  
Add Output Property
 
Now we will try to change data in the child component and pass it to the container component using the "Output" property.
 
Add the below output property in "employee-detail" component. Line 03 we have added "employeeChanged" output property. Also, I added the "employeeChange" event to change the employee name (Line 07 to 11).
  1. export class EmployeeDetailComponent implements OnInit {  
  2. @Input() public employeeData: any;  
  3. @Output() public employeeChanged = new EventEmitter<any>();  
  4.   constructor() { }  
  5.   ngOnInit(): void {   
  6.   }  
  7.   employeeChange(){  
  8.     console.log(this.employeeData.name);  
  9.     this.employeeData.name =  'Name Changed from EmployeeDetail';  
  10.     this.employeeChanged.emit(this.employeeData);  
  11.   }  
  12. }  
 Add the below "button" control to "employee-detail.html" to trigger "employeeChange" event.
  1. <button (click)="employeeChange()">Update Employee</button>  
 To receive data in "Employee" component, add the below "employeeChanged" event to update state from child component.
  1. <app-employee-detail [employeeData]="employeeData" (employeeChanged)='changed($event)'></app-employee-detail>  
Also implement the "changed" event in "employee" component cs file. 
  1. changed(employeeData: any){  
  2.     this.employeeData = employeeData;  
  3.   }  
Now run the application to test our changes and the initial employee data loaded.
 
Container Components In Angular
Once we click the "Update Employee" button, "Employee Name" is changed as expected.
 
Container Components In Angular
Add Change detection strategy
 
The strategy that the default change detector uses to detect changes is called "ChangeDetectionStrategy". By default, angular will use the "Default" strategy to all the child components.
 
"Presentation/Child component should not change the state/data", we can achieve this point using change detection strategy. Our child components have various user events, timer, promises. Angular will detect changes for all these events, we don't need to detect changes for all these events, we need only to trigger if any input or output property got changed. This will prevent the presentation/child component from updating the state.
 
In "employee-component.ts", add the below line to implement the change detection strategy. 
  1. changeDetection: ChangeDetectionStrategy.OnPush  
Codes
 
Added below codes for your reference:
employee.module.ts:
  1. import { NgModule } from '@angular/core';  
  2. import { CommonModule } from '@angular/common';  
  3. import { EmployeeComponent } from './employee.component';  
  4. import { EmployeeDetailComponent } from './employee-detail/employee-detail.component';  
  5.   
  6. @NgModule({  
  7.   declarations: [EmployeeComponent, EmployeeDetailComponent],  
  8.   imports: [  
  9.     CommonModule  
  10.   ],  
  11.   exports: [EmployeeComponent]  
  12. })  
  13. export class EmployeeModule { }  
employee.component.ts:
  1. import { Component, OnInit } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'app-employee',  
  5.   templateUrl: './employee.component.html',  
  6.   styleUrls: ['./employee.component.css']  
  7. })  
  8. export class EmployeeComponent implements OnInit {  
  9.   
  10.   public employeeData: any;  
  11.   
  12.   constructor() { }  
  13.   
  14.   ngOnInit(): void {  
  15.     this.employeeData = {  
  16.       name : 'Test EmployeeName',  
  17.       email: 'Test1@testemail.com'  
  18.     };  
  19.     console.log(this.employeeData.name);  
  20.   }  
  21.   
  22.   changed(employeeData: any){  
  23.     this.employeeData = employeeData;  
  24.   }  
  25. }  
 employee.component.html
  1. <p>Employee</p>  
  2. <app-employee-detail [employeeData]="employeeData" (employeeChanged)='changed($event)'></app-employee-detail>  
 employee-detail.component.ts,
  1. import { Component, OnInit, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'app-employee-detail',  
  5.   templateUrl: './employee-detail.component.html',  
  6.   styleUrls: ['./employee-detail.component.css'],  
  7.   changeDetection: ChangeDetectionStrategy.OnPush  
  8. })  
  9.   
  10. export class EmployeeDetailComponent implements OnInit {  
  11.   
  12. @Input() public employeeData: any;  
  13.   
  14. @Output() public employeeChanged = new EventEmitter<any>();  
  15.   
  16.   constructor() { }  
  17.   
  18.   ngOnInit(): void {  
  19.   }  
  20.   
  21.   employeeChange(){  
  22.     console.log(this.employeeData.name);  
  23.     this.employeeData.name =  'Name Changed from EmployeeDetail';  
  24.     this.employeeChanged.emit(this.employeeData);  
  25.   }  
  26. }  
 employee-detail.component.html:
  1. Name: {{employeeData.name}}  
  2. <br/>  
  3. Email: {{employeeData.email}}  
  4. <br/>  
  5. <button (click)="employeeChange()">Update Employee</button>  

Summary

 
In this post we learned about "Container(parent)" component and "Presentation(child)" components. We looked at how these components communicate with each other and saw the importance of change detection strategies.