Pricing changes are coming in January. Contact our Sales team to secure the current price for your desired license.
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.

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

    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.