☝️Small business or a startup? See if you qualify for our special offer.
+
List of all demos

Angular Pivot Table with amCharts

Flexmonster Pivot Table & Charts seamlessly integrates with amCharts — a simple but powerful and flexible drop-in data visualization library with super interactive charts.

To show you a case of Flexmonster & amCharts usage we took some Kaggle data about the Cheese industry and created this Angular UI grid example.

Domain mismatchFlexmonster is used on the domain prod-flexmonster-container-service.tbvgv2m9krico.us-east-1.cs.amazonlightsail.com, which does not match the domain your license key was issued for (flexmonster.com)

Contact our team to request a key for the domain prod-flexmonster-container-service.tbvgv2m9krico.us-east-1.cs.amazonlightsail.com

You are using the following key:
Z707-XCI209-0Q203E-3I5M00-28254L-0B2Z2S-0L0T3F-1H3W3A-421F46

Current version: 2.9.102 (build 04/28/2025)

Thanks to our chart connector, your visualizations will become even more interactive: as soon as you change the slice on the pivot or filter the data, your charts will immediately transform.

Overall Cheese Interest by Month

With the amCharts connector you can create an efficient and interactive Angular chart dashboard: change the pivot grid slice or apply a new filter - the charts will transform instantly.

Total Cheese Interest by Country

Icons made by Freepik from www.flaticon.com

Combining amCharts and Flexmonster Pivot Table is a piece of cake! A wide range of colorful and modern charts can be fully customized to match your Angular report style.

Feta Interest by Month

Simply connect your Angular pivot grid to any data source, aggregate, and proceed the data. Then just pass the data to the charting library and get a modern visualization for your report.

Feta Interest by Country

  • .ts
  • .html
  • .css
  • index.html
import { Component, OnInit, ViewChild } from "@angular/core";
import { FlexmonsterPivot } from "ng-flexmonster";

// Importing Flexmonster Connector for amCharts
import "flexmonster/lib/flexmonster.amcharts.js";

// amCharts imports
import * as am5 from "@amcharts/amcharts5";
import * as am5xy from "@amcharts/amcharts5/xy";
import * as am5percent from "@amcharts/amcharts5/percent";
import * as am5map from "@amcharts/amcharts5/map";
import am5geodata_worldHigh from "@amcharts/amcharts5-geodata/worldHigh";
import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
import am5themes_Responsive from "@amcharts/amcharts5/themes/Responsive";

import * as Flexmonster from "flexmonster";

@Component({
  selector: "pivotComponent",
  templateUrl: "./pivot.component.html",
  styleUrls: ["./pivot.component.css"],
})
export class PivotComponent implements OnInit {
  @ViewChild("pivot") pivot!: FlexmonsterPivot;

  public report: Flexmonster.Report = {
    dataSource: {
      type: "json",
      filename: "data/demos/amcharts-demo-data.json",
      mapping: {
        Date: {
          type: "date",
        },
        Country: {
          type: "string",
        },
        id: {
          type: "string",
        },
        CountryCode: {
          type: "property",
          hierarchy: "Country",
        },
        Feta: {
          type: "number",
        },
        Mozzarella: {
          type: "number",
        },
        "Parmigiano-Reggiano": {
          type: "number",
        },
      },
    },
    slice: {
      rows: [
        {
          uniqueName: "Date.Month",
          filter: {
            exclude: ["date.month.[december]", "date.month.[november]", "date.month.[october]"],
          },
        },
        {
          uniqueName: "[Measures]",
        },
      ],
      columns: [
        {
          uniqueName: "Country",
        },
      ],
      measures: [
        {
          uniqueName: "Feta",
          aggregation: "sum",
          grandTotalCaption: "Feta",
        },
        {
          uniqueName: "Mozzarella",
          aggregation: "sum",
          grandTotalCaption: "Mozzarella",
        },
        {
          uniqueName: "Parmigiano-Reggiano",
          aggregation: "sum",
          grandTotalCaption: "Parmigiano-Reggiano",
        },
      ],
    },
    options: {
      grid: {
        showHeaders: false,
        showGrandTotals: "rows",
      },
      showAggregationLabels: false,
    },
  };

  stackedChartRoot: am5.Root | undefined;
  pictorialChartRoot: am5.Root | undefined;
  pieChartRoot: am5.Root | undefined;
  mapChartRoot: am5.Root | undefined;

