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.
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.
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.
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.
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"><strong>Overall Cheese Interest by Month</strong></div> <div id="amcharts-stacked-container" class="chart-container"></div> </div> <div class="demo-box"> <div class="demo-title"><strong>Total Cheese Interest by Country</strong></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"><strong>Feta Interest by Month</strong></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"><strong>Feta Interest by Country</strong></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.