---
title: Percentage Progress Bar
product: Form Library
description: Learn how to add a progress bar with percentage to your web form to keep track of unanswered questions. View a free demo for JavaScript to see this useful feature in action. Enhance the user experience and increase form completion rates.
framework: Angular
source: https://surveyjs.io/form-library/examples/progress-bar-with-percentage/angular
index: https://surveyjs.io/form-library/examples/overview.md
---

# Percentage Progress Bar (Angular)

To help respondents keep track of answered and unanswered questions, your survey can display a progress bar that indicates a percentage of survey completion. Follow the instructions below to implement the percentage progress bar in your application:

1. Create a custom component that renders the progress bar.         
You can implement the progress bar based on a simple `<div>` element. Fill this `<div>` with color depending on `SurveyModel`'s [`progressValue`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#progressValue) property value.

1. Display a percentage value and a title.       
A percentage value is already stored in the `progressValue` property. To store the title, you need to create a custom property&mdash;`progressTitle`. Call `Serializer`'s `addProperty` method to add the property to the survey. Then, specify the property value in the survey JSON schema.

1. Register the custom component so that it can be accessed by name.            
    In HTML/CSS/JavaScript projects, register the component in `ReactElementFactory` as shown in the `index.js` file.           
    In React, register the component in `ReactElementFactory` as shown in the `SurveyComponent.jsx` file.          
    In Angular, register the component in `AngularComponentFactory` as shown in the `progressbar-percentage.component.ts` file.          
    In Vue.js, use [techniques native to this framework](https://vuejs.org/guide/components/registration).

2. Add the progress bar to the survey layout.           
Call the [`addLayoutElement`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#addLayoutElement) method. It accepts an object whose properties specify the element's `id`, a `container` that determines the element's location, a `component` that renders the element, and `data` to pass as component props. Refer to the method description for more information on these properties.

## Files

### `src/app/components/json.ts`

```ts
export const json = {
  "title": "Customer Satisfaction Survey",
  "showTOC": true,
  "progressTitle": "Survey Progress",
  "progressBarType": "questions",
  "pages": [
    {
      "navigationTitle": "Satisfaction",
      "elements": [
        {
          "type": "matrix",
          "name": "Quality",
          "title": "Please indicate if you agree or disagree with the following statements",
          "columns": [
            {
              "value": 1,
              "text": "Strongly Disagree"
            },
            {
              "value": 2,
              "text": "Disagree"
            },
            {
              "value": 3,
              "text": "Neutral"
            },
            {
              "value": 4,
              "text": "Agree"
            },
            {
              "value": 5,
              "text": "Strongly Agree"
            }
          ],
          "rows": [
            {
              "value": "affordable",
              "text": "Product is affordable"
            },
            {
              "value": "does what it claims",
              "text": "Product does what it claims"
            },
            {
              "value": "better then others",
              "text": "Product is better than other products on the market"
            },
            {
              "value": "easy to use",
              "text": "Product is easy to use"
            }
          ]
        },
        {
          "type": "rating",
          "name": "satisfaction",
          "title": "How satisfied are you with our product?",
          "minRateDescription": "Not Satisfied",
          "maxRateDescription": "Completely satisfied"
        },
        {
          "type": "rating",
          "name": "recommend friends",
          "visibleIf": "{satisfaction} > 3",
          "title": "How likely are you to recommend our product to a friend or co-worker?",
          "minRateDescription": "Will not recommend",
          "maxRateDescription": "I will recommend"
        },
        {
          "type": "comment",
          "name": "suggestions",
          "title": "What would make you more satisfied with our product?",
          "maxLength": 500
        }
      ]
    },
    {
      "navigationTitle": "Pricing",
      "elements": [
        {
          "type": "radiogroup",
          "name": "price to competitors",
          "title": "Compared to our competitors, do you feel our product is",
          "choices": [ "Less expensive", "Priced about the same", "More expensive", "Not sure" ]
        },
        {
          "type": "radiogroup",
          "name": "price",
          "title": "Do you feel our current price is merited by our product?",
          "choices": [ "correct|Yes, the price is about right", "low|No, the price is too low for your product", "high|No, the price is too high for your product" ]
        },
        {
          "type": "multipletext",
          "name": "pricelimit",
          "title": "What is the... ",
          "items": [
            {
              "name": "mostamount",
              "title": "Most amount you would every pay for a product like ours"
            },
            {
              "name": "leastamount",
              "title": "The least amount you would feel comfortable paying"
            }
          ]
        }
      ]
    },
    {
      "navigationTitle": "Contacts",
      "elements": [
        {
          "type": "text",
          "name": "email",
          "title": "Thank you for taking our survey. Your survey is almost complete, please enter your email address in the box below if you wish to participate in our drawing, then press the 'Submit' button."
        }
      ]
    }
  ]
};
```

### `src/app/components/survey.component.css`

```css
.sv-progressbar-percentage {
    display: flex;
    flex-direction: row;
    gap: 16px;
    line-height: 32px;
    padding: 24px;
    border-radius: 6px;
    box-shadow: var(--sjs2-border-effect-surface-default, 0px 1px 2px 0px rgba(0, 0, 0, 0.15));
    background: var(--sjs2-color-component-panel-default-bg, #fff);
    margin-bottom: 24px;
    justify-content: center;
    align-items: center;
    color: var(--sjs2-color-component-question-default-title, #161616);
}
.sd-root--compact .sv-progressbar-percentage {
    box-shadow: none;
    border: 1px solid var(--sjs2-color-component-input-default-line, #d6d6d6);
}
.sv-progressbar-percentage__title {
    font-size: 20px;
    display: flex;
}

.sv-progressbar-percentage__indicator {
    position: relative;
    display: flex;
    width: 100%;
    max-width: 50%;
    height: 32px;
    box-shadow: var(--sjs2-border-effect-surface-default, 1px 1px 1px 1px rgba(0, 0, 0, 0.15));
    border-radius: 9999px;
    background: var(--sjs2-color-bg-basic-secondary-dim, rgba(243, 243, 243, 1));
    height: 16px;
}

.sv-progressbar-percentage__value-bar {
    position: absolute;
    left: 0;
    top: 0;
    border-radius: 9999px;
    border: 1px solid var(--sjs2-color-project-brand-600, #19b394);
    background: var(--sjs2-color-project-brand-600, #19b394);
    height: 16px;
}

.sv-progressbar-percentage__value {
    font-size: 20px;
    display: flex;    
}

```

### `src/app/components/survey.component.html`

```html
<survey [model]="model" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; height:100%"></survey>
```

### `src/app/components/progressbar-percentage.component.css`

```css
.sv-progressbar-percentage {
    display: flex;
    flex-direction: row;
    gap: 16px;
    line-height: 32px;
    padding: 24px;
    border-radius: 6px;
    box-shadow: var(--sjs2-border-effect-surface-default, 0px 1px 2px 0px rgba(0, 0, 0, 0.15));
    background: var(--sjs2-color-component-panel-default-bg, #fff);
    margin-bottom: 24px;
    justify-content: center;
    align-items: center;
    color: var(--sjs2-color-component-question-default-title, #161616);
}
.sd-root--compact .sv-progressbar-percentage {
    box-shadow: none;
    border: 1px solid var(--sjs2-color-component-input-default-line, #d6d6d6);
}
.sv-progressbar-percentage__title {
    font-size: 20px;
    display: flex;
}

.sv-progressbar-percentage__indicator {
    position: relative;
    display: flex;
    width: 100%;
    max-width: 50%;
    height: 32px;
    box-shadow: var(--sjs2-border-effect-surface-default, 1px 1px 1px 1px rgba(0, 0, 0, 0.15));
    border-radius: 9999px;
    background: var(--sjs2-color-bg-basic-secondary-dim, rgba(243, 243, 243, 1));
    height: 16px;
}

.sv-progressbar-percentage__value-bar {
    position: absolute;
    left: 0;
    top: 0;
    border-radius: 9999px;
    border: 1px solid var(--sjs2-color-project-brand-600, #19b394);
    background: var(--sjs2-color-project-brand-600, #19b394);
    height: 16px;
}

.sv-progressbar-percentage__value {
    font-size: 20px;
    display: flex;    
}
```

### `src/app/components/progressbar-percentage.component.html`

```html
<div class="sv-progressbar-percentage">
    <div class="sv-progressbar-percentage__title">
        <span>{{title}}</span>
    </div>
    <div class="sv-progressbar-percentage__indicator">
        <div class="sv-progressbar-percentage__value-bar" [style.width]="value"></div>
    </div>
    <div class="sv-progressbar-percentage__value">
        <span>{{value}}</span>
    </div>
</div>
```

### `src/app/components/progressbar-percentage.component.ts`

```ts
import { Component, Input } from "@angular/core";
import { AngularComponentFactory } from "survey-angular-ui";
import { SurveyModel, Serializer } from "survey-core";

Serializer.addProperty("survey", "progressTitle");

@Component({
    // tslint:disable-next-line:component-selector
    selector: "sv-ng-progress-buttons",
    templateUrl: "./progressbar-percentage.component.html",
    styleUrls: ["./progressbar-percentage.component.css"]
})
export class PercentageProgressBarComponent {
    @Input() model!: SurveyModel;
    get title() {
        return this.model.progressTitle;
    }
    get value() {
        return this.model.progressValue + "%";
    }
}

AngularComponentFactory.Instance.registerComponent(
    "sv-progressbar-percentage",
    PercentageProgressBarComponent
);
```

### `src/app/components/survey.component.ts`

```ts
import { Component, OnInit } from "@angular/core";
import { Model } from "survey-core";
import { json } from "./json";
import "./survey.component.css";
import "survey-core/survey-core.min.css";
import { PercentageProgressBarComponent } from "./progressbar-percentage.component";


@Component({
    // tslint:disable-next-line:component-selector
    selector: "component-survey",
    templateUrl: "./survey.component.html",
    styleUrls: ["./survey.component.css"]
})
export class SurveyComponent implements OnInit {
    static declaration = [PercentageProgressBarComponent];
    model: Model;
    ngOnInit() {
        const survey = new Model(json);
        survey.addLayoutElement({
            id: "progressbar-percentage",
            component: "sv-progressbar-percentage",
            container: "contentTop",
            data: survey
        });
        
        survey.onComplete.add((sender, options) => {
            console.log(JSON.stringify(sender.data, null, 3));
        });
        this.model = survey;
    }
}
```

### `src/app/components/theme.ts`

```ts
export const themeJson = {};
```

### `src/app/app.component.css`

```css

```

### `src/app/app.component.html`

```html
<component-survey></component-survey>
```

### `src/app/app.component.ts`

```ts
import { Component } from "@angular/core";

@Component({
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.css"]
})
export class AppComponent {
    title = "CodeSandbox";
}
```

### `src/app/app.module.ts`

```ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";

import { AppComponent } from "./app.component";
import { SurveyModule } from "survey-angular-ui";
import { SurveyComponent } from "./components/survey.component";
import { PercentageProgressBarComponent } from "./components/progressbar-percentage.component";


@NgModule({
    declarations: [AppComponent, SurveyComponent, PercentageProgressBarComponent],
    imports: [BrowserModule, SurveyModule],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }
```

### `src/environments/environment.prod.ts`

```ts
export const environment = {
    production: true
};
```

### `src/environments/environment.ts`

```ts
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.

export const environment = {
    production: false
};
```

### `src/index.html`

```html
<app-root></app-root>
```

### `src/main.ts`

```ts
import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch(err => console.log(err));
```

### `src/polyfills.ts`

```ts
/**
 * This file includes polyfills needed by Angular and is loaded before the app.
 * You can add your own extra polyfills to this file.
 *
 * This file is divided into 2 sections:
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 *      file.
 *
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 *
 * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
 */

/***************************************************************************************************
 * BROWSER POLYFILLS
 */

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js';  // Run `npm install classlist.js`.

/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';

/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import "core-js/proposals/reflect-metadata";

/**
 * Required to support Web Animations `@angular/platform-browser/animations`.
 * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
 **/
// import 'web-animations-js';  // Run `npm install web-animations-js`.

/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import "zone.js/dist/zone"; // Included with Angular CLI.

/***************************************************************************************************
 * APPLICATION IMPORTS
 */
```

### `src/styles.css`

```css
.sv-progressbar-percentage {
    display: flex;
    flex-direction: row;
    gap: 16px;
    line-height: 32px;
    padding: 24px;
    border-radius: 6px;
    box-shadow: var(--sjs2-border-effect-surface-default, 0px 1px 2px 0px rgba(0, 0, 0, 0.15));
    background: var(--sjs2-color-component-panel-default-bg, #fff);
    margin-bottom: 24px;
    justify-content: center;
    align-items: center;
    color: var(--sjs2-color-component-question-default-title, #161616);
}
.sd-root--compact .sv-progressbar-percentage {
    box-shadow: none;
    border: 1px solid var(--sjs2-color-component-input-default-line, #d6d6d6);
}
.sv-progressbar-percentage__title {
    font-size: 20px;
    display: flex;
}

.sv-progressbar-percentage__indicator {
    position: relative;
    display: flex;
    width: 100%;
    max-width: 50%;
    height: 32px;
    box-shadow: var(--sjs2-border-effect-surface-default, 1px 1px 1px 1px rgba(0, 0, 0, 0.15));
    border-radius: 9999px;
    background: var(--sjs2-color-bg-basic-secondary-dim, rgba(243, 243, 243, 1));
    height: 16px;
}

.sv-progressbar-percentage__value-bar {
    position: absolute;
    left: 0;
    top: 0;
    border-radius: 9999px;
    border: 1px solid var(--sjs2-color-project-brand-600, #19b394);
    background: var(--sjs2-color-project-brand-600, #19b394);
    height: 16px;
}

.sv-progressbar-percentage__value {
    font-size: 20px;
    display: flex;    
}

```

### `src/typings.d.ts`

```ts
/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
    id: string;
}
```

### `.angular-cli.json`

```json
{
  "apps": [
    {
      "root": "src",
      "outDir": "dist",
      "assets": [ "assets", "favicon.ico" ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "prefix": "app",
      "styles": [ "styles.css" ],
      "scripts": [ ],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ]
}
```

### `_tsconfig.json`

```json
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "resolveJsonModule": true,
    "target": "es2015",
    "module": "es2020",
    "lib": [
      "es2018",
      "dom"
    ]
  }
}
```

### `package.json`

```json
{
  "name": "surveyjs-angular",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build --prod",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "14.1.1",
    "@angular/cdk": "14.1.1",
    "@angular/common": "14.1.1",
    "@angular/compiler": "14.1.1",
    "@angular/core": "14.1.1",
    "@angular/forms": "14.1.1",
    "@angular/platform-browser": "14.1.1",
    "@angular/platform-browser-dynamic": "14.1.1",
    "@angular/router": "14.1.1",
    "core-js": "3.6.4",
    "rxjs": "6.5.4",
    "survey-core": "latest",
    "survey-angular-ui": "latest",
    "tslib": "1.13.0",
    "zone.js": "0.11.7"
  },
  "devDependencies": {
    "@angular/cli": "1.6.6",
    "@angular/compiler-cli": "^5.2.0",
    "@angular/language-service": "^5.2.0",
    "@types/core-js": "0.9.46",
    "@types/jasmine": "~2.8.3",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~4.1.0",
    "tslint": "~5.9.1",
    "typescript": "3.9.7"
  },
  "keywords": [ "angular", "surveyjs" ],
  "description": "SurveyJS-Angular example project"
}
```

## Other Frameworks

- [React](https://surveyjs.io/form-library/examples/progress-bar-with-percentage/reactjs.md)
- [Vue 3](https://surveyjs.io/form-library/examples/progress-bar-with-percentage/vue3js.md)
- [jQuery](https://surveyjs.io/form-library/examples/progress-bar-with-percentage/jquery.md)
- [Vanilla JS](https://surveyjs.io/form-library/examples/progress-bar-with-percentage/vanillajs.md)
