Building a Custom Progress Bar in Angular 8

Perry Martijena
May 18 · 6 min read
Let’s make some progress!

Sometimes using Angular Material can be a little overkill. For a new project at work, I wanted to create a custom styled progress bar. Angular Material was a little too complex for what I needed, so I ended up implementing my own. Here’s how to build a custom progress bar in 5 simple steps:

Step 1) Create the progress bar component

In Angular, a component is simply a directive with a template. To generate your own component, type in the following into the command line in your Angular project:

ng g c progress-bar

As you probably know, this generates a new component (ng generate component == ng g c). Your newly created component (progress-bar.component.ts) should look something like this:

import { Component, OnInit } from '@angular/core';@Component({
selector: 'app-progress-bar'
...
styleUrls: ['./progress-bar.component.css']
})
export class ProgressBarComponent implements OnInit { constructor() { } ngOnInit() { }
}

Step 2) Create the Template

The next step is to create our template. You can choose to create your template inline in the component definition or in an external template. In this case, it’s a very small template so I decided to do it inline.

@Component({
selector: 'app-progress-bar',
template: `
<div class="progress-bar-container">
<div class="progress-bar {{color}}"
[ngStyle]="{'width': progress + '%'}">
</div>
</div>
`,
...
})

The template is fairly simple but has a few variables we’ll need to create. As you can see, we’re binding a color property which will be a class name. We’re later going to use this to change the color of our progress bar based on the percentage.

We also have the progress property, which we’re adding as a percentage to the directive which is setting the width of the progress bar dynamically.

Step 3) Create Inputs

Now that our component is setup comes the fun part, the code! Right now, your component should look something like this:

@Component({
selector: 'app-progress-bar',
template: `
<div class="progress-bar-container">
<div class="progress-bar {{color}}"
[ngStyle]="{'width': progress + '%'}">
</div>
</div>
`,
styleUrls: ['./progress-bar.component.css']
})
export class ProgressBarComponent implements OnInit { constructor() { } ngOnInit() { }
}

We’re going to add two inputs for our component. An input allows us to bind values to our component.

Ideally, we’d like to be able to do something like this:

<app-progress-bar [progress]="someProperty"></app-progress-bar>

someProperty having some sort of a number value.

So what inputs do we need? We know we need a progress input to get the current progress, but how do we know what that progress is relative to? We could default it to 100, but that might not always be correct. For instance, if there are 10 steps in a wizard and only 3 are completed so far, we need to know the total number of steps. We should add an optional total input that accepts a number, so we know what to compare the progress input to. If we don’t pass the total, we can default it to some other number like 100.

<app-progress-bar [progress]="someProperty" [total]="someOtherProperty"></app-progress-bar>

To do this, simply add the following above the constructor:

@Input() progress: number;
@Input() total: number;

Inputs are optional by default, so we don’t need to do anything special for the total. Make sure to add to your imports from “@angular/core”.

import { Component, OnInit, Input } from '@angular/core';

We also need the color property we talked about earlier, so let’s add that underneath our inputs while we’re at it.

@Input() progress: number;
@Input() total: number;
color: string;

Step 4) Progress Logic

Now that we have our inputs, we need some logic to handle initialization of the progress bars. This is the code we’re going to be putting in our method:

//if we don't have progress, set it to 0.
if(!this.progress) {
this.progress = 0;
}
//if we don't have a total aka no requirement, it's 100%.
if(this.total === 0) {
this.total = this.progress;
} else if(!this.total) {
this.total = 100;
}
//if the progress is greater than the total, it's also 100%.
if(this.progress > this.total) {
this.progress = 100;
this.total = 100;
}
this.progress = (this.progress / this.total) * 100;if(this.progress < 55) {
this.color = 'red';
} else if(this.progress < 75) {
this.color= 'yellow';
} else {
this.color = 'green';
}

So what’s going on here? It’s pretty straight forward, but there are a few conditions we need to look out for.

if(!this.progress) {
this.progress = 0;
}

This is a condition that will catch the case where we don’t have progress. Angular/TypeScript will validate that we have a progress of the correct type if we’re passing in data that isn’t dynamic content (e.g. a primitive number). However, remember how inputs are optional by default? We need to validate that we have the progress input in case we forget to bind to it, or we’re getting the progress from an API that could return null/undefined.

if(this.total === 0) {
this.total = this.progress;
} else if(!this.total) {
this.total = 100;
}

This first checks if our total is 0. I decided that if the total was 0, I would make the total equal to the progress (making it 100%). You might decide you’d prefer different behavior for this condition, so it’s up to you. The else if checks if we actually have a total, otherwise I’m defaulting it to 100 as we mentioned in Step 3. Depending on your application, you might want to default this to a different value (for instance, if all the progress variables you’re passing in are out of 5).

if(this.progress > this.total) {
this.progress = 100;
this.total = 100;
}

This checks if the progress is greater than the total. In this case, I default both to 100 so it equals 100%.

this.progress = (this.progress / this.total) * 100;

This is where the actual progress calculation is done. To get the percentage one number is of another number, the formula is (A/B) * 100.

if(this.progress < 55) {
this.color = 'red';
} else if(this.progress < 75) {
this.color= 'yellow';
} else {
this.color = 'green';
}

Finally, we set our progress bar color based on the progress we’ve calculated! I chose below 75% to make it yellow and below 55% to make it red, otherwise it’s green. These are completely up to you, so have some fun with it!

Step 5) Style it!

Now for our final step, we need to add some styles. To recap, our template has the following styles:

<div class="progress-bar-container">
<div class="progress-bar {{color}}"....

In your LESS/CSS/SASS file, add the following style:

.progress-bar-container {
background-color: #eee;
}

This color is the container for the progress bar and can be whatever you’d like it to be. For the actual progress-bar, I added the following styles:

.progress-bar {    
height: 12px;
width: 0;
}
.progress-bar.green {
background-color: green;
}
.progress-bar.yellow {
background-color: yellow;
}
.progress-bar.red {
background-color: red;
}

This is a very basic style with nothing fancy, so I’d encourage you to play with it and create your own style.

That’s it! Here’s how your final component should look:

import { Component, OnInit, Input } from '@angular/core';@Component({
selector: 'app-progress-bar'
template: `
<div class="progress-bar-container">
<div class="progress-bar {{color}}"
[ngStyle]="{'width': progress + '%'}">
</div>
</div>
`,
styleUrls: ['./progress-bar.component.css']
})
export class ProgressBarComponent implements OnInit {@Input() progress: number;
@Input() total: number;
color: string;
constructor() { }ngOnInit() {
//if we don't have progress, set it to 0.
if(!this.progress) {
this.progress = 0;
}
//if we don't have a total aka no requirement, it's 100%.
if(this.total === 0) {
this.total = this.progress;
} else if(!this.total) {
this.total = 100;
}
//if the progress is greater than the total, it's also 100%.
if(this.progress > this.total) {
this.progress = 100;
this.total = 100;
}
this.progress = (this.progress / this.total) * 100; if(this.progress < 55) {
this.color = 'red';
} else if(this.progress < 75) {
this.color= 'yellow';
} else {
this.color = 'green';
}
}
}

I hope you enjoyed this tutorial! Feel free to follow for more Angular / Javascript tutorials 😃

Frontend Weekly

It's really hard to keep up with all the front-end…

Perry Martijena

Written by

Front End @ Pocket Recruiter. Love coffee, JavaScript and high performance websites.

Frontend Weekly

It's really hard to keep up with all the front-end development news out there. Let us help you. We hand-pick interesting articles related to front-end development. You can also subscribe to our weekly newsletter at http://frontendweekly.co

Perry Martijena

Written by

Front End @ Pocket Recruiter. Love coffee, JavaScript and high performance websites.

Frontend Weekly

It's really hard to keep up with all the front-end development news out there. Let us help you. We hand-pick interesting articles related to front-end development. You can also subscribe to our weekly newsletter at http://frontendweekly.co

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store