I have a simple issue relating to function sequence that I'm struggling to solve on my own. Essentially, I'm rendering a bar chart based on parameters selected by the user. This works perfectly on the first go, but when the user updates their parameter selection and hits "render" again, the chart doesn't update. It's only on the second button click that the chart renders with updated data.
I can see in the console that the "generate chart" function IS receiving the updated data, so I know the issue is that the chart is rendering before the new data is loaded. I understand that the common solution to this type of problem would be using JS Promises or callbacks, but I can't figure out how to execute it, and would really appreciate some help.
Here's the code structure. I have to make two API calls to get the data.
const ajaxCallOne = () => {
// Make API call for first data set (data1). On success, invoke the next API call, passing data1.
success: (data1) => {
ajaxCallTwo(data1);
}
}
const ajaxCallTwo = (data1) => {
// Make second API call for another set of data ('data2'). On success, invoke a function to visualize all the data in a bar chart, passing it both data1 and data2.
success: (data2) => {
populateBarChart(data1, data2)
}
}
const populateBarChart = (data1, data2) => {
// Function to create a bar chart and populate it with data1 and data2 from the API calls
// The data retrieved from the API calls is based on parameters selected by the user. When the user selects new parameters and hits the "view results" button again, we kick off another round of API calls, which pass the new data to this populateBarChart function.
// I am using ApexCharts.js library to render the bar chart. The syntax for actually rendering the chart looks like this:
let chart = new ApexCharts(
document.querySelector("#audience-reach-chart-container"),
options
);
chart.render();
}
At first I thought I needed to destroy / empty out the chart at the beginning of the function, but since the function creates a new instance of the chart every time it's invoked, there's nothing to target and destroy.
Also, when I console.log the data being received by the populateBarChart function, I can see the updated data is being received. So the issue must be that the chart is rendering based on the old data, and I need to control the sequence of events?
I can't figure out how to go about this within the code design. Any insights would be greatly appreciated.
You're absolutely right that this can be solved using callbacks, promises and even async/await.
However, this problem statement may have a nuance e.g. if you're using <select> dropdowns, the event listener registered on them could be slightly incorrect. OR, if the ApexCharts library's behavior is to withhold rendering for some reason, then it'll be hard to say so without going through the documentation. Hence, an example on codesandbox or jsfiddle can help us.
Let's look at one possible solution - Ideally, when using promises, we should do something like this:
let container = document.querySelector("#audience-reach-chart-container");
const ajaxCallOne = () => {
// Make API call for first data set (data1).
// No need to invoke the next API call. Just ensure it returns a promise object
}
const ajaxCallTwo = () => {
// Make second API call for another set of data ('data2').
// Sit still! No need to do anything here as long as this function returns a promise
}
// Run this when the user hits 'render' button
Promise.all([ajaxCallOne, ajaxCallTwo]).then((values) => {
console.log(values);
// values[0] holds data1
// values[1] holds data2
let options = doSomethingOnThese(values[0], values[1]);
// Populate the BarChart here
let chart = new ApexCharts(container, options);
chart.render();
});
I've created a sample app(https://codesandbox.io/s/unruffled-cookies-tbcerp?file=/src/index.js) to mimic your usecase here:
// https://codesandbox.io/s/unruffled-cookies-tbcerp?file=/src/index.js
(async function () {
var options = {
chart: {
height: 350,
type: "bar"
},
dataLabels: {
enabled: false
},
series: [],
title: {
text: "Ajax Example"
},
noData: {
text: "Loading..."
}
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
try {
const ajaxCallOne = await fetch(
"https://my-json-server.typicode.com/apexcharts/apexcharts.js/yearly"
);
const ajaxCallTwo = await fetch(
"https://my-json-server.typicode.com/apexcharts/apexcharts.js/yearly2"
);
let data1 = await ajaxCallOne.json();
let data2 = await ajaxCallTwo.json();
console.log(data1, data2);
chart.updateSeries([
{
name: "Sales",
data: data1
},
{
name: "Sales2",
data: data2
}
]);
} catch (e) {
console.log(e);
}
})();