Syncing chart data with table/grid
This tutorial will look at ways on how we can grab chart data and display it in an HTML table. We will then build on it to enhance for display in a Grid component. Finally, we’ll make chart data sync up with editable grid.
Introduction
Section titled “Introduction”Since amCharts is not in the data grid business, we will be relying on 3rd parties for this specific functionality.
We will be looking at different libraries and solutions, just to give an idea on how it can be universally used.
Let’s use a common scenario char - an XYChart with date-based data and multi-series:
Chart data in a table
Section titled “Chart data in a table”The first step in everything we will do in this tutorial is getting chart data into an HTML table.
Building data table
Section titled “Building data table”One way would be to grab chart.data, iterate through it, and build HTML for it which we would then plop onto our document.
Doable, and quite easy, but we have a better solution: using built-in export API.
Among other things it supports exporting data as an HTML table markup, essentially doing all the job for us.
The export function that will give us that is called chart.exporting.getHTML().
This method returns a Promise, which means it’s asynchronous, requiring us to either use await or then() syntax:
chart.exporting.getHTML("html", { addColumnNames: true}, false).then(function(html) { // html will contain fully formatted markup for data in a <table>});chart.exporting.getHTML("html", { addColumnNames: true}, false).then(function(html) { // html will contain fully formatted markup for data in a <table>});Adding table to document
Section titled “Adding table to document”Now that we know how to grab chart’s data as a <table>, all we need to do is to plop it onto our document.
Let’s put our table into a separate <div> so that we can style and size it separately from the chart.
<div id="chartdiv"></div><div id="chartdata"></div>Let’s also add very basic styling, to limit height of the resulting table.
#chartdata { max-height: 400px; overflow: auto;}
#chartdata table { width: 100%;}Now that we have our data container, let’s make the magic happen.
One thing before we start: when chart is created it hasn’t had a chance to parse it’s data yet.
Let’s use chart’s datavalidated event, to start our export instead.
This events kicks in whenever data is fully parsed and ready to be used.
An added bonus is that it will automatically update our table if/whenever chart’s data is updated, so it’s basically a healthy thing to do.
chart.events.on("datavalidated", function(ev) { chart.exporting.getHTML("html", { addColumnNames: true }, false).then(function(html) { let div = document.getElementById("chartdata"); div.innerHTML = html; });});chart.events.on("datavalidated", function(ev) { chart.exporting.getHTML("html", { addColumnNames: true }, false).then(function(html) { var div = document.getElementById("chartdata"); div.innerHTML = html; });});{ // ... "events": { "datavalidated": function(ev) { var chart = ev.target; chart.exporting.getHTML("html", { addColumnNames: true }, false).then(function(html) { var div = document.getElementById("chartdata"); div.innerHTML = html; }); } }}Let’s see how this works in real life:
Styling the table
Section titled “Styling the table”Looks a bit dull, doesn’t it?
By default, <table> comes without any styling or even class names attached.
However, we can pass in class names to be attached to each of the elements of the table, via format options (second parameter to the getHTML() function):
| Tag | Option |
|---|---|
<table> |
tableClass |
<tr> |
rowClass |
<th> and <td> |
cellClass |
Let’s class-up our table for use with Bootstrap.
chart.events.on("datavalidated", function(ev) { chart.exporting.getHTML("html", { addColumnNames: true, tableClass: "table table-striped table-hover table-sm" }, false).then(function(html) { let div = document.getElementById("chartdata"); div.innerHTML = html; });});chart.events.on("datavalidated", function(ev) { chart.exporting.getHTML("html", { addColumnNames: true, tableClass: "table table-striped table-hover table-sm" }, false).then(function(html) { var div = document.getElementById("chartdata"); div.innerHTML = html; });});{ // ... "events": { "datavalidated": function(ev) { var chart = ev.target; chart.exporting.getHTML("html", { addColumnNames: true, tableClass: "table table-striped table-hover table-sm" }, false).then(function(html) { var div = document.getElementById("chartdata"); div.innerHTML = html; }); } }}Let’s see how it looks like now:
3rd party libraries
Section titled “3rd party libraries”Let’s keep on moving. Let’s turn our static table into a fully-featured grid.
As there are hundreds, or even thousands, of grid libraries out there, let’s explore ones that we like best.
DataTables
Section titled “DataTables”DataTables is a free jQuery-based grid plugin. It can instantly turn a regular <table> element into a searchable, sortable table with paging and other neat features.
We don’t need to do much for it:
- Include jQuery, as well as CSS and JS for DataTables.
- Turn our plain table into a DataTable:
$("#chartdata table").DataTable().
chart.events.on("datavalidated", function(ev) { chart.exporting.getHTML("html", { addColumnNames: true }, false).then(function(html) { let div = document.getElementById("chartdata"); div.innerHTML = html; $("#chartdata table").DataTable(); });});chart.events.on("datavalidated", function(ev) { chart.exporting.getHTML("html", { addColumnNames: true }, false).then(function(html) { var div = document.getElementById("chartdata"); div.innerHTML = html; $("#chartdata table").DataTable(); });});{ // ... "events": { "datavalidated": function(ev) { var chart = ev.target; chart.exporting.getHTML("html", { addColumnNames: true }, false).then(function(html) { var div = document.getElementById("chartdata"); div.innerHTML = html; $("#chartdata table").DataTable(); }); } }}DataGridXL
Section titled “DataGridXL”DataGridXL is a no-nonsense light and super fast grid library, that does not require any additional dependencies, like jQuery.
It’s also allows editing any data displayed in it. We will explore this as well, to give it a two way communication between chart and grid.
Displaying data in a grid
Section titled “Displaying data in a grid”Unlike DataTables we explored before, DataGridXL does not support turning a <table> into grid.
Instead it will accept data in a JavaScript array, which means we can skip the whole getHTML() step, and just dump chart’s data to the grid:
chart.events.on("datavalidated", function(ev) { let grid = new DataGridXL("chartdata", { data: chart.data });});chart.events.on("datavalidated", function(ev) { var grid = new DataGridXL("chartdata", { data: chart.data });});{ // ... "events": { "datavalidated": function(ev) { var chart = ev.target; var grid = new DataGridXL("chartdata", { data: chart.data }); } }}Live example:
Pushing data edits back to chart
Section titled “Pushing data edits back to chart”DataGridXL allows editing value sin any cell.
Whenever it happens it triggers a cellvaluechange event.
At that point we could just use grid’s getData() method, then set chart’s data to it.
However, that would be very inneficient.
The parameter passed into handler function of cellvaluechange contains all the new values as well as identifiers of cells that were chainged.
Using those, we can modify items in chart’s data array individually, then call its invalidateRawData() method to take in just the changed values:
chart.events.on("datavalidated", function(ev) { let grid = new DataGridXL("chartdata", { data: chart.data });
grid.events.on("cellvaluechange", function(ev) { for(let r = 0; r < ev.rowIds.length; r++) { var row = chart.data[ev.rowIds[r]]; for(let c = 0; c < ev.colIds.length; c++) { var col = grid.getColFromStore(ev.colIds[c]).field; row[col] = ev.changes[r][c]; } } chart.invalidateRawData(); });
});chart.events.on("datavalidated", function(ev) { var grid = new DataGridXL("chartdata", { data: chart.data });
grid.events.on("cellvaluechange", function(ev) { for(var r = 0; r < ev.rowIds.length; r++) { var row = chart.data[ev.rowIds[r]]; for(var c = 0; c < ev.colIds.length; c++) { var col = grid.getColFromStore(ev.colIds[c]).field; row[col] = ev.changes[r][c]; } } chart.invalidateRawData(); });
});{ // ... "events": { "datavalidated": function(ev) { var chart = ev.target; var grid = new DataGridXL("chartdata", { data: chart.data });
grid.events.on("cellvaluechange", function(ev) { var grid = this; for(var r = 0; r < ev.rowIds.length; r++) { var row = chart.data[ev.rowIds[r]]; for(var c = 0; c < ev.colIds.length; c++) { var col = grid.getColFromStore(ev.colIds[c]).field; row[col] = ev.changes[r][c]; } } chart.invalidateRawData(); }); } }}Go ahead, try changing values in the grid:
Toggling data table
Section titled “Toggling data table”Let’s add one final touch: a switch which would toggle data table on and off.
amCharts 4 has a perfect control for it: SwitchButton.
Let add one to our chart, as well as attach an event that would toggle data table:
// A button to toggle the data tablelet button = chart.createChild(am4core.SwitchButton);button.align = "right";button.leftLabel.text = "Show data";button.isActive = false;
// Set toggling of data tablebutton.events.on("toggled", function(ev) { let div = document.getElementById("chartdata"); if (button.isActive) { div.style.display = "block"; } else { div.style.display = "none"; }});// A button to toggle the data tablevar button = chart.createChild(am4core.SwitchButton);button.align = "right";button.leftLabel.text = "Show data";button.isActive = false;
// Set toggling of data tablebutton.events.on("toggled", function(ev) { var div = document.getElementById("chartdata"); if (button.isActive) { div.style.display = "block"; } else { div.style.display = "none"; }});