Angular Pivot Table: Live data update

Update the data in the Angular pivot grid component in real-time and catch all the insights in an instant.


    import { Component, ViewChild } from "@angular/core";
    import { FlexmonsterPivotModule, FlexmonsterPivot } from "ngx-flexmonster";
    
    @Component({
      selector: "pivot-table-demo",
      standalone: true,
      imports: [FlexmonsterPivotModule],
      templateUrl: "./pivot-table-demo.component.html",
      styleUrls: ["./pivot-table-demo.component.css"]
    })
    export class PivotTableDemo {
      @ViewChild("pivot") pivotRef!: FlexmonsterPivot;
    
      public report: Object = {
        dataSource: {
          type: "json",
          data: [
            {
              "ID": "ISK",
              "Currency": "ISK",
              "Bid Rate": 144.904417,
              "Ask Rate": 144.904417
            }
          ],
          mapping: {
            "ID": {
              type: "id"
            },
            "Currency": {
              type: "string"
            },
            "Bid Rate": {
              type: "number"
            },
            "Ask Rate": {
              type: "number"
            }
          }
        },
        slice: {
          rows: [
            {
              uniqueName: "Currency",
              sort: "asc"
            }
          ],
          columns: [
            {
              uniqueName: "[Measures]"
            }
          ],
          measures: [
            {
              uniqueName: "Bid Rate",
              aggregation: "sum",
              format: "rate_format"
            },
            {
              uniqueName: "Ask Rate",
              aggregation: "sum",
              format: "rate_format"
            },
            {
              uniqueName: "Spread",
              formula: "(sum('Ask Rate') - sum('Bid Rate')",
              caption: "Spread",
              format: "spread_format"
            }
          ],
          flatOrder: ["Bid Rate", "Currency", "Ask Rate", "Spread"]
        },
        options: {
          grid: {
            type: "flat",
            showFilter: false,
            showHeaders: false,
            showTotals: "off",
            showGrandTotals: "off"
          },
          configuratorButton: false,
          drillThrough: false
        },
        conditions: [
          {
            formula: "if(#updateDifference < 0, 'fm-updated-cell-down', 'fm-updated-cell-up')"
          }
        ],
        formats: [
          {
            name: "rate_format",
            decimalPlaces: 4,
            textAlign: "left",
            thousandsSeparator: ",",
            decimalSeparator: "."
          },
          {
            name: "spread_format",
            decimalPlaces: 2,
            textAlign: "right"
          }
        ],
        tableSizes: {
          columns: [
            {
              tuple: ["Currency"],
              width: 200
            },
            {
              tuple: ["Bid Rate"],
              width: 200
            },
            {
              tuple: ["Ask Rate"],
              width: 200
            },
            {
              tuple: ["Spread"],
              width: 200
            }
          ]
        }
      };
    
      currency_data = {
        base: "EUR",
        date: "2026-02-19",
        rates: {
          "AED": 4.315695,
          "AFN": 73.196281,
          "ALL": 96.41278,
          "AMD": 442.699239,
          "AOA": 1075.5504,
          "ARS": 1637.8094,
          "AUD": 1.668851,
          "AWG": 2.103194,
          "AZN": 1.997389,
          "BAM": 1.95583,
          "BBD": 2.349943,
          "BCH": 0.00217389,
          "BDT": 143.624622,
          "BHD": 0.441754,
          "BIF": 3484.9372,
          "BMD": 1.174735,
          "BND": 1.491319,
          "BOB": 8.123182,
          "BRL": 6.139918,
          "BSD": 1.174774,
          "BTC": 0.00001785,
          "BTN": 107.071755,
          "BWP": 15.569885,
          "BZD": 2.366204,
          "CAD": 1.610968,
          "CDF": 2714.6536,
          "CHF": 0.911323,
          "CLP": 1019.4707,
          "CNY": 8.117184,
          "COP": 4341.444,
          "CRC": 564.998862,
          "CUC": 1.174898,
          "CUP": 28.206249,
          "CVE": 110.27,
          "CZK": 24.263945,
          "DJF": 209.051711,
          "DKK": 7.471706,
          "DOP": 72.326258,
          "DZD": 152.8183896,
          "EGP": 55.918178,
          "ERN": 17.650672,
          "ETB": 181.052726,
          "ETH": 0.00061124,
          "EUR": 1,
          "FJD": 2.588981,
          "FKP": 0.873974,
          "GBP": 0.873974,
          "GEL": 3.148256,
          "GGP": 0.873974,
          "GHS": 12.934981,
          "GIP": 0.873974,
          "GMD": 87.113153,
          "GNF": 10329.61,
          "GTQ": 9.028754,
          "GYD": 246.2709298,
          "HKD": 9.194832,
          "HNL": 31.132507,
          "HTG": 154.376922,
          "HUF": 378.7144395,
          "IDR": 19886.25,
          "ILS": 3.684162,
          "IMP": 0.873974,
          "INR": 107.139324,
          "IQD": 1542.0246,
          "IRR": 1513627.74,
          "ISK": 144.904417,
          "JEP": 0.873974,
          "JMD": 183.420735,
          "JOD": 0.834364,
          "JPY": 182.478885,
          "KES": 151.918593,
          "KGS": 102.952542,
          "KHR": 4729.8484,
          "KMF": 491.96775,
          "KPW": 1059.6772,
          "KRW": 1704.4228,
          "KWD": 0.361109,
          "KYD": 0.977261,
          "KZT": 580.061951,
          "LAK": 25229.85,
          "LBP": 105696.5,
          "LKR": 364.287964,
          "LRD": 218.4740298,
          "LSL": 19.0093097,
          "LTL": 3.4528,
          "LVL": 0.7028,
          "LYD": 7.450847,
          "MAD": 10.79119,
          "MDL": 20.155611,
          "MGA": 5122.2147,
          "MKD": 61.600169,
          "MMK": 2470.7194,
          "MNT": 4201.1779,
          "MOP": 9.4714,
          "MRU": 46.910393,
          "MUR": 54.285057,
          "MVR": 18.1624696,
          "MWK": 2041.4086,
          "MXN": 20.296938,
          "MYR": 4.598598,
          "MZN": 75.125987,
          "NAD": 19.010569,
          "NGN": 1579.6115,
          "NIO": 43.297921,
          "NOK": 11.257276,
          "NPR": 171.453562,
          "NZD": 1.969253,
          "OMR": 0.45244,
          "PAB": 1.176271,
          "PEN": 3.951717,
          "PGK": 5.055734,
          "PHP": 68.268117,
          "PKR": 328.967861,
          "PLN": 4.223936,
          "PYG": 7637.6817,
          "QAR": 4.281802,
          "RON": 5.097529,
          "RSD": 117.421545,
          "RWF": 1718.5892,
          "SAR": 4.411401,
          "SBD": 9.462084,
          "SCR": 17.047283,
          "SDG": 706.077194,
          "SEK": 10.667844,
          "SGD": 1.492383,
          "SHP": 0.873974,
          "SLE": 27.002706,
          "SLL": 27002.7,
          "SOS": 670.180148,
          "SPL": 0.196101,
          "SRD": 44.258017,
          "STN": 24.634009,
          "SVC": 10.294256,
          "SYP": 130.079984,
          "SZL": 19.034846,
          "THB": 36.66563,
          "TJS": 11.097921,
          "TMT": 4.115697,
          "TND": 3.381045,
          "TOP": 2.830983,
          "TRY": 51.488771,
          "TTD": 7.963055,
          "TVD": 1.667513,
          "TWD": 37.173483,
          "TZS": 3045.3736,
          "UAH": 50.8901,
          "UGX": 4219.8786,
          "USD": 1.176459,
          "UYU": 45.654212,
          "UZS": 14280.82,
          "VES": 466.598574,
          "VND": 30522.47,
          "VUV": 140.00752,
          "WST": 3.178346,
          "XAF": 655.957,
          "XAG": 0.015023,
          "XAU": 0.00023519,
          "XCD": 3.184203,
          "XCG": 2.120881,
          "XDR": 0.8541,
          "XOF": 655.957,
          "XPF": 119.331742,
          "YER": 280.462391,
          "ZAR": 19.038241,
          "ZMK": 22332.84,
          "ZMW": 22.33284,
          "ZWG": 30.124114
        }
      };
      valids: string[] = [
        "EUR",
        "JPY",
        "CZK",
        "DKK",
        "GBP",
        "HUF",
        "UAH",
        "LTL",
        "LVL",
        "PLN",
        "RON",
        "SEK",
        "CHF",
        "NOK",
        "HRK",
        "TRY",
        "AUD",
        "BRL",
        "CAD",
        "CNY",
        "HKD",
        "IDR",
        "ILS",
        "INR",
        "KRW",
        "MXN",
        "MYR",
        "NZD",
        "PHP",
        "SGD",
        "THB",
        "ZAR",
        "ISK"
      ];
      dataset: object[] = [];
    
      getData() {
        const rates = this.currency_data.rates;
    
        Object.entries(rates).forEach(([key, value]) => {
          let record: any = {};
          record["ID"] = key;
          record["Currency"] = key;
          record["Bid Rate"] = value;
          record["Ask Rate"] = value;
          if (this.valids.indexOf(record["Currency"]) > 0) {
            this.dataset.push(record);
          }
        });
    
        if (this.dataset.length > 0) {
          this.pivotRef.flexmonster.updateData(
            {
              data: this.dataset
            },
            {
              partial: true
            }
          );
        }
      }
    
      updateData() {
        const numbers: number[] = [];
    
        for (let i = 0; i < this.dataset.length; i++) {
          // Generate an array of numbers that are used for indices
          numbers[i] = i;
        }
    
        const shuffleArray = (arr: number[]) => {
          return arr
            .map((a) => [Math.random(), a])
            .sort((a, b) => a[0] - b[0])
            .map((a) => a[1]);
        };
    
        const random_permutation = shuffleArray(numbers);
        random_permutation.forEach((index: number) => {
          let item: any = this.dataset[index];
          let prev_rate;
          let new_rate;
    
          if (index % 2 === 0) {
            prev_rate = item["Bid Rate"];
            new_rate = prev_rate + this.randomFloatBetween(0.01, 0.3, 3);
            item["Bid Rate"] = new_rate;
            item["Ask Rate"] = new_rate - this.randomFloatBetween(0.01, 0.3, 3);
            this.dataset[index] = item;
          } else {
            prev_rate = item["Bid Rate"];
            new_rate = prev_rate - this.randomFloatBetween(0.01, 0.2, 3);
            if (new_rate > 0) {
              item["Bid Rate"] = new_rate;
              item["Ask Rate"] = new_rate + this.randomFloatBetween(0.01, 0.2, 3);
              this.dataset[index] = item;
            } else {
              new_rate = prev_rate + this.randomFloatBetween(0.01, 0.2, 3);
              item["Bid Rate"] = new_rate;
              item["Ask Rate"] = new_rate - this.randomFloatBetween(0.01, 0.2, 3);
              this.dataset[index] = item;
            }
          }
        });
        this.pivotRef.flexmonster.updateData(
          {
            data: this.dataset
          },
          {
            partial: true
          }
        );
      }
    
      randomFloatBetween(minValue: number, maxValue: number, precision: number) {
        if (typeof precision === "undefined") {
          precision = 4;
        }
    
        return parseFloat(
          Math.min(
            minValue + Math.random() * (maxValue - minValue),
            maxValue
          ).toFixed(precision)
        );
      }
    
      customizeCellFunction(cell: Flexmonster.CellBuilder, data: Flexmonster.CellData) {
        if (data.hierarchy?.uniqueName === "Currency" && data.member) {
          cell.addClass("header-cell");
          const names = data.member.caption!.split("/");
          const flag = `<img class="flag" style="width:36px; height:24px;"
            src="https://cdn.flexmonster.com/flags/${names[0].toLowerCase()}.svg">`;
          cell.text = `<div style="display:flex; align-items:center; font-size:12px;
            position:relative; bottom: 4px;">${flag} ${names[0]}</div>`;
        }
      }
    
      onReportComplete() {
        this.pivotRef.flexmonster.off("reportcomplete");
        this.getData();
        this.updateData();
        setInterval(() => {
          this.updateData();
        }, 6000);
      }
    }
    
    <fm-pivot
      #pivot
      [height]="450"
      [report]="report"
      [customizeCell]="customizeCellFunction"
      (reportcomplete)="onReportComplete()">
    </fm-pivot>
    
    .header-cell {
      background-color: #F7F7F7 !important;
      border-bottom: 1px solid #E9E9E9 !important;
      border-right: 1px solid #E9E9E9 !important;
    }
    
    #fm-pivot-view .fm-grid-layout div.fm-cell {
      padding-left: 20px;
    }
    
    #fm-pivot-view .fm-header {
      background-color: #e9e9e9 !important;
      border-right: 1px solid #d5d5d5 !important;
      border-bottom: 1px solid #d5d5d5 !important;
    }
    
    .fm-updated-cell-down {
      color: #DF3800 !important;
      font-weight: bold !important;
      background-image: none !important;
      -webkit-animation: fadeIt 5s ease-in-out;
      -moz-animation: fadeIt 5s ease-in-out;
      -o-animation: fadeIt 5s ease-in-out;
      animation: fadeIt 5s ease-in-out;
    }
    
    .fm-updated-cell-down::before {
      content: "\2193 \00a0";
      position: relative;
      bottom: 2px;
    }
    
    .fm-updated-cell-up {
      color: #00A45A !important;
      font-weight: bold !important;
      background-image: none !important;
      -webkit-animation: fadeIt1 5s ease-in-out;
      -moz-animation: fadeIt1 5s ease-in-out;
      -o-animation: fadeIt1 5s ease-in-out;
      animation: fadeIt1 5s ease-in-out;
    }
    
    .fm-updated-cell-up::before {
      content: '\2191 \00a0';
      position: relative;
      bottom: 2px;
    }
    
    @-webkit-keyframes fadeIt {
      0% {
        background-color: #FFFFFF;
      }
    
      20% {
        background-color: #F9D7CC;
        border-bottom: 1px solid #e9c5ba;
        border-right: 1px solid #e9c5ba;
      }
    
      50% {
        background-color: #F9D7CC;
        border-bottom: 1px solid #e9c5ba;
        border-right: 1px solid #e9c5ba;
      }
    
      100% {
        background-color: #FFFFFF;
      }
    }
    
    @-moz-keyframes fadeIt {
      0% {
        background-color: #FFFFFF;
      }
    
      20% {
        background-color: #F9D7CC;
        border-bottom: 1px solid #e9c5ba;
        border-right: 1px solid #e9c5ba;
      }
    
      50% {
        background-color: #F9D7CC;
        border-bottom: 1px solid #e9c5ba;
        border-right: 1px solid #e9c5ba;
      }
    
      100% {
        background-color: #FFFFFF;
      }
    }
    
    @-o-keyframes fadeIt {
      0% {
        background-color: #FFFFFF;
      }
    
      20% {
        background-color: #F9D7CC;
        border-bottom: 1px solid #e9c5ba;
        border-right: 1px solid #e9c5ba;
      }
    
      50% {
        background-color: #F9D7CC;
        border-bottom: 1px solid #e9c5ba;
        border-right: 1px solid #e9c5ba;
      }
    
      100% {
        background-color: #FFFFFF;
      }
    }
    
    @keyframes fadeIt {
      0% {
        background-color: #FFFFFF;
      }
    
      20% {
        background-color: #F9D7CC;
        border-bottom: 1px solid #e9c5ba;
        border-right: 1px solid #e9c5ba;
      }
    
      50% {
        background-color: #F9D7CC;
        border-bottom: 1px solid #e9c5ba;
        border-right: 1px solid #e9c5ba;
      }
    
      100% {
        background-color: #FFFFFF;
      }
    }
    
    @-webkit-keyframes fadeIt1 {
      0% {
        background-color: #FFFFFF;
      }
    
      20% {
        background-color: #CCEDDE;
        border-bottom: 1px solid #aedbc7;
        border-right: 1px solid #aedbc7;
      }
    
      50% {
        background-color: #CCEDDE;
        border-bottom: 1px solid #aedbc7;
        border-right: 1px solid #aedbc7;
      }
    
      100% {
        background-color: #FFFFFF;
      }
    }
    
    @-moz-keyframes fadeIt1 {
      0% {
        background-color: #FFFFFF;
      }
    
      20% {
        background-color: #CCEDDE;
        border-bottom: 1px solid #aedbc7;
        border-right: 1px solid #aedbc7;
      }
    
      50% {
        background-color: #CCEDDE;
        border-bottom: 1px solid #aedbc7;
        border-right: 1px solid #aedbc7;
      }
    
      100% {
        background-color: #FFFFFF;
      }
    }
    
    @-o-keyframes fadeIt1 {
      0% {
        background-color: #FFFFFF;
      }
    
      20% {
        background-color: #CCEDDE;
        border-bottom: 1px solid #aedbc7;
        border-right: 1px solid #aedbc7;
      }
    
      50% {
        background-color: #CCEDDE;
        border-bottom: 1px solid #aedbc7;
        border-right: 1px solid #aedbc7;
      }
    
      100% {
        background-color: #FFFFFF;
      }
    }
    
    @keyframes fadeIt1 {
      0% {
        background-color: #FFFFFF;
      }
    
      20% {
        background-color: #CCEDDE;
        border-bottom: 1px solid #aedbc7;
        border-right: 1px solid #aedbc7;
      }
    
      50% {
        background-color: #CCEDDE;
        border-bottom: 1px solid #aedbc7;
        border-right: 1px solid #aedbc7;
      }
    
      100% {
        background-color: #FFFFFF;
      }
    }
    

    Using our updateData() API call, you can receive the newly updated data from your data source and visualize it with the same slice and formatting. With such an approach, you don’t need to reload the full report and can speed up the data loading process to create the most effective Angular reporting tool.

    This Angular pivot grid demo shows how JSON data can be partially updated and how to highlight all the changes via the conditional formatting feature.