---
title: Aggregate Data within a Form
product: Form Library
description: The Dynamic Matrix question enables you to perform various calculations on user input and consolidate this data into separate rows and/or columns based on the selected data aggregation type. For instance, if you want to calculate the total cost of items within a form, you'll need to set the aggregation type to 'Sum'. View this demo to learn more and access ready-to-use code examples for JavaScript.
framework: Angular
source: https://surveyjs.io/form-library/examples/aggregate-data-within-form/angular
index: https://surveyjs.io/form-library/examples/overview.md
---

# Aggregate Data within a Form (Angular)

Dynamic Matrix supports different data aggregation methods that you can use to combine or summarize data across multiple matrix rows or columns based on provided answers. For instance, in a Dynamic Matrix where users input numerical values, aggregation might involve calculating totals, averages, counts, or other statistical operations across rows or columns. This example illustrates the dynamic generation of Totals for rows and columns based on selected phone models and their quantities.

## Calculate Row Totals

A row total aggregates values in a particular matrix row. To calculate and display row totals, create a column with the `cellType` property set to `"expression"`. Create an expression to calculate row totals. Within the expression, you can access individual row cells using the `row` prefix as follows: `{row.columnName}`. For a code example, refer to the "Total" column configuration in the `survey.json` file.

## Calculate Column Totals

A column total aggregates values in an individual matrix column. Use the following column properties to configure column totals:

- `totalType`: `"sum"` | `"count"` | `"min"` | `"max"` | `"avg"`        
An aggregation method.

- `totalDisplayStyle`: `"decimal"` | `"currency"` | `"percent"`         
A format for calculated total values.

- `totalFormat`: `string`        
A string pattern used to display column totals. To reference a total value within this pattern, use the `{0}` placeholder.

This demo calculates column totals for the "Phone model", "Quantity", and "Total" columns. You can find their configurations in the `survey.json` file.

To access the total value of an individual column, use the `total` suffix as follows: `{matrixName-columnName.total}`. In the current demo, this suffix is used by the "VAT" and "Total" questions. Refer to the `survey.json` file for a code example.

## Files

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

```ts
export const json = {
  "elements": [
    {
      "type": "matrixdynamic",
      "name": "orders",
      "minRowCount": 1,
      "titleLocation": "hidden",
      "addRowText": "Add new item",
      "columns": [
        {
          "name": "id",
          "title": "Id",
          "cellType": "expression",
          "expression": "{rowIndex}",
          "visible": false
        },
        {
          "name": "phone_model",
          "title": "Phone model",
          "isRequired": true,
          "cellType":  "dropdown",
          "choices": [
            { "text": "iPhone 15 Pro Max, 256GB", "value": "iPhone15ProMax-256", "price": 1199 },
            { "text": "iPhone 15 Pro Max, 512GB", "value": "iPhone15ProMax-512", "price": 1399 },
            { "text": "iPhone 15 Pro Max, 1TB", "value": "iPhone15ProMax-1024", "price": 1599 },
            { "text": "iPhone 15 Pro, 128GB", "value": "iPhone15Pro-128", "price": 999 },
            { "text": "iPhone 15 Pro, 256GB", "value": "iPhone15Pro-256", "price": 1099 },
            { "text": "iPhone 15 Pro, 512GB", "value": "iPhone15Pro-512", "price": 1299 },
            { "text": "iPhone 15 Pro, 1TB", "value": "iPhone15Pro-1024", "price": 1499 },
            { "text": "iPhone 15 Plus, 128GB", "value": "iPhone15Plus-128", "price": 899 },
            { "text": "iPhone 15 Plus, 256GB", "value": "iPhone15Plus-256", "price": 999 },
            { "text": "iPhone 15 Plus, 512GB", "value": "iPhone15Plus-512", "price": 1199 },
            { "text": "iPhone 15, 128GB", "value": "iPhone15-128", "price": 799 },
            { "text": "iPhone 15, 256GB", "value": "iPhone15-256", "price": 899 },
            { "text": "iPhone 15, 512GB", "value": "iPhone15-512", "price": 1099 },
            { "text": "iPhone 14 Pro Max, 256GB", "value": "iPhone14ProMax-256", "price": 1099 },
            { "text": "iPhone 14 Pro Max, 512GB", "value": "iPhone14ProMax-512", "price": 1299 },
            { "text": "iPhone 14 Pro Max, 1TB", "value": "iPhone14ProMax-1024", "price": 1499 },
            { "text": "iPhone 14 Pro, 128GB", "value": "iPhone14Pro-128", "price": 899 },
            { "text": "iPhone 14 Pro, 256GB", "value": "iPhone14Pro-256", "price": 999 },
            { "text": "iPhone 14 Pro, 512GB", "value": "iPhone14Pro-512", "price": 1199 },
            { "text": "iPhone 14 Pro, 1TB", "value": "iPhone14Pro-1024", "price": 1399 },
            { "text": "iPhone 14 Plus, 128GB", "value": "iPhone14Plus-128", "price": 799 },
            { "text": "iPhone 14 Plus, 256GB", "value": "iPhone14Plus-256", "price": 899 },
            { "text": "iPhone 14 Plus, 512GB", "value": "iPhone14Plus-512", "price": 1099 },
            { "text": "iPhone 14, 128GB", "value": "iPhone14-128", "price": 699 },
            { "text": "iPhone 14, 256GB", "value": "iPhone14-256", "price": 799 },
            { "text": "iPhone 14, 512GB", "value": "iPhone14-512", "price": 999 }
          ],
          "totalType": "count",
          "totalFormat": "Total: {0}"
        },
        {
          "name": "price",
          "title": "Price",
          "cellType": "expression",
          "expression": "getItemPrice('phone_model')",
          "displayStyle": "currency"
        },
        {
          "name": "quantity",
          "title": "Quantity",
          "isRequired": true,
          "cellType": "text",
          "inputType": "number",
          "min": 1,
          "max": 100,
          "defaultValue": 1,
          "textUpdateMode": "onTyping",
          "enableIf": "{row.phone_model} notempty",
          "totalType": "sum",
          "totalFormat": "Total: {0}"
        },
        {
          "name": "total",
          "title": "Total",
          "cellType": "expression",
          "expression": "{row.quantity} * {row.price}",
          "displayStyle": "currency",
          "totalType": "sum",
          "totalDisplayStyle": "currency",
          "totalFormat": "Total: {0}"
        }
      ]
    },
    {
      "name": "vatPercentage",
      "type": "text",
      "title": "VAT (in %)",
      "inputType": "number",
      "min": 0,
      "max": 40,
      "defaultValue": 20,
      "textUpdateMode": "onTyping"
    },
    {
      "name": "vatTotal",
      "type": "expression",
      "title": "VAT",
      "expression": "{orders-total.total} * {vatPercentage} / 100",
      "displayStyle": "currency",
      "startWithNewLine": false
    },
    {
      "name": "total",
      "type": "expression",
      "title": "Total",
      "expression": "{orders-total.total} + {vatTotal}",
      "displayStyle": "currency",
      "startWithNewLine": false
    }
  ]
};
```

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

```css
/* You can add your custom CSS here. */
```

### `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/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 { Serializer, registerFunction } from "survey-core";

// Add the `price` property to choice options
Serializer.addProperty("itemvalue", "price:number");

// A custom function that returns a selected item's price
const getItemPrice = function (params) {
    // `this.row` is available only within matrix cells
    const question = this.row ? this.row.getQuestionByColumnName(params[0]) : null;
    if (!question) return 0;
    const selectedItem = question.selectedItem;
    // Return 0 if a user haven't selected an item yet
    return selectedItem ? selectedItem.price : 0;
};
registerFunction({ name: "getItemPrice", func: getItemPrice });

@Component({
    // tslint:disable-next-line:component-selector
    selector: "component-survey",
    templateUrl: "./survey.component.html",
    styleUrls: ["./survey.component.css"]
})
export class SurveyComponent implements OnInit {
    model: Model;
    ngOnInit() {
        const survey = new Model(json);
        survey.data = {
            "vatPercentage": 20,
            "orders": [{
                "id": 1,
                "phone_model": "iPhone15ProMax-1024",
                "quantity": 1,
            }, {
                "id": 2,
                "phone_model": "iPhone15-256",
                "quantity": 2,
            }, {
                "id": 3,
                "phone_model": "iPhone14Pro-512",
                "quantity": 2,
            }]
        };
        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";

@NgModule({
    declarations: [AppComponent, SurveyComponent],
    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
/* You can add your custom CSS here. */
```

### `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/aggregate-data-within-form/reactjs.md)
- [Vue 3](https://surveyjs.io/form-library/examples/aggregate-data-within-form/vue3js.md)
- [jQuery](https://surveyjs.io/form-library/examples/aggregate-data-within-form/jquery.md)
- [Vanilla JS](https://surveyjs.io/form-library/examples/aggregate-data-within-form/vanillajs.md)