  chartColors = [
    am5.color("#4CBF8B"),
    am5.color("#FFCD4C"),
    am5.color("#E8734C"),
    am5.color("#9875E3"),
    am5.color("#4C9EFF"),
    am5.color("#8ACFC3"),
    am5.color("#CD97E6"),
    am5.color("#F1D34C"),
    am5.color("#65D2E7"),
  ];

  cheeseColors = [am5.color("#FFE268"), am5.color("#FFCD4C"), am5.color("#FFB037")];

  constructor() {}

  ngOnInit(): void {}

  onReportComplete() {
    this.pivot.flexmonster.off("reportcomplete");
    this.createStackedChart();
    this.createPictorialChart();
    this.createPieChart();
    this.createMapChart();
  }

  createStackedChart() {
    this.pivot.flexmonster.amcharts!.getData(
      {},
      this.drawStackedChart.bind(this),
      this.updateStackedChart.bind(this)
    );
  }

  drawStackedChart = (
    chartData: Flexmonster.GetDataValueObject,
    rawData: Flexmonster.GetDataValueObject
  ) => {
    // Create chart instance
    this.stackedChartRoot = am5.Root.new("amcharts-stacked-container");

    // Set themes
    this.stackedChartRoot.setThemes([am5themes_Animated.new(this.stackedChartRoot)]);

    let stackedChart = this.stackedChartRoot.container.children.push(
      am5xy.XYChart.new(this.stackedChartRoot, {})
    );
    stackedChart.get("colors")!.set("colors", this.chartColors);
    // Craete Y-axis
    var valueAxis = stackedChart.yAxes.push(
      am5xy.ValueAxis.new(this.stackedChartRoot, {
        renderer: am5xy.AxisRendererY.new(this.stackedChartRoot, {}),
        tooltip: am5.Tooltip.new(this.stackedChartRoot, {
          maxWidth: 200,
          tooltipText: "{category}",
        }),
      })
    );

    valueAxis.children.unshift(
      am5.Label.new(this.stackedChartRoot, {
        rotation: -90,
        text: "Queries",
        y: am5.p50,
        centerX: am5.p50,
      })
    );

    // Create X-Axis
    var categoryAxis = stackedChart.xAxes.push(
      am5xy.CategoryAxis.new(this.stackedChartRoot, {
        renderer: am5xy.AxisRendererX.new(this.stackedChartRoot, {
          minGridDistance: 20,
        }),
        categoryField: this.pivot.flexmonster.amcharts!.getCategoryName(rawData),
        tooltip: am5.Tooltip.new(this.stackedChartRoot, {
          maxWidth: 200,
          tooltipText: "{category}",
        }),
      })
    );

    //Get xRendeder for axis configurations
    let xRenderer = categoryAxis.get("renderer");

    xRenderer.grid.template.setAll({
      location: 0,
    });

    const maxWidth = 200;

    xRenderer.labels.template.setAll({
      maxWidth: maxWidth,
      tooltipText: "{category}",
    });

    categoryAxis.events.on("boundschanged", function (ev) {
      let axis = ev.target;
      let cellWidth =
        axis.innerWidth() / (axis.getPrivate("endIndex")! - axis.getPrivate("startIndex")!);
      if (cellWidth < maxWidth) {
        xRenderer.labels.template.setAll({
          rotation: -45,
        });
      } else {
        xRenderer.labels.template.setAll({
          rotation: 0,
        });
      }
    });

    categoryAxis.data.setAll(chartData.data);

    for (let i = 0; i < this.pivot.flexmonster.amcharts!.getNumberOfMeasures(rawData); i++) {
      // Create series
      let series = stackedChart.series.push(
        am5xy.ColumnSeries.new(this.stackedChartRoot, {
          xAxis: categoryAxis,
          yAxis: valueAxis,
          categoryXField: this.pivot.flexmonster.amcharts!.getCategoryName(rawData),
          valueYField: this.pivot.flexmonster.amcharts!.getMeasureNameByIndex(rawData, i),
          name: this.pivot.flexmonster.amcharts!.getMeasureNameByIndex(rawData, i).split(" ").pop(),
          stacked: true,
        })
      );

      series.columns.template.setAll({
        tooltipText: "{name}, {categoryX}: {valueY}",
      });

      series.data.setAll(chartData.data);
    }
    stackedChart.set("cursor", am5xy.XYCursor.new(this.stackedChartRoot, {}));
  };

  updateStackedChart(
    chartData: Flexmonster.GetDataValueObject,
    rawData: Flexmonster.GetDataValueObject
  ) {
    this.stackedChartRoot!.dispose();
    this.drawStackedChart(chartData, rawData);
  }

  createPictorialChart() {
    this.pivot.flexmonster.amcharts!.getData(
      {
        slice: {
          rows: [
            {
              uniqueName: "Country",
            },
            {
              uniqueName: "[Measures]",
            },
          ],
          measures: [
            {
              uniqueName: "Fetas",
              formula: 'sum("Feta")',
              caption: "value",
            },
          ],
        },
      },
      this.drawPictorialChart.bind(this),
      this.updatePictorialChart.bind(this)
    );
  }

  drawPictorialChart(
    chartData: Flexmonster.GetDataValueObject,
    rawData: Flexmonster.GetDataValueObject
  ) {
    let iconPathValue = window.iconPath();

    // Create chart instance
    this.pictorialChartRoot = am5.Root.new("amcharts-pictorial-container");
    let pictorialChart = this.pictorialChartRoot.container.children.push(
      am5percent.SlicedChart.new(this.pictorialChartRoot, {
        layout: this.pictorialChartRoot.horizontalLayout,
      })
    );

    // Set themes
    this.pictorialChartRoot.setThemes([am5themes_Responsive.new(this.pictorialChartRoot)]);

    //Create series
    let series = pictorialChart.series.push(
      am5percent.PictorialStackedSeries.new(this.pictorialChartRoot, {
        name: "Series",
        valueField: this.pivot.flexmonster.amcharts!.getMeasureNameByIndex(rawData, 0),
        orientation: "horizontal",
        categoryField: this.pivot.flexmonster.amcharts!.getCategoryName(rawData),
        alignLabels: true,
        svgPath: iconPathValue,
        maxWidth: 500,
        centerX: am5.percent(50),
        x: am5.percent(50),
      })
    );
    series.ticks.template.set("visible", false);
    series.labels.template.set("visible", false);

    series.get("colors")?.set("colors", this.cheeseColors);

    series.data.setAll(chartData.data);

    //Create legend
    var legend = pictorialChart.children.push(
      am5.Legend.new(this.pictorialChartRoot, {
        centerY: am5.percent(50),
        y: am5.percent(50),
        layout: this.pictorialChartRoot.verticalLayout,
      })
    );

    legend.markerRectangles.template.setAll({
      cornerRadiusTL: 10,
      cornerRadiusTR: 10,
      cornerRadiusBL: 10,
      cornerRadiusBR: 10,
    });

    legend.data.setAll(series.dataItems);
  }

  updatePictorialChart(
    chartData: Flexmonster.GetDataValueObject,
    rawData: Flexmonster.GetDataValueObject
  ) {
    // Here you can add your own logic for updating the chart
  }

  createPieChart() {
    this.pivot.flexmonster.amcharts!.getData(
      {
        slice: {
          rows: [
            {
              uniqueName: "Date.Month",
              filter: {
                members: [
                  "date.month.[january]",
                  "date.month.[february]",
                  "date.month.[march]",
                  "date.month.[april]",
                  "date.month.[may]",
                  "date.month.[june]",
                ],
              },
            },
          ],
          measures: [
            {
              uniqueName: "Feta",
              aggregation: "sum",
            },
          ],
        },
      },
      this.drawPieChart.bind(this),
      this.updatePieChart.bind(this)
    );
  }

  drawPieChart(chartData: Flexmonster.GetDataValueObject, rawData: Flexmonster.GetDataValueObject) {
    this.pieChartRoot = am5.Root.new("amcharts-pie-container");

    // Set themes
    this.pieChartRoot.setThemes([am5themes_Animated.new(this.pieChartRoot)]);

    let pieChart = this.pieChartRoot.container.children.push(
      am5percent.PieChart.new(this.pieChartRoot, {
        numberFormatter: am5.NumberFormatter.new(this.pieChartRoot, {
          numberFormat: this.pivot.flexmonster.amcharts!.getNumberFormatPattern(
            (<any>rawData.meta).formats[0]
          ),
        }),
      })
    );

    // Create pie series
    let series = pieChart.series.push(
      am5percent.PieSeries.new(this.pieChartRoot, {
        name: "Series",
        y: am5.percent(-10),
        valueField: this.pivot.flexmonster.amcharts!.getMeasureNameByIndex(rawData, 0),
        categoryField: this.pivot.flexmonster.amcharts!.getCategoryName(rawData),
        alignLabels: true,
      })
    );
    series.get("colors")?.set("colors", this.chartColors);
    series.labels.template.set("text", "{category}: {value}");

    series.slices.template.adapters.add("radius", function (radius: any, target: any) {
      let dataItem = target.dataItem;
      let high = series.getPrivate("valueHigh");

      if (dataItem) {
        let value = target.dataItem?.get("valueWorking", 0);
        return (radius! * value) / high!;
      }
      return radius;
    });

    series.slices.template.setAll({
      cornerRadius: 6,
      stroke: am5.color("#fff"),
      strokeWidth: 2,
      strokeOpacity: 1,
    });

    // Fill the chart with the data from Flexmonster
    series.data.setAll(chartData.data);

    //Create legend
    var legend = pieChart.children.push(
      am5.Legend.new(this.pieChartRoot, {
        centerX: am5.percent(50),
        x: am5.percent(50),
        y: am5.percent(95),
        centerY: am5.percent(100)
      })
    );

    const responsive = am5themes_Responsive.new(this.pieChartRoot);

    responsive.addRule({
      name: "Series",
      relevant: function (width, height) {
        return width <= 600;
      },
      applying: () => {
        series.ticks.template.set("visible", false);
        series.labels.template.set("visible", false);
      },
      removing: () => {
        series.ticks.template.set("visible", true);
        series.labels.template.set("visible", true);
      },
    });

    this.pieChartRoot.setThemes([responsive]);

    legend.data.setAll(series.dataItems);
  }

  updatePieChart(
    chartData: Flexmonster.GetDataValueObject,
    rawData: Flexmonster.GetDataValueObject
  ) {
    // Here you can add your own logic for updating the chart
  }

  createMapChart() {
    this.pivot.flexmonster.amcharts!.getData(
      {
        slice: {
          rows: [
            {
              uniqueName: "id",
            },
          ],
          columns: [
            {
              uniqueName: "[Measures]",
            },
          ],
          measures: [
            {
              uniqueName: "Fetas",
              formula: 'sum("Feta")',
              caption: "value",
            },
          ],
        },
      },
      this.drawMapChart.bind(this),
      this.updateMapChart.bind(this)
    );
  }

  drawMapChart(chartData: Flexmonster.GetDataValueObject, rawData: Flexmonster.GetDataValueObject) {
    this.mapChartRoot = am5.Root.new("amcharts-map-container");

    // Set themes
    this.mapChartRoot.setThemes([am5themes_Animated.new(this.mapChartRoot)]);
    // Create chart
    let mapChart = this.mapChartRoot.container.children.push(
      am5map.MapChart.new(this.mapChartRoot, <any>{
        panX: "rotateX",
        panY: "none",
        valueField: this.pivot.flexmonster.amcharts!.getMeasureNameByIndex(rawData, 0),
        homeZoomLevel: 9,
        maxZoomLevel: 9,
        minZoomLevel: 9,
        wheelY: "none",
        homeGeoPoint: {
          longitude: 12.496366,
          latitude: 42.399982,
        },
      })
    );

    // Create polygon series
    let polygonSeries = mapChart.series.push(
      am5map.MapPolygonSeries.new(this.mapChartRoot, {
        geoJSON: am5geodata_worldHigh,
        valueField: this.pivot.flexmonster.amcharts!.getMeasureNameByIndex(rawData, 0),
        calculateAggregates: true,
        exclude: ["AQ"],
      })
    );

    polygonSeries.mapPolygons.template.setAll({
      tooltipText: "{name} {value}",
      fill: am5.color("#D3D3D3"),
    });

    polygonSeries.mapPolygons.template.events.on("pointerover", function (ev: any) {
      heatLegend.showValue(ev.target.dataItem.get("value"));
    });

    polygonSeries.events.on("datavalidated", () => {
      mapChart.goHome();
    });

    polygonSeries.set("heatRules", [
      {
        target: polygonSeries.mapPolygons.template,
        dataField: this.pivot.flexmonster.amcharts!.getMeasureNameByIndex(rawData, 0),
        min: am5.color("#F1D34C"),
        max: am5.color("#4CBF8B"),
        key: "fill",
      },
    ]);

    polygonSeries.data.setAll(chartData.data);

    // Add heat legend
    var heatLegend = mapChart.children.push(
      am5.HeatLegend.new(this.mapChartRoot, <any>{
        target: polygonSeries.mapPolygons.template,
        orientation: "horizontal",
        width: am5.percent(100),
        y: am5.percent(100),
        centerY: am5.percent(100),
        startColor: am5.color("#F1D34C"),
        endColor: am5.color("#4CBF8B"),
        paddingBottom: 20,
        paddingTop: 20,
        paddingLeft: 20,
        paddingRight: 20,
      })
    );

    polygonSeries.events.on("datavalidated", function () {
      heatLegend.set("startValue", polygonSeries.getPrivate("valueLow"));
      heatLegend.set("endValue", polygonSeries.getPrivate("valueHigh"));
    });
  }

  updateMapChart(
    chartData: Flexmonster.GetDataValueObject,
    rawData: Flexmonster.GetDataValueObject
  ) {
    // Here you can add your own logic for updating the chart
  }

  customizeCellFunction(cell: Flexmonster.CellBuilder, data: Flexmonster.CellData) {
    if (
      data.type === "header" &&
      data.hierarchy?.caption === "Country" &&
      data.member?.properties
    ) {
      let name = data.member.properties.CountryCode;
      let flag = `<i class="fm-icon ${data.expanded ? "fm-expanded-icon" : "fm-collapsed-icon"}"
  title="${data.expanded ? "Click to collapse" : "Click to expand"}"></i><img class="flag-icon"
  src="https://cdn.flexmonster.com/i/flags/${name.toLowerCase()}.svg">`;
      cell.text = `${flag}<span style="margin-left: 2px; line-height: 16px">${data.member.caption}</span>`;
      cell.addClass("fm-custom-cell");
    }
  }
}
<fm-pivot
  #pivot
  [componentFolder]="'https://cdn.flexmonster.com/'"
  [height]="440"
  [licenseFilePath]="'https://cdn.flexmonster.com/jsfiddle.charts.key'"
  [report]="report"
  [customizeCell]="customizeCellFunction"
  (reportcomplete)="onReportComplete()"
>
</fm-pivot>

<div class="demo-box">
  <div class="demo-title">Overall Cheese Interest by Month</div>
  <div id="amcharts-stacked-container" class="chart-container"></div>
</div>

<div class="demo-box">
  <div class="demo-title">Total Cheese Interest by Country</div>
  <div id="amcharts-pictorial-container" class="chart-container"></div>
  <div>
    Icons made by <a href="https://www.freepik.com" title="Freepik">Freepik</a> from
    <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
  </div>
</div>

<div class="demo-box">
  <div class="demo-title">Feta Interest by Month</div>
  <div id="amcharts-pie-container" class="chart-container"></div>
</div>

<div class="demo-box no-text-before no-text-after">
  <div class="demo-title">Feta Interest by Country</div>
  <div id="amcharts-map-container" class="chart-container"></div>
</div>
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

.chart-container {
  height: 450px;
}

.demo-box {
  background-color: #fafafa;
  position: relative;
  padding: 40px 30px 30px 30px;
  border: 1px solid #e9e9e9;
  margin-bottom: 20px;
  margin-top: 40px;
}

.demo-title {
  font-size: 18px;
  margin-bottom: 30px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  line-height: 24px;
}

.fm-custom-cell {
  display: flex !important;
  align-items: center !important;
  font-size: 12px !important;
}

.fm-custom-cell .flag-icon {
  width: 21px !important;
  height: 16px !important;
  margin-left: 0 !important;
  margin-right: 2px !important;
}

#fm-pivot-view .fm-grid-layout .fm-custom-cell.fm-expanded .fm-expanded-icon::before,
#fm-pivot-view .fm-grid-layout .fm-custom-cell.fm-collapsed .fm-collapsed-icon::before {
  top: -2px;
  height: 16px;
}
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Flexmonster & amCharts Demo</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <!-- This file contains the iconPathValue variable needed for the pictorial chart -->
  <script src="https://cdn.flexmonster.com/data/demos/amcharts-iconPath.js"></script>
</head>

<body>
  <app-root></app-root>
</body>

</html>

Follow our step-by-step integration with amCharts guide to quickly and easily start your Angular reporting.