I am using Tableau to create a dashboard. I needed the power of D3 in my dashboard, and so I created a Tableau Extension that creates the viz. Here's the skeleton of the JS that uses the Tableau Extensions API functionality:
$(document).ready(function () {
console.log('were in again');
tableau.extensions.initializeAsync().then(function () {
// To get dataSource info, first get the dashboard.
const dashboard = tableau.extensions.dashboardContent.dashboard;
const worksheets = dashboard.worksheets;
let underlyingDataFetchPromises = [];
let dataObjects = {};
let worksheetNames = [];
// Save Promises to Array + Add Event Listeners
dashboard.worksheets.forEach(worksheet => {
worksheetNames.push(worksheet.name);
underlyingDataFetchPromises.push(worksheet.getUnderlyingDataAsync());
// add event listener too...
worksheet.addEventListener(tableau.TableauEventType.FilterChanged, (filterEvent) => {
clearChart();
drawChart(dataObjects.dataA, dataObjects.dataB);
});
});
// Maps dataSource id to dataSource so we can keep track of unique dataSources.
Promise.all(underlyingDataFetchPromises).then(fetchedData => {
// loop over the 2 fetchedData arrays [(1) dataA, (2) dataB]
fetchedData.forEach((dataTable, i) => {
let data = dataTable.data;
let columns = dataTable.columns;
let formattedData = formatData(data, columns);
dataObjects[worksheetNames[i]] = formattedData;
});
// currently requires worksheets named "dataA" and "dataB"... not flexible atm
drawChart(dataObjects.dataA, dataObjects.dataB);
});
}, function (err) {
// Something went wrong in initialization.
console.log('Error while Initializing: ' + err.toString());
});
});
cleanData()
and drawChart()
are functions I wrote myself that draw the D3 visualizations, these are working. My event handler "works" a bit, in the sense that it is properly triggered when a fitler in the dashboard is changed.
However, my problem is that, when drawChart()
is called from within the eventListener, my chart is drawn with the same data, not the new data resulting from the filter being toggled. I understand inherently why this is the case - the eventListener is not refetching data, only redrawing the chart.
My question is then, how do I re-fetch new data to re-draw my viz correctly after the filters are toggled? And also, is there a function that will return to me the filter names + values, that I can use to improve the functionality of my extension? It would be good to have all of the dashboard filters + values in JS to use in this code.
Thanks in advance for any help with this!
The following code refreshes the data source "ABC" and reloads the workbook-
/**
* This function is designed to refresh a specific Tableau data source named "ABC"
* within a Tableau dashboard extension. It initializes the extension, finds all
* data sources, and refreshes the target data source.
*/
function refreshMySql() {
tableau.extensions.initializeAsync().then(() => {
// Fetch data sources from each worksheet and consolidate them.
const dashboard = tableau.extensions.dashboardContent.dashboard;
let dataSourceFetchPromises = dashboard.worksheets.map(worksheet => worksheet.getDataSourcesAsync());
let dashboardDataSources = {};
Promise.all(dataSourceFetchPromises).then(fetchResults => {
fetchResults.forEach(dataSourcesForWorksheet => {
dataSourcesForWorksheet.forEach(dataSource => {
// Process each data source only once.
if (!dashboardDataSources[dataSource.id]) {
dashboardDataSources[dataSource.id] = dataSource;
// Check if the data source name matches "ABC" and refresh it.
if (dataSource.name === "ABC") {
refreshDataSource(dataSource);
}
}
});
});
}).catch(err => {
console.error('Error while fetching data sources: ' + err.toString());
});
}).catch(err => {
console.error('Error while initializing Tableau Extensions API: ' + err.toString());
});
}
/**
* Refreshes the given data source.
* @param {object} dataSource - The data source to be refreshed.
*/
function refreshDataSource(dataSource) {
dataSource.refreshAsync().then(() => {
console.log(`${dataSource.name}: Refreshed Successfully`);
}).catch(err => {
console.error(`Error refreshing data source ${dataSource.name}: ` + err.toString());
});
}
// Execute the refresh function when the document is ready.
$(document).ready(refreshMySql);