Search code examples
javascriptamcharts

How to add series on external event in amCharts v4?


I have a simple amCharts v4 chart with a function that adds some series and axes.

If I call the function from the script, the chart is displayed correctly, but if it is called externally from a button click or timeout, the legend displays but the series and axes do not. Is there something extra that needs to be done if the series is added later?

var chart = am4core.create("chartdiv", am4charts.XYChart);

chart.data = generateChartData();
chart.legend = new am4charts.Legend();
chart.cursor = new am4charts.XYCursor();

// Create axes
var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.renderer.minGridDistance = 50;

// Create series
function createAxisAndSeries(field, name, opposite, bullet) {
  var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
  var series = chart.series.push(new am4charts.LineSeries());
  series.dataFields.valueY = field;
  series.dataFields.dateX = "date";
  series.strokeWidth = 2;
  series.yAxis = valueAxis;
  series.name = name;
  series.tooltipText = "{name}: [bold]{valueY}[/]";
  var interfaceColors = new am4core.InterfaceColorSet();
}

var axisAndSeriesList = [];
function addAxisAndSeries(name) {
  if (name==="Visits") {
    createAxisAndSeries("visits", "Visits", false, "circle");  
  } else if (name==="Views") {
    createAxisAndSeries("views", "Views", true, "triangle");
  } else if (name==="Hits") {
    createAxisAndSeries("hits", "Hits", true, "rectangle");
  } else {
    console.warn('what is ' + name +'?');
  }
}

// generate some random data, quite different range
function generateChartData() {
  var chartData = [];
  var firstDate = new Date();
  firstDate.setDate(firstDate.getDate() - 100);
  firstDate.setHours(0, 0, 0, 0);

  var visits = 1600;
  var hits = 2900;
  var views = 8700;

  for (var i = 0; i < 15; i++) {
    var newDate = new Date(firstDate);
    newDate.setDate(newDate.getDate() + i);

    visits += Math.round((Math.random()<0.5?1:-1)*Math.random()*10);
    hits += Math.round((Math.random()<0.5?1:-1)*Math.random()*10);
    views += Math.round((Math.random()<0.5?1:-1)*Math.random()*10);

    chartData.push({date: newDate, visits: visits, hits: hits, views: views });
  }
  return chartData;
}

function addSeries() {
  console.log('Add all 3 in the series');
  addAxisAndSeries("Visits");
  addAxisAndSeries("Views");
  addAxisAndSeries("Hits");
}

// These work
// addAxisAndSeries("Visits");
// addAxisAndSeries("Views");
// addAxisAndSeries("Hits");

// This works
// addSeries();  

// This does not work
// setTimeout(function(){
//   addSeries();
// }, 3000);

// Clicking on "Add series" button does not work (from HTML)
// <input type="button" onclick="addSeries()" value="Add series" />

See sample pen: https://codepen.io/anon/pen/YdYJKy?editors=0011


Solution

  • You were on the right track in your answer:

    The series inherit data from the chart, but apparently that ends with the script.

    As you've witnessed, when adding a series dynamically, basically the chart doesn't make presumptions whether the asynchronously-added series will have its own data or will use the chart's. To have the chart continue to provide its data to newly-added series, run chart.invalidateData();.

    Here's a fork of your demo that adds that to the addSeries() function:

    https://codepen.io/team/amcharts/pen/bb863c597a46895f87d2b67534c353f6

    Now the function works whether synchronously or asynchronously, whether via setTimeout or via the button.