Implementing Simple Progress Bar using Angular,Ajax and Node

Angular&NodeEnthusiast
4 min readApr 2, 2020

--

progressBar

I have already written a post how to show spinners to keep users posted on the processing happening in the background.

In this post, I will using a Node server to fetch data and showing how you could display a nice progress bar in an Angular application and using Ajax as well

Lets begin with server side. I will be using a photos.json file as the data store. It has 5000 objects.

photos.json

We will use Node to read this file and send the data to the Angular application. We have used the file system module to read the file asynchronously. An important point to note here is the header.

res.set(“Content-Length”,fileSizeInBytes);

This header is a very important step in implementing the progress bar because it tells the Angular application about the total size of data it is about to receive.

Since we sending only the data in the file and not downloading the file as a whole, it is essential to set this header.

//server.js
const express=require(‘express’);
const bodyParser=require(‘body-parser’);
const cors=require(‘cors’);
const fs=require(‘fs’);
const app=express();
app.use(bodyParser.json({type:”application/json”}));
app.use(cors());
//Routeapp.get(‘/comments’,function(req,res){const stats = fs.statSync(“photos.json”);const fileSizeInBytes = stats[“size”]; //get the size of the filefs.readFile(‘photos.json’,”utf8",function(err,data){if(err) throw err;res.set(“Content-Length”,fileSizeInBytes);res.send(data);})})app.listen(8080,function(){console.log(“app listening on port 8080”);
//server listening on port 8080
})

I. Angular implementation

Now lets go to the Angular project.

We just have 2 components=>

ComponentA to make a call to the service which in turn calls the server to retrieve the data.

ProgressComponent to show a reusable progress bar.

We have 2 services=>

ServiceA to retrieve data from the server.

InterceptorService which monitors the progress of the Http request.

ServiceA:

export class ServiceA{constructor(private http:HttpClient) { }private progressSub=new BehaviorSubject(0);returnProgressObservable(){
return this.progressSub.asObservable();
}
returnProgress(data){
this.progressSub.next(data);
}
getPhoto(){
return this.http.get(‘http://localhost:8080/comments',{reportProgress:true}).pipe(catchError(err=>throwError(err)))
}}

ComponentA Template:

//template
<button id=”but”>Load Data</button>
<app-progress [ratio]=”ratio”></app-progress>

ComponentA Class: We are calling the getPhoto() in the ServiceA to retreive and log the json data.

The component subscribes to the subject defined in ServiceA.The subject basically returns a progress ratio(which we shall see later) to the component. We are in turn passing this ratio to the ProgressComponent using property binding.

<app-progress [ratio]=”ratio”></app-progress>

export class ComponentA implements OnInit {constructor(private serv:ServiceA) { }ratio:number=0;ngOnInit() {this.serv.returnProgressObservable().subscribe((data)=>this.ratio=data); //subscribing to the subjectfromEvent(document.getElementById(‘but’),’click’).pipe(mergeMap(form=>this.serv.getPhoto())).subscribe(data=>console.log(data))//log the JSON data}}

ProgressComponent Template:

<div id=”progressContainer”><div #prog id=”progress”></div></div><p #percent></p>

ProgressComponent Class: ProgressComponent receives the progress ratio from Component via property binding.We are just multiplying this ratio by 100 to get the percentage. We are using this percentage to fix the width of the progress bar.

export class ProgressComponent implements OnInit {constructor() { }@Input() ratio:number=0;@ViewChild(‘prog’)prog:ElementRef;@ViewChild(‘percent’)percent:ElementRef;ngOnChanges(changes:SimpleChanges){this.prog.nativeElement.style.width=(changes.ratio.currentValue*100)+”%”;this.percent.nativeElement.innerHTML=(changes.ratio.currentValue*100)+”% loaded”;}ngOnInit() {}}

As you can see, we have 2 div tags. The outer div is just a container.We shall be modifying the width of the inner div. You could add a different background color to the inner div to show the user the change in the width.

#progressContainer{width:100%;background-color:rgb(243, 111, 111);}#progress{width:0%;background-color: green;height:30px;}

Finally,the InterceptorService

export class InterceptorService implements HttpInterceptor {constructor(private serv:ServiceA) { }intercept(request:HttpRequest<any>,next:HttpHandler):Observable<HttpEvent<any>>{return next.handle(request).pipe(tap(event=>{if(event.type==HttpEventType.DownloadProgress){
this.serv.returnProgress(event.loaded/event.total);
}
if(event.type==HttpEventType.Response){
console.log("Content downloaded completely");
}
if(event.type==HttpEventType.Sent){
console.log("Request sent");
}
))
}}

event.load is the no of bytes loaded.

event.total is the total no of bytes that needs to be loaded. This number is set from the value assigned to Content-Length header sent by the server. If the header isnt set,event.total shall be 0 or undefined.

event.load/event.total gives the progress ratio.

We are calling the returnProgress() on ServiceA to call next() on the Subject instance,passing the progress ratio as argument.

ComponentA which is subscribed to the subject receives this ratio and passes it to the ProgressComponent for using it in the progress bar.

Since we retrieving data from the server, the event will be a HttpEventType.DownloadProgress.

HttpEventType.Response indicates the response has been received successfully.

HttpEventType.Sent indicates the request has been sent.

II. Ajax Implementation

Lets now try to implement the Progress Bar using the same server.js,photos.json but using ajax.

HTML is the same as ProgressComponent Template

<div id=”progressContainer”>
<div id=”progress”></div>
</div>
<p id=”percent”></p>

We are using XMLHttpRequest to connect to the server and get the data.

What is important here is the onprogress event where we are calculating the progress ratio. Here again, the lengthComputable property will be true only when the Content-Length header is set on the server side. Else it will be false and the ratio can never be calculated.

<script>
var progressBar=document.getElementById(“progress”);
if(window.XMLHttpRequest)
{
const http=new XMLHttpRequest();
http.open(“GET”,”http://localhost:8080/comments",true);
//async GET request
http.onprogress=function(pe)
{
if(pe.lengthComputable)
{
document.getElementById(‘progress’).style.width=((pe.loaded/pe.total)*100)+”%”;
document.getElementById(“percent”).innerHTML=((pe.loaded/pe.total)*100)+”% loaded”;
}
}
http.onreadystatechange=function()
{
if(this.readyState==4 && this.status==200)
{
console.log(this.responseText); //log the JSON data
}}
http.send(); //send the request to server}</script>

Please let me know your comments:)

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Angular&NodeEnthusiast
Angular&NodeEnthusiast

Written by Angular&NodeEnthusiast

Loves Angular and Node. I wish to target issues that front end developers struggle with the most.

No responses yet

Write a response