Mon, Mar 16, 2020
Read in 5 minutes
In this blog, you will learn to create step by step progress bar like below. For the CSS design , I referred to the codepen https://codepen.io/polinovskyi/pen/embZmw and developed the logic in angular.
First, you need to have the design for an arrow style steps. I get the page content from the backend JSON. So the number of steps depends on the array count which is coming from backend.
[
{
"steps": [
{
"id": "1",
"key": "PersonalBlock",
"description": "Personal Information",
"Elements": [
{
"id": "2",
"key": "name",
"description": " Name",
"type": "textbox"
},
{
"id": "3",
"key": "dob",
"description": "Date of Birth",
"type": "date"
}
]
},
{
"id": "2",
"key": "ContactBlock",
"description": "Contact Information",
"Elements": [
{
"id": "4",
"key": "email",
"description": "Email Address",
"type": "textbox"
},
{
"id": "4",
"key": "phonenumber",
"description": "PhoneNumber",
"type": "textbox"
}
]
},
{
"id": "3",
"key": "ProfessionalBlock",
"description": "Professional Information",
"Elements": [
{
"id": "5",
"key": "company",
"description": " Company",
"type": "textbox"
},
{
"id": "6",
"key": "position",
"description": "Position",
"type": "textbox"
},
{
"id": "7",
"key": "employmenttype",
"description": "Employeement Type",
"type": "dropdown",
"possibleValues": [
"FullTime",
"PartTime",
"Contract"
]
}
]
}
]
}
]
So here in the JSON steps array, three items. So the array count 3 is the number of steps in the progress bar.
Step 1 ,Step 2, Step 3 Logic
<div class="row" *ngFor="let step of stepsArr">
<div class="eachStep" *ngFor="let s of step.steps;
index as i;">
<div style="margin:20px">
<div class="step" [class.current]="i==currentStep"><span> step {{i+1}}
</span>
</div>
</div>
</div>
</div>
I am getting this “stepsArr” from the backend JSON. Each step in the progress bar is painted on the above for loop execution.
The logic for getting JSON from the backend
url = 'assets/data.json';
this.http.get<any>(this.url).subscribe(data => {
console.log(data);
this.stepsArr = data;})
Populating the content of each step: Each step, the different content should get populated. Each array content is getting populated as content(text boxes/dropdown) for each step on clicking the next button. To achieve this I use child-component. I pass each array block into a child component.
<div *ngFor="let step of stepsArr">
<div *ngFor="let s of step.steps;index as i">
<div *ngIf="i==currentStep">
<app-childcontent [step]="s">
</app-childcontent>
</div>
</div>
<div class="buttonGroup">
<button (click)="back(i)" class="btn-primary alignButton">
Previous</button>
<button (click)="next(i)" class="btn-primary alignButton ">
Next</button>
</div>
</div>
Child Component:(childcontent.component.html)
<div *ngFor="let element of step.Elements">
<div *ngIf="element.type=='textbox'" class="form-group">
<label>{{element.description}}</label>
<input type={{element.type}} name={{element.key}} class="form-control" id={{element.key}}
ngModel>
</div>
<div *ngIf="element.type=='date'" class="form-group">
<label>{{element.description}}</label>
<input type={{element.type}} name={{element.key}} class="form-control" id={{element.key}}
ngModel>
</div>
<div *ngIf="element.type=='dropdown'" class="form-group">
<label>{{element.description}}</label>
<select name={{element.key}} class="form-control" id={{element.key}} ngModel>
<option *ngFor="let value of element.possibleValues">
{{value}}
</option>
</select>
</div>
</div>
ChildcontentComponent.ts
import { Component, OnInit, Input } from '@angular/core';
import { Step } from 'src/model/step';
import { Elements } from 'src/model/elements';
@Component({
selector: 'app-childcontent',
templateUrl: './childcontent.component.html',
styleUrls: ['./childcontent.component.css']
})
export class ChildcontentComponent implements OnInit {
@Input() step:Step;
@Input() element:Elements;
constructor() { }
ngOnInit() {
}
}
CSS Design(app.component.css)
/* Global CSS, you probably don't need that */
.clearfix:after {
clear: both;
content: "";
display: block;
height: 0;
}
.container {
font-family: 'Lato', sans-serif;
width: 1000px;
margin: 0 auto;
}
.wrapper {
display: table-cell;
height: 400px;
vertical-align: middle;
}
.nav {
margin-top: 40px;
}
.pull-right {
float: right;
}
a, a:active {
color: #333;
text-decoration: none;
}
a:hover {
color: #999;
}
/* Breadcrups CSS */
.arrow-steps .step {
font-size: 14px;
text-align: center;
color: #666;
cursor: default;
margin: 0 3px;
padding: 10px 10px 10px 30px;
min-width: 180px;
float: left;
position: relative;
background-color: #d9e3f7;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
transition: background-color 0.2s ease;
}
.arrow-steps .step:after,
.arrow-steps .step:before {
content: " ";
position: absolute;
top: 0;
right: -17px;
width: 0;
height: 0;
border-top: 27px solid transparent;
border-bottom: 15px solid transparent;
border-left: 17px solid #d9e3f7;
z-index: 2;
transition: border-color 0.2s ease;
}
.arrow-steps .step:before {
right: auto;
left: 0;
border-left: 17px solid #fff;
z-index: 0;
}
.arrow-steps .step:first-child:before {
border: none;
}
.arrow-steps .step:first-child {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.arrow-steps .step span {
position: relative;
}
.arrow-steps .step span:before {
opacity: 0;
content: "✔";
position: absolute;
top: -2px;
left: -20px;
}
.arrow-steps .step.done span:before {
opacity: 1;
-webkit-transition: opacity 0.3s ease 0.5s;
-moz-transition: opacity 0.3s ease 0.5s;
-ms-transition: opacity 0.3s ease 0.5s;
transition: opacity 0.3s ease 0.5s;
}
.arrow-steps .step.current {
color: #fff;
background-color: #23468c;
}
.arrow-steps .step.current:after {
border-left: 17px solid #23468c;
}
.eachStep{
margin-right:20px;
}
.alignButton{
margin: 20px;
}
.currentDisplay{
background-color: #007bff;
}
app.component.html
<div class="container">
<div class="wrapper">
<div class="arrow-steps clearfix">
<div>
<div class="row " *ngFor="let step of stepsArr">
<div class="eachStep" *ngFor="let s of step.steps;index as i;">
<div style="margin:20px">
<div class="step" [class.current]="i==currentStep">
<span> step {{i+1}}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngFor="let step of stepsArr">
<div *ngFor="let s of step.steps;index as i">
<div *ngIf="i==currentStep">
<app-childcontent [step]="s">
</app-childcontent>
</div>
</div>
<div class="buttonGroup">
<button (click)="back(i)" class="btn-primary alignButton">
Previous</button>
<button (click)="next(i)" class="btn-primary alignButton ">
Next</button>
</div>
</div>
</div>
</div>
app.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Step } from 'src/model/step';
import { Elements } from 'src/model/elements';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'demoTWO';
url = 'assets/data.json';
//url="https://reqres.in/api/users";
private stepsArr:object[];
step:Step;
element:Elements;
currentStep=0;
constructor(private http:HttpClient){
}
ngOnInit(){
this.http.get<any>(this.url).subscribe(data => {
console.log(data);
this.stepsArr = data;
//console.log(JSON.stringify(this.blockArr));
})
}
next(i){
this.currentStep=this.currentStep+1;
//alert(this.currentStep);
if(this.currentStep>2){
}
}
back(i){
this.currentStep=this.currentStep-1;
//alert(this.currentStep);
}
}
So far, we have seen how to create a progress bar in angular. Angular material also has a stepper component https://material.angular.io/components/stepper/overview. But the stepper component icon is round shape and here we discussed the arrow shape progress bar. I could not create an arrow progress bar using the material stepper component.