visit
Angular is one of the most popular Javascript frameworks out there. On the other hand, Material UI is a library that provides us with a large collection of ready-made components such as buttons, toolbars, icons, inputs, etc. Material UI will make our development process super fast.
node -v
ng version
If these tools are not installed then this will give an error otherwise this will return the version number. To install NodeJS go to and if you install NodeJS, npm will also get installed along with it.
npm install -g @angular/cli
npm install -g json-server
ng new simple-employee-management-crud-app
ng add @angular/material
cd simple-employee-management-crud-app
ng serve -o
Now let’s start coding step-by-step:
Create a db.json
file in the project root directory, not inside the src/
or app/
directory. Now open the db.json
file and paste the following code inside:
{
"employees": [
{
"id": 1,
"firstName": "Brinn",
"lastName": "Jephcote",
"email": "[email protected]",
"dob": "1981-10-05T12:09:39Z",
"gender": "Female",
"education": "Graduate",
"company": "Gabspot",
"experience": 36,
"salary": 37
},
{
"id": 2,
"firstName": "Kenneth",
"lastName": "MacElholm",
"email": "[email protected]",
"dob": "1991-09-12T22:14:02Z",
"gender": "Male",
"education": "Matric",
"company": "Agivu",
"experience": 75,
"salary": 17
}
]
}
Here we’re just storing some employee details. This db.json
file is our database and the employees array our table!
Now open another terminal tab or split the terminal in VSCode then start the json-server
via the following command:
json-server --watch 'db.json'
This command will locally start a server at port 3000 where we will send our requests for creating, reading, updating, and deleting an employee.
ng generate service employee –skip-tests
This command will create a boilerplate service file for us inside the src/app
directory called employee.service.ts
. Next, let's implement the logic for getting, adding, updating, and deleting employees in the employee service.
Open the employee.service.ts
file and paste the following code:
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class EmployeeService {
baseUrl: string = "//localhost:3000/";
constructor(private httpClient: HttpClient) { }
addEmployee(data: any): Observable<any> {
return this.httpClient.post(this.baseUrl + 'employees', data);
}
updateEmployee(id: number, data: any): Observable<any> {
return this.httpClient.put(this.baseUrl + employees / ${ id }, data);
}
getEmployeeList(): Observable<any> {
return this.httpClient.get(this.baseUrl + 'employees');
}
deleteEmployee(id: number): Observable<any> {
return this.httpClient.delete(this.baseUrl + employees / ${ id });
}
}
Here, we’ve created four methods for getting an employee list, adding an employee, updating an employee, and deleting an employee respectively by using the built-in HttpClient service for sending requests to our JSON-server.
In Angular, a component is a building block of the application. It is responsible for managing a piece of the UI, handling user interactions, and rendering data. We will create one additional component in our app: emp-add-edit
component. This component will be responsible for adding or updating an employee.
ng generate component emp-add-edit –skip-tests
This command will create a directory called emp-add-edit
inside our /src/app
folder and some other necessary files with some boilerplate code for you. We will use this component as the dialog(modal) to create and update an employee. So for now let’s keep it like this and move on to make the toolbar and employee table inside the app component. To do so, open up the app.component.html
file and paste the following code inside it.
<!-- The toolbar of our app -->
<mat-toolbar color="primary">
<span>Employee Management Crud App</span>
<span class="example-spacer"></span>
<button mat-raised-button color="accent" (click)="openAddEditEmployeeDialog()">ADD EMPLOYEE</button>
</mat-toolbar>
<!-- The body of our app -->
<div class="main-body">
<!-- The filter section -->
<mat-form-field aria-haspopup="outline">
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="i.e David Smith" #input>
</mat-form-field>
<!-- The employee details table -->
<div class="table-container">
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ID </th>
<td mat-cell *matCellDef="let row"> {{row.id}} </td>
</ng-container>
<ng-container matColumnDef="firstName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> First Name </th>
<td mat-cell *matCellDef="let row"> {{row.firstName}}</td>
</ng-container>
<ng-container matColumnDef="lastName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Last Name </th>
<td mat-cell *matCellDef="let row"> {{row.lastName}}</td>
</ng-container>
<ng-container matColumnDef="email">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Email </th>
<td mat-cell *matCellDef="let row"> {{row.email}}</td>
</ng-container>
<ng-container matColumnDef="dob">
<th mat-header-cell *matHeaderCellDef mat-sort-header> DOB </th>
<td mat-cell *matCellDef="let row"> {{row.dob | date}}</td>
</ng-container>
<ng-container matColumnDef="gender">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Gender </th>
<td mat-cell *matCellDef="let row"> {{row.gender}}</td>
</ng-container>
<ng-container matColumnDef="education">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Education </th>
<td mat-cell *matCellDef="let row"> {{row.education}}</td>
</ng-container>
<ng-container matColumnDef="company">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Company </th>
<td mat-cell *matCellDef="let row"> {{row.company}}</td>
</ng-container>
<ng-container matColumnDef="experience">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Experience </th>
<td mat-cell *matCellDef="let row"> {{row.experience}}</td>
</ng-container>
<ng-container matColumnDef="salary">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Salary </th>
<td mat-cell *matCellDef="let row"> {{row.salary | currency:'USD'}}L</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Action </th>
<td mat-cell *matCellDef="let row">
<span (click)="openEditForm(row)" class="action-icon" style="margin-right: 5px;">
<mat-icon color="primary">edit</mat-icon>
</span>
<span (click)="deleteEmployee(row.id)" class="action-icon">
<mat-icon color="warn">delete</mat-icon>
</span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<!-- This row will be shown when there is no matching data. -->
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4">No data matching the filter "{{input.value}}"</td>
</tr>
</table>
<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" aria-label="Select page of users"></mat-paginator>
</div>
</div>
In the first section, we’re creating the toolbar of our app, and then in the second section, we’re creating the table for the list of employees and also creating pagination. We’re creating these using the Material UI library (if you notice all those custom tags, and attributes starting with “mat”). In the toolbar, we created a button for adding an employee and bound a click event to this button. In the click event, we’re passing a method openAddEditEmployeeDialog()
.
This function will be used to invoke and open our dialog (emp-add-edit
component). So now let’s paste the following CSS code in the app.component.css
file to make our toolbar more beautiful:
.example-spacer {
flex: 1 1 auto;
}
mat-form-field {
width: 100%;
margin: auto;
}
.main-body {
max-width: 80%;
margin: 20px auto;
}
.action-icon:hover {
opacity: 0.8;
cursor: pointer;
}
So let’s open the app.component.ts
file and paste the following code:
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { EmpAddEditComponent } from './emp-add-edit/emp-add-edit.component';
import { EmployeeService } from './employee.service';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
// the columns that will be displayed in the employee details table
displayedColumns: string[] = [
'id',
'firstName',
'lastName',
'email',
'dob',
'gender',
'education',
'company',
'experience',
'salary',
'action',
];
// employee list will be assigned to this and it is passed as the data source to the mat-table in the HTML template
dataSource!: MatTableDataSource<any>;
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
// dependency injection
constructor(
private dialog: MatDialog,
private empService: EmployeeService,
) {}
ngOnInit(): void {
this.getEmployeeList();
}
openAddEditEmployeeDialog() {
const dialogRef = this.dialog.open(EmpAddEditComponent);
dialogRef.afterClosed().subscribe({
next: (val) => {
if (val) {
this.getEmployeeList();
}
},
});
}
getEmployeeList() {
this.empService.getEmployeeList().subscribe({
next: (res) => {
this.dataSource = new MatTableDataSource(res);
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
console.log(res);
},
error: (err) => {
console.log(err);
},
});
}
// for searching employees with firstname, lastname, gennder, etc
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
if (this.dataSource.paginator) {
this.dataSource.paginator.firstPage();
}
}
deleteEmployee(id: number) {
let confirm = window.confirm("Are you sure you want to delete this employee?");
if(confirm) {
this.empService.deleteEmployee(id).subscribe({
next: (res) => {
alert('Employee deleted!');
this.getEmployeeList();
},
error: (err) => {
console.log(err);
},
});
}
}
openEditForm(data: any) {
const dialogRef = this.dialog.open(EmpAddEditComponent, {
data,
});
dialogRef.afterClosed().subscribe({
next: (val) => {
if (val) {
this.getEmployeeList();
}
}
});
}
}
At first, we’re importing the components we used in our app.component.html from the Material UI library. Then we’re initiating some properties like displayedColumns
and dataSource
for the mat-table
component. And at last, we’ve created some methods like openAddEditEmployeeDialog()
, getEmployeeList()
, applyFilter()
, deleteEmployee()
, openEditForm()
. Some of these methods we used in the app.component.html
file to open the dialog or delete an employee or update an employee or filter the table.
Now that our app component is complete if you try to view the app you will get a bunch of dirty errors. And why is that? Because we still didn’t import all those modules we used from the material-ui library in the app.module.ts
. So, now let’s open app.module.ts
and paste the following code:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { EmpAddEditComponent } from './emp-add-edit/emp-add-edit.component';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
@NgModule({
declarations: [
AppComponent,
EmpAddEditComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MatToolbarModule,
MatIconModule,
MatButtonModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
MatNativeDateModule,
MatRadioModule,
MatSelectModule,
ReactiveFormsModule,
HttpClientModule,
MatTableModule,
MatPaginatorModule,
MatSortModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Now that our app component is complete, it's time to prepare the emp-add-edit
component. We will first prepare the emp-add-edit.component.html
.
Open emp-add-edit.component.html
file and paste the following code:
<div mat-dialog-title>
<h1>{{data ? 'Edit': 'Add'}} Employee Form</h1>
</div>
<form [formGroup]="empForm" (ngSubmit)="onSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<mat-form-field appearance="outline">
<mat-label>First name:</mat-label>
<input matInput type="text" placeholder="i.e David" formControlName="firstName">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Last Name:</mat-label>
<input matInput placeholder="i.e Smith" formControlName="lastName">
</mat-form-field>
</div>
<div class="row">
<mat-form-field appearance="outline">
<mat-label>Email:</mat-label>
<input matInput type="email" placeholder="i.e [email protected]" formControlName="email">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Date of birth:</mat-label>
<input matInput [matDatepicker]="picker" formControlName="dob">
<mat-hint>MM/DD/YYYY</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</div>
<div class="row">
<mat-radio-group aria-label="Select an option" formControlName="gender">
<mat-label><b>Gender:</b></mat-label>
<mat-radio-button value="male">Male</mat-radio-button>
<mat-radio-button value="female">Female</mat-radio-button>
<mat-radio-button value="others">Others</mat-radio-button>
<mat-radio-button value="others">Not Interested</mat-radio-button>
</mat-radio-group>
</div>
<div class="row">
<mat-form-field appearance="outline">
<mat-label>Education:</mat-label>
<mat-select formControlName="education">
<mat-option *ngFor="let val of education" [value]="val">{{val}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Company:</mat-label>
<input matInput placeholder="i.e Amazon" formControlName="company">
</mat-form-field>
</div>
<div class="row">
<mat-form-field appearance="outline">
<mat-label>Experience:</mat-label>
<input matInput placeholder="i.e 5" type="number" formControlName="experience">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Salary:</mat-label>
<input matInput placeholder="i.e 15" type="number" formControlName="salary">
<mat-hint>In dollar</mat-hint>
</mat-form-field>
</div>
</div>
<div mat-dialog-actions class="action-btns">
<button style="margin: 10px;" mat-raised-button color="accent" type="submit">{{data ? 'Update': 'Save'}}</button>
<button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">Cancel</button>
</div>
</form>
.row {
display: flex;
justify-content: space-between;
gap: 20px;
margin-bottom: 10px;
}
mat-form-field {
width: 100%;
}
mat-hint {
margin: 5px;
}
.action-btns {
float: right;
margin-right: 10px;
}
.action-btns button {
padding: 20px;
}
Now let’s add the logic of creating and updating an employee in the emp-add-edit.component.ts
via pasting the following code:
import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { EmployeeService } from '../employee.service';
@Component({
selector: 'app-emp-add-edit',
templateUrl: './emp-add-edit.component.html',
styleUrls: ['./emp-add-edit.component.css'],
})
export class EmpAddEditComponent implements OnInit {
empForm: FormGroup;
education: string[] = [
'Matric',
'Diploma',
'Intermediate',
'Graduate',
'Post Graduate',
];
constructor(
private empService: EmployeeService,
private dialogRef: MatDialogRef<EmpAddEditComponent>,
private formBuilder: FormBuilder,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.empForm = this.formBuilder.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
email: ['', Validators.required],
dob: ['', Validators.required],
gender: ['', Validators.required],
education: ['', Validators.required],
company: ['', Validators.required],
experience: ['', Validators.required],
salary: ['', Validators.required]
});
}
ngOnInit(): void {
this.empForm.patchValue(this.data);
}
onSubmit() {
if (this.empForm.valid) {
if (this.data) {
this.empService
.updateEmployee(this.data.id, this.empForm.value)
.subscribe({
next: (val: any) => {
alert('Employee details updated!');
this.dialogRef.close(true);
},
error: (err: any) => {
console.error(err);
alert("Error while updating the employee!");
},
});
} else {
this.empService.addEmployee(this.empForm.value).subscribe({
next: (val: any) => {
alert('Employee added successfully!');
this.empForm.reset();
this.dialogRef.close(true);
},
error: (err: any) => {
console.error(err);
alert("Error while adding the employee!");
},
});
}
}
}
}
Here, we’re collecting the input values using the FormBuilder modules and making sure that the required inputs are given using the Validators module. When the user clicks on the update or save button (if you’ve noticed we used conditional rendering in the HTML template to show update or save depending on the data property) an existing employee will update or a new employee will be added to our table.