MockAPI (https://mockapi.io/) is a simple tool that lets you easily mockup APIs, generate custom data, and perform operations on it using RESTful interface. We can consume these API endpoints in our Angular 14 application to test the flow without actual Web APIs.
Create an account in MockAPI.io
It is amazingly easy to create a new account in MockAPI.MockAPI gives various flexible plans. In the free plan, we can create one Project and four resources in that project. In the $5/month plan, we can create 20 projects and 50 resources per project. If you are ready to buy a yearly plan, you will get 20 projects plan at $35.
For testing purposes, we can choose a Free plan. We can create a new Project in this free plan.
We can give a valid project name. It is mandatory. Also, we can give an optional API prefix as well. Please note that we can create up to four resources in a project in Free plan. After creating the project, we can click on the project name, and it will allow us to create resources. We can click on the New Resource button as well. It will open a new popup window and we can give resource details here.
We are going to create an employee management application and will see all the CRUD operations using MockAPI. Hence, we can create the resource with a specific employee model. Please note that each resource must start with an id property. You can add multiple properties as per your requirements.
Currently MockAPI supports seven types of properties.
We can create the employee resource with the following properties.
Each resource will support the below CRUD methods.
You can see a unique API endpoint for each project.
If you click the Generate All button, it will create 50 employee sample records automatically. We can also add more employee records by clicking the employee resource button. Please note that Free plan will support a maximum of 100 records per resource.
You can use the Reset All button to clear all mock data.
We can use the Data button to view all data for a particular resource. We can use the Edit button to add or remove properties from a resource. The Delete button will remove entire resources from the project.
We can create an Angular 14 project and consume these API endpoints easily. We will see all the CRUD actions using our Angular 14 project.
Create Angular 14 project using Angular CLI
We can use the CLI command below to create a new Angular project.
ng new Angular14MockAPI
Choose the routing option as Yes and stylesheet format as CSS.
Add bootstrap and font-awesome libraries to the project.
npm install bootstrap font-awesome
We must change “styles.css” file in the root folder with below changes to access these packages globally in the application without further references.
styles.css
@import "~bootstrap/dist/css/bootstrap.css";
@import "~font-awesome/css/font-awesome.css";
Create an environment variable inside environment class for baseUrl. This will be used across the application.
environment.ts
export const environment = {
production: false,
baseUrl: 'https://63261f0eba4a9c4753234af7.mockapi.io/'
};
Copy the API endpoint value from MockAPI and give it as baseUrl in environment file.
Create an employee interface now.
ng g class employee\employee
Use the below code.
employee.ts
export interface Employee {
id: string,
name: string,
address: string,
company: string,
designation: string,
cityname: string
}
Create an employee service now.
ng g service employee\employee
Use the below code.
employee.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, throwError, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { Employee } from './employee';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
private employeesUrl = environment.baseUrl + 'api/employee';
constructor(private http: HttpClient) { }
getEmployees(): Observable<Employee[]> {
return this.http.get<Employee[]>(this.employeesUrl)
.pipe(
catchError(this.handleError)
);
}
getEmployee(id: string | null): Observable<Employee> {
if (id === '') {
return of(this.initializeEmployee());
}
const url = `${this.employeesUrl}/${id}`;
return this.http.get<Employee>(url)
.pipe(
catchError(this.handleError)
);
}
createEmployee(employee: Employee): Observable<Employee> {
employee.id = '';
return this.http.post<Employee>(this.employeesUrl, employee)
.pipe(
catchError(this.handleError)
);
}
deleteEmployee(id: string): Observable<{}> {
const url = `${this.employeesUrl}/${id}`;
return this.http.delete<Employee>(url)
.pipe(
catchError(this.handleError)
);
}
updateEmployee(employee: Employee): Observable<Employee> {
const url = `${this.employeesUrl}/${employee.id}`;
return this.http.put<Employee>(url, employee)
.pipe(
map(() => employee),
catchError(this.handleError)
);
}
private handleError(err: any) {
let errorMessage: string;
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Backend returned code ${err.status}: ${err.body.error}`;
}
console.error(err);
return throwError(() => errorMessage);
}
private initializeEmployee(): Employee {
return {
id: "",
name: "",
address: "",
company: "",
designation: "",
cityname: ""
};
}
}
We can create a Loader service for entire application. This service can be used for showing a loader while the application is accessing the backend MockAPI.
ng g service loader
Copy the below code.
loader.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LoaderService {
controlLoader!: Subject<boolean>;
constructor() {
this.controlLoader = new Subject<boolean>();
}
show() {
this.controlLoader.next(true);
}
hide() {
this.controlLoader.next(false);
}
}
Add the modules below to the AppModule class.
ReactiveFormsModule
FormsModule
HttpClientModule
We can create an employee list component. This component will be used to display all the employee information. This component is also used to edit and remove employee data.
ng g component employee\EmployeeList
Change the class file with the code below.
employee-list.component.ts
import { Component, OnInit } from '@angular/core';
import { LoaderService } from 'src/app/loader.service';
import { Employee } from '../employee';
import { EmployeeService } from '../employee.service';
@Component({
selector: 'app-employee-list',
templateUrl: './employee-list.component.html',
styleUrls: ['./employee-list.component.css']
})
export class EmployeeListComponent implements OnInit {
pageTitle = 'Employee List';
filteredEmployees: Employee[] = [];
employees: Employee[] = [];
errorMessage = '';
_listFilter = '';
get listFilter(): string {
return this._listFilter;
}
set listFilter(value: string) {
this._listFilter = value;
this.filteredEmployees = this.listFilter ? this.performFilter(this.listFilter) : this.employees;
}
constructor(private employeeService: EmployeeService, private loader: LoaderService) { }
performFilter(filterBy: string): Employee[] {
filterBy = filterBy.toLocaleLowerCase();
return this.employees.filter((employee: Employee) =>
employee.name.toLocaleLowerCase().indexOf(filterBy) !== -1);
}
ngOnInit(): void {
this.getEmployeeData();
}
getEmployeeData() {
this.loader.show();
this.employeeService.getEmployees()
.subscribe({
next: (employees) => {
this.employees = employees;
this.filteredEmployees = employees;
},
error: (err) => {
this.errorMessage = <any>err;
this.loader.hide();
},
complete: () => {
console.info('Get employees in employee list');
this.loader.hide();
}
});
}
deleteEmployee(id: string, name: string): void {
if (id === '') {
this.onSaveComplete();
} else {
if (confirm(`Are you sure want to delete this Employee: ${name}?`)) {
this.loader.show();
this.employeeService.deleteEmployee(id)
.subscribe({
next: () => this.onSaveComplete(),
error: (err) => {
this.errorMessage = <any>err;
this.loader.hide();
},
complete: () => {
console.info('Delete employee in employee list');
this.loader.hide();
}
});
}
}
}
onSaveComplete(): void {
this.employeeService.getEmployees()
.subscribe({
next: (employees) => {
this.employees = employees;
this.filteredEmployees = employees;
},
error: (err) => this.errorMessage = <any>err,
complete: () => console.info('Get employees in employee list')
});
}
}
We can change the template and style files also.
employee-list.component.html
<div class="card">
<div class="card-header">
{{pageTitle}}
</div>
<div class="card-body">
<div class="row" style="margin-bottom:15px;">
<div class="col-md-2">Filter by:</div>
<div class="col-md-4">
<input type="text" [(ngModel)]="listFilter" />
</div>
<div class="col-md-2"></div>
<div class="col-md-4">
<button class="btn btn-primary mr-3" [routerLink]="['/employees/0/edit']">
New Employee
</button>
</div>
</div>
<div class="row" *ngIf="listFilter">
<div class="col-md-6">
<h4>Filtered by: {{listFilter}}</h4>
</div>
</div>
<div class="table-responsive">
<table class="table mb-0" *ngIf="employees && employees.length">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>Company</th>
<th>Designation</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let employee of filteredEmployees">
<td>
<a [routerLink]="['/employees', employee.id]">
{{ employee.name }}
</a>
</td>
<td>{{ employee.address }}</td>
<td>{{ employee.company }}</td>
<td>{{ employee.designation}} </td>
<td>
<button class="btn btn-outline-primary btn-sm"
[routerLink]="['/employees', employee.id, 'edit']">
Edit
</button>
</td>
<td>
<button class="btn btn-outline-warning btn-sm"
(click)="deleteEmployee(employee.id,employee.name);">
Delete
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div *ngIf="errorMessage" class="alert alert-danger">
Error: {{ errorMessage }}
</div>
Change the stylesheet as well.
employee-list.component.css
thead {
color: #337AB7;
}
We can create employee edit component with the below command
ng g component employee\EmployeeEdit
Modify the class file with the code below.
employee-edit.component.ts
import { Component, OnInit, OnDestroy, ElementRef, ViewChildren } from '@angular/core';
import { FormControlName, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';
import { Employee } from '../employee';
import { EmployeeService } from '../employee.service';
import { LoaderService } from 'src/app/loader.service';
@Component({
selector: 'app-employee-edit',
templateUrl: './employee-edit.component.html',
styleUrls: ['./employee-edit.component.css']
})
export class EmployeeEditComponent implements OnInit, OnDestroy {
@ViewChildren(FormControlName, { read: ElementRef }) formInputElements!: ElementRef[];
pageTitle = 'Employee Edit';
errorMessage!: string;
employeeForm!: FormGroup;
tranMode!: string;
employee!: Employee;
private sub!: Subscription;
displayMessage: { [key: string]: string } = {};
private validationMessages: { [key: string]: { [key: string]: string } };
constructor(private fb: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private employeeService: EmployeeService,
private loader: LoaderService) {
this.validationMessages = {
name: {
required: 'Employee name is required.',
minlength: 'Employee name must be at least three characters.',
maxlength: 'Employee name cannot exceed 50 characters.'
},
cityname: {
required: 'Employee city name is required.',
}
};
}
ngOnInit() {
this.tranMode = "new";
this.employeeForm = this.fb.group({
name: ['', [Validators.required,
Validators.minLength(3),
Validators.maxLength(50)
]],
address: '',
cityname: ['', [Validators.required]],
company: '',
designation: '',
});
this.sub = this.route.paramMap.subscribe(
params => {
const id = params.get('id');
const cityname = params.get('cityname');
if (id == '0') {
const employee: Employee = { id: "0", name: "", address: "", company: "", designation: "", cityname: "" };
this.displayEmployee(employee);
}
else {
this.getEmployee(id);
}
}
);
}
ngOnDestroy(): void {
this.sub.unsubscribe();
}
getEmployee(id: string | null): void {
this.loader.show();
this.employeeService.getEmployee(id)
.subscribe({
next: (employee: Employee) => this.displayEmployee(employee),
error: (err) => {
this.errorMessage = <any>err;
this.loader.hide();
},
complete: () => {
console.info('Get employee in employee edit');
this.loader.hide();
}
});
}
displayEmployee(employee: Employee): void {
if (this.employeeForm) {
this.employeeForm.reset();
}
this.employee = employee;
if (this.employee.id == '0') {
this.pageTitle = 'Add Employee';
} else {
this.pageTitle = `Edit Employee: ${this.employee.name}`;
}
this.employeeForm.patchValue({
name: this.employee.name,
address: this.employee.address,
company: this.employee.company,
designation: this.employee.designation,
cityname: this.employee.cityname
});
}
deleteEmployee(): void {
if (this.employee.id == '0') {
this.onSaveComplete();
} else {
if (confirm(`Are you sure want to delete this Employee: ${this.employee.name}?`)) {
this.loader.show();
this.employeeService.deleteEmployee(this.employee.id)
.subscribe({
next: () => this.onSaveComplete(),
error: (err) => {
this.errorMessage = <any>err;
this.loader.hide();
},
complete: () => {
console.info('Delete employee in employee edit');
this.loader.hide();
}
});
}
}
}
saveEmployee(): void {
if (this.employeeForm.valid) {
if (this.employeeForm.dirty) {
const e = { ...this.employee, ...this.employeeForm.value };
if (e.id === '0') {
this.loader.show();
this.employeeService.createEmployee(e)
.subscribe({
next: () => this.onSaveComplete(),
error: (err) => {
this.errorMessage = <any>err;
this.loader.hide();
},
complete: () => {
console.info('Create employee in employee edit');
this.loader.hide();
}
});
} else {
this.loader.show();
this.employeeService.updateEmployee(e)
.subscribe({
next: () => this.onSaveComplete(),
error: (err) => {
this.errorMessage = <any>err;
this.loader.hide();
},
complete: () => {
console.info('Update employee in employee edit');
this.loader.hide();
}
});
}
} else {
this.onSaveComplete();
}
} else {
this.errorMessage = 'Please correct the validation errors.';
}
}
onSaveComplete(): void {
this.employeeForm.reset();
this.router.navigate(['/employees']);
}
}
We can change the template file also.
employee-edit.component.html
<div class="card">
<div class="card-header">
{{pageTitle}}
</div>
<div class="card-body">
<form novalidate (ngSubmit)="saveEmployee()" [formGroup]="employeeForm">
<div class="form-group row mb-2">
<label class="col-md-3 col-form-label" for="employeeNameId">Employee Name</label>
<div class="col-md-7">
<input class="form-control" id="employeeNameId" type="text" placeholder="Name (required)"
formControlName="name" />
</div>
</div>
<div class="form-group row mb-2">
<label class="col-md-3 col-form-label" for="citynameId">City</label>
<div class="col-md-7">
<input class="form-control" id="citynameid" type="text" placeholder="Cityname (required)"
formControlName="cityname" />
</div>
</div>
<div class="form-group row mb-2">
<label class="col-md-3 col-form-label" for="addressId">Address</label>
<div class="col-md-7">
<input class="form-control" id="addressId" type="text" placeholder="Address" formControlName="address" />
</div>
</div>
<div class="form-group row mb-2">
<label class="col-md-3 col-form-label" for="companyId">Company</label>
<div class="col-md-7">
<input class="form-control" id="companyId" type="text" placeholder="Company" formControlName="company" />
</div>
</div>
<div class="form-group row mb-2">
<label class="col-md-3 col-form-label" for="designationId">Designation</label>
<div class="col-md-7">
<input class="form-control" id="designationId" type="text" placeholder="Designation"
formControlName="designation" />
</div>
</div>
<div class="form-group row mb-2">
<div class="offset-md-2 col-md-6">
<button class="btn btn-primary" style="width:80px;margin-right:10px;" type="submit"
[title]="employeeForm.valid ? 'Save your entered data' : 'Disabled until the form data is valid'"
[disabled]="!employeeForm.valid">
Save
</button>
<button class="btn btn-outline-secondary" style="width:80px;margin-right:10px;" type="button"
title="Cancel your edits" [routerLink]="['/employees']">
Cancel
</button>
<button class="btn btn-outline-warning" *ngIf="pageTitle != 'Add Employee'"
style="width:80px;margin-right:10px" type="button" title="Delete this product" (click)="deleteEmployee()">
Delete
</button>
</div>
</div>
</form>
</div>
<div class="alert alert-danger" *ngIf="errorMessage">{{errorMessage}}
</div>
</div>
We need one more component to display the employee details in a separate window. We can create now.
ng g component employee\EmployeeDetail
We can change the class file with the code below.
employee-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { LoaderService } from 'src/app/loader.service';
import { Employee } from '../employee';
import { EmployeeService } from '../employee.service';
@Component({
selector: 'app-employee-detail',
templateUrl: './employee-detail.component.html',
styleUrls: ['./employee-detail.component.css']
})
export class EmployeeDetailComponent implements OnInit {
pageTitle = 'Employee Detail';
errorMessage = '';
employee: Employee | undefined;
constructor(private route: ActivatedRoute,
private router: Router,
private employeeService: EmployeeService,
private loader:LoaderService) { }
ngOnInit() {
const id = this.route.snapshot.paramMap.get('id');
if (id) {
this.getEmployee(id);
}
}
getEmployee(id: string) {
this.loader.show();
this.employeeService.getEmployee(id)
.subscribe({
next: (employee) => this.employee = employee,
error: (err) => {
this.errorMessage = <any>err;
this.loader.hide();
},
complete: () => {
console.info('Get employee in employee details');
this.loader.hide();
}
});
}
onBack(): void {
this.router.navigate(['/employees']);
}
}
Modify the template file with the code below.
employee-detail.component.html
<div class="card">
<div class="card-header"
*ngIf="employee">
{{pageTitle + ": " + employee.name}}
</div>
<div class="card-body"
*ngIf="employee">
<div class="row">
<div class="col-md-8">
<div class="row">
<div class="col-md-3">Name:</div>
<div class="col-md-6">{{employee.name}}</div>
</div>
<div class="row">
<div class="col-md-3">City:</div>
<div class="col-md-6">{{employee.cityname}}</div>
</div>
<div class="row">
<div class="col-md-3">Address:</div>
<div class="col-md-6">{{employee.address}}</div>
</div>
<div class="row">
<div class="col-md-3">Company:</div>
<div class="col-md-6">{{employee.company}}</div>
</div>
<div class="row">
<div class="col-md-3">Designation:</div>
<div class="col-md-6">{{employee.designation}}</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-4">
<button class="btn btn-outline-secondary mr-3"
style="width:80px;margin-right:10px;"
(click)="onBack()">
<i class="fa fa-chevron-left"></i> Back
</button>
<button class="btn btn-outline-primary"
style="width:80px;margin-right:10px;"
[routerLink]="['/employees', employee.id,'edit']">
Edit
</button>
</div>
</div>
</div>
<div class="alert alert-danger"
*ngIf="errorMessage">
{{errorMessage}}
</div>
</div>
Create the navigation menu component now.
ng g component NavMenu
Modify the class file with the code below.
nav-menu.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html',
styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent implements OnInit {
isExpanded = false;
ngOnInit() {
}
collapse() {
this.isExpanded = false;
}
toggle() {
this.isExpanded = !this.isExpanded;
}
}
Change the template file with the code below.
nav-menu.component.html
<header>
<nav class='navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3'>
<div class="container">
<a class="navbar-brand" [routerLink]='["/"]'>Employee App</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse"
aria-label="Toggle navigation" [attr.aria-expanded]="isExpanded" (click)="toggle()">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse" [ngClass]='{"show": isExpanded}'>
<ul class="navbar-nav flex-grow">
<li class="nav-item" [routerLinkActive]='["link-active"]' [routerLinkActiveOptions]='{ exact: true }'>
<a class="nav-link text-dark" [routerLink]='["/"]'>Home</a>
</li>
<li class="nav-item" [routerLinkActive]='["link-active"]'>
<a class="nav-link text-dark" [routerLink]='["/employees"]'>Employees</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<footer>
<nav class="navbar navbar-light bg-white mt-5 fixed-bottom">
<div class="navbar-expand m-auto navbar-text">
Developed with <i class="fa fa-heart"></i> by <b>Sarathlal
Saseendran</b>
</div>
</nav>
</footer>
We can also change the stylesheet file with the code below.
nav-menu.component.css
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.fa-heart {
color: hotpink;
}
Create the final component Home now.
ng g component home
There is no code change for the class file. We can change the html template file with the code below.
home.component.html
<div style="text-align:center;">
<h3>Easily use MockAPI.io with Angular 14</h3>
<p>Welcome to our new single-page Employee application using MockAPI</p>
<img src="../../assets/angular mockapi.jpg" width="800px">
</div>
We must add below route values in the app-routing.module class as well.
app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { EmployeeDetailComponent } from './employee/employee-detail/employee-detail.component';
import { EmployeeEditComponent } from './employee/employee-edit/employee-edit.component';
import { EmployeeListComponent } from './employee/employee-list/employee-list.component';
import { HomeComponent } from './home/home.component';
const routes: Routes = [
{ path: '', component: HomeComponent, pathMatch: 'full' },
{
path: 'employees',
component: EmployeeListComponent
},
{
path: 'employees/:id',
component: EmployeeDetailComponent
},
{
path: 'employees/:id/edit',
component: EmployeeEditComponent
},
]
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Change the AppComponent with the code below.
app.component.ts
import { AfterContentChecked, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { LoaderService } from './loader.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterContentChecked {
title = 'Angular14MockAPI';
showLoader!: boolean;
constructor(private loader: LoaderService, private ref: ChangeDetectorRef) {
}
ngOnInit() {
this.loader.controlLoader.subscribe((result) => {
this.showLoader = result;
});
}
ngAfterContentChecked() {
this.ref.detectChanges();
}
}
We can change the app.component.html with the code below.
app.component.html
<body>
<app-nav-menu></app-nav-menu>
<div class="container">
<div class="file-loader" *ngIf="showLoader">
<div class="upload-loader">
<div class="loader"></div>
</div>
</div>
<router-outlet></router-outlet>
</div>
</body>
Change the stylesheet with the code below.
app.component.css
/* Spin Start*/
.file-loader {
background-color:
rgba(0, 0, 0, .5);
overflow: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100000 !important;
}
.upload-loader {
position: absolute;
width: 60px;
height: 60px;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.upload-loader .loader {
border: 5px solid
#f3f3f3 !important;
border-radius: 50%;
border-top: 5px solid
#005eb8 !important;
width: 100% !important;
height: 100% !important;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Spin End*/
We have completed the entire coding part. We can run the application now.
Click the Employees menu and add a new employee record.
Added employees will be listed as a grid format on the screen.
We have search, edit, and remove features also available in the application.
Conclusion
In this post, we have seen how to create a mock API using MockAPI.io. MockAPI provides a free plan to create one project and four resources. We have created an employee resource and consumed this resource from an Angular 14 application. We have seen all CRUD operations using MockAPI and Angular 14 application. MockAPI also provides various paid plans as well. You can try from your end and feel free to write your valuable feedback.