I'm working on a large existing Angular application that has a requirement for a dashboard. I've added fusion charts via https://github.com/SunGard-Labs/angular-fusioncharts/. It is looking good but I'm concerned at the complexity of the javascript I'm writing.
I need 'drill down' on a multi-series chart (which has variable aggregation) to open a tabular report listing the rows corresponding to the data aggregated into the selected bar. I've wired in a link function that opens the relevant page with the filters set depending upon the parameters so it all works but corrupts the global name space with a function (_ARD) to call back into the chart service and feels like I'm jumping through too many hoops to achieve the goal. Is there a better way to do this?
angular.module('dhuiApp').factory(
'FCCharts', function () {
var charts = {};
var factory = {
charts: charts
};
function specializeChart(existing_name, specialization_name){
var newChart = jQuery.extend(true, {}, charts[existing_name]);
newChart.name = specialization_name;
charts[specialization_name] = newChart;
}
charts.multi_series = {
name: 'multi_series',
chart: {
caption: "Caption",
xAxisName: "X Axis",
yAxisName: "Y Axis",
theme: "fint"
},
categories: [
{
category: []
}
],
dataset: [],
seriesNames: [],
categoryLabels: [],
makeSeriesItem: function(categoryIdx, seriesIdx){
// Make fc link string so we can do our own drill down
// Use _ARD global function to get back into this service
var categoryLabel = this.categoryLabels[categoryIdx];
var seriesName = this.seriesNames[seriesIdx];
var params = [this.name, categoryLabel, seriesName];
return {
value: 0,
link: 'j-_ARD-'+params.join('-')
};
},
newCategory: function (label){
// confused naming - fc has an extra 'category' layer under categories
var seriesIdx, series;
var category = {label: label};
var categoryIdx = this.categoryLabels.length; // about to add it
this.categories[0].category.push(category);
this.categoryLabels.push(label);
// Add category to all series
var num_series = this.dataset.length;
for (seriesIdx = 0; seriesIdx < num_series; seriesIdx++){
series = this.dataset[seriesIdx];
series.data.push(this.makeSeriesItem(categoryIdx, seriesIdx));
}
},
newSeries: function(series_name){
var series = {
seriesname: series_name,
data: []
};
var categoryIdx;
var seriesIdx = this.seriesNames.length; // about to add it
var num_categories = this.categories[0].category.length;
for (categoryIdx = 0; categoryIdx < num_categories; categoryIdx++){
series.data.push(this.makeSeriesItem(categoryIdx, seriesIdx));
}
this.dataset.push(series);
this.seriesNames.push(series_name);
},
getCategory: function(dt, aggregation){
var dt_format = {
Day: 'shortDate',
Week: 'ww/YY',
Month: 'MMM/YY'
}[aggregation] || 'MMM/YY';
return dt.format(dt_format);
}
};
specializeChart('multi_series', 'uploads');
charts.uploads.populate = function(report, from_dt, to_dt, aggregation, linkFn){
var i;
var num_rows = report.rows.length;
var row, series_name, category_label;
var series_data;
// do the preamble
this.chart.caption = report.title || 'Untitled';
this.chart.xAxisName = aggregation;
this.chart.yAxisName = this.chart.caption;
// clear any existing data
this.linkFn = linkFn;
this.categoryLabels = [];
this.seriesNames = [];
this.categories[0].category = [];
this.dataset = [];
// Extract the data from the rows
for (i = 0; i < num_rows; i++){
row = report.rows[i];
category_label = this.getCategory(row['Added On'], aggregation);
if (this.categoryLabels.indexOf(category_label) == -1){
this.newCategory(category_label);
}
series_name = row['Type'];
if (this.seriesNames.indexOf(series_name) == -1){
this.newSeries(series_name);
}
series_data = this.dataset[this.seriesNames.indexOf(series_name)].data;
series_data[this.categoryLabels.indexOf(category_label)].value += 1
}
};
factory.getChart = function(chart_name) {
return charts[chart_name];
};
factory.linkClicked = function(s){
var params = s.split('-');
console.log(params[0]);
console.log(charts[params[0]]);
charts[params[0]].linkFn(params.slice(1));
};
return factory;
});
/*
Not having the event listeners working (might be a better solution if I could get them working):
_ARD is used in the link text as the fusion chart function.
This is nasty global pollution to link the angular scope to the chart data and I'd love a better method.
*/
function _ARD(s){
var fc_charts = angular.element(document.querySelector('.ng-scope')).injector().get('FCCharts');
fc_charts.linkClicked(s);
}
I have wondered about rewriting the angular directives so that I use a specialization of the fusioncharts object as my 'intelligent chart object' (exposing the fusion chart object in the service) but with two angular fusioncharts packages already out there, a third seems like overkill.
I'm going to be adding quite a few charts with similar drill down to tables representing the selected underlying data (sometimes through the intermediary of drill down charts) which is why I've set this up so that I can add more charts.