CSV parsing and generating charts in lightning components

CSV PARSING AND GENERATING CHARTS IN LIGHTNING COMPONENTS

Lightning offers advantage over classic that it allows a lot of client processing thus saving the server side actions. One of the scenarios that I would like to cover is parsing CSV file and generating reports from the data fetched without any server side actions.

Let us consider, I have a CSV file (comma separated ) that contains information about students who play different sports in different age groups.

Let us first learn how to generate a Donut Chart in a lightning component to depict the count of the number of students playing different sports.

Download the d3.js file from https://d3js.org/d3.v4.js and upload as a static resource in your Salesforce Org. D3.js a JavaScript library used for visualization purpose. D3 can be used right from generating HTML tables to bar graphs.

Now let’s get started with the lightning component.

This blog will demonstrate how to:

  1. Parse a CSV file
  2. Generate a Donut Chart
  3. Generate a Grouped Bar chart

Generate a CSV file based on your requirement. Consider a file as shown below:

Capture-1

First we will parse the CSV file, to generate the required format necessary for generating a Donut Chart.

We will calculate the count of student playing different sports.

[sourcecode language=”java”]
<aura:component implements=”force:appHostable, flexipage:availableForAllPageTypes, flexipage:availableForRecordHome, force:hasRecordId, forceCommunity:availableForAllPageTypes, force:lightningQuickAction” access=”global”>

<ltng:require scripts=”/resource/lodash/lodash/lodash.js” />

<ltng:require scripts=”{!$Resource.d3}” />
<div class=”scroll” style=”width:100%”>
<div style=”padding-right:5%”>
<!– csv file input –>
<lightning:input type=”file” label=”” name=”Mapping” multiple=”false” accept=”.csv” onchange=”{!c.readCSV}” /></div>
<div style=”width:960px” class=”slds-scrollable” id=”chart”></div>
<div style=”width:960px” class=”slds-scrollable” id=”piechart”></div>
</div>
</aura:component>
[/sourcecode]

Controller.js

[sourcecode language=”java”]
({
readCSV: function(component, event, helper) {

var filename = event.getSource().get(“v.files”);

var textdata;
var reader = new FileReader();
var infolst = [];
reader.onload = function() {

var text = reader.result; /*Get the data stored in file*/
console.log(reader.result.substring(0, 200));
console.log(‘Data from CSV file’ + text);
textdata = text;
var rows = textdata.split(‘n’); /*Spilt based on new line to get each row*/
console.log(‘File header’ + rows[0]);
component.set(“v.showSpinner”, false);

/* Ignore the first row (header) and start from second*/
for (var i = 1; i <span data-mce-type=”bookmark” id=”mce_SELREST_start” data-mce-style=”overflow:hidden;line-height:0″ style=”overflow:hidden;line-height:0″ ></span>< rows.length; i = i + 1) {
console.log(‘Length’, +rows.length); //total number of rows in the file including header
/*Spilt based on the comma*/
var cells = rows[i].split(‘,’);
console.log(‘One row’ + cells);
console.log(‘Row length’ + cells.length);

if(cells.length!=1){
var sports=cells[3].split(‘r’);
var cellinfo = {
‘StudentNo’: cells[0],
‘Name’: cells[1],
‘Date’: cells[2],
‘Sports’: sports[0],
};

infolst.push(cellinfo);
component.set(“v.data”,infolst);
}

}

helper.generatechartdata(component);

};
if (filename[0] !== undefined && filename[0] !== null && filename[0] !== ”) {
reader.readAsText(filename[0]);
}

},

[/sourcecode]

Helper.js

[sourcecode language=”java”]
({
displaydata: function(component, data) {

d3.select(“#chart”)
.select(“svg”)
.remove();

var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},

width = 800 – margin.left – margin.right,
height = 200 – margin.top – margin.bottom;

var x0 = d3.scaleBand()
.rangeRound([0, width]);

var x1 = d3.scaleBand();

var y = d3.scaleLinear()
.rangeRound([height, 0]);

var xAxis = d3.axisBottom(x0)
.tickSize(0);

var yAxis = d3.axisLeft(y);

var color = d3.scaleOrdinal()
.range([“#CD853F”, “#6A5ACD”, “#2F4F4F”, “#6B8E23”]);

var svg = d3.select(‘#chart’)
.append(“svg”)
.attr(“width”, width + margin.left + margin.right)
.attr(“height”, height + margin.top + margin.bottom)
.append(“g”)
.attr(“transform”, “translate(” + margin.left + “,” + margin.top + “)”);

var categoriesNames = data.map(function(d) {
return d.type;
});
var rateNames = data[0].values.map(function(d) {
return d.agerange;
});
console.log(categoriesNames);

x0.domain(categoriesNames);
x1.domain(rateNames)
.range([0, x0.bandwidth()]);

y.domain([0, d3.max(data, function(categorie) {
return d3.max(categorie.values, function(d) {
return d.count;
});
})]);

svg.append(“g”)
.attr(“class”, “x axis”)
.attr(“transform”, “translate(0,” + height + “)”)
.call(xAxis);

svg.append(“g”)
.attr(“class”, “y axis”)
.style(‘opacity’, ‘0’)
.call(yAxis)
.append(“text”)
.attr(“transform”, “rotate(-90)”)
.attr(“y”, 6)
.attr(“dy”, “.71em”)
.style(“text-anchor”, “end”)
.style(‘font-weight’, ‘bold’)
.text(“Value”);

svg.select(‘.y’)
.transition()
.duration(500)
.delay(1300)
.style(‘opacity’, ‘1’);

var slice = svg.selectAll(“.slice”)
.data(data)
.enter()
.append(“g”)
.attr(“class”, “g”)
.attr(“transform”, function(d, i) {
console.log(i);
console.log(x0(d.type));
var s = x0(d.type) + 15 * i;
return “translate(” + s + “,0)”;
});

slice.selectAll(“rect”)
.data(function(d) {
return d.values;
})
.enter()
.append(“rect”)
.attr(“width”, x1.bandwidth())
.attr(“x”, function(d) {
return x1(d.agerange);
})
.style(“fill”, function(d) {
return color(d.agerange)
})
.attr(“y”, function(d) {
return y(0);
})
.attr(“height”, function(d) {
return height – y(0);
})
.on(“mouseover”, function(d) {
d3.select(this)
.style(“fill”, d3.rgb(color(d.agerange))
.darker(2));
})
.on(“mouseout”, function(d) {
d3.select(this)
.style(“fill”, color(d.agerange));
});

slice.selectAll(“rect”)
.transition()
.delay(function(d) {
return Math.random() * 1000;
})
.duration(1000)
.attr(“y”, function(d) {
return y(d.count);
})
.attr(“height”, function(d) {
return height – y(d.count);
});

//Legend
var legend = svg.selectAll(“.legend”)
.data(data[0].values.map(function(d) {
return d.agerange;
})
.reverse())
.enter()
.append(“g”)
.attr(“class”, “legend”)
.attr(“transform”, function(d, i) {
var h = (i * 20) – 20;
return “translate(20,” + h + “)”;
})
.style(“opacity”, “0”);

legend.append(“rect”)
.attr(“x”, width – 10) //18
.attr(“width”, 10) //18
.attr(“height”, 10) //18
.style(“fill”, function(d) {
return color(d);
});

legend.append(“text”)
.attr(“x”, width – 10) //18
.attr(“y”, 5) // 9
.attr(“dy”, “.35em”)
.style(“text-anchor”, “end”)
.text(function(d) {
return d;
});

legend.transition()
.duration(500)
.delay(function(d, i) {
return 1300 + 100 * i;
})
.style(“opacity”, “1”);

component.set(“v.showSpinner”, false);

},

generatechartdata: function(component, event, helper) {
var infolst = component.get(“v.data”);
var aMap = {};

function addValueToKey(key, value) {
aMap[key] = aMap[key] || [];
var tempArr = aMap[key];
tempArr.push(value);
aMap[key] = tempArr;
aMap[key] = _.sortBy(aMap[key], function(x) {
return x.StudentNo;
});
}
for (var i = 0; i < infolst.length; i = i + 1) {
addValueToKey(infolst[i].StudentNo, infolst[i]);
}
console.log(‘Map’ + JSON.stringify(aMap));
var studentlist = [];
var key = Object.keys(aMap);
console.log(‘Keys’ + key);
var today_date = new Date();
var today_year = today_date.getFullYear();
//console.log(today_date);
for (var i = 0; i < key.length; i = i + 1) {
var itemlist = aMap[key[i]];
var age;
var agerange;

var Sportslist = [];
_.forEach(itemlist, function(o) {
Sportslist.push(o.Sports);

});

var sports;
for (var j = 0; j < itemlist.length; j = j + 1) { var type; console.log(‘Fields’ + itemlist[j].Date); if (itemlist[j].Date !== undefined) { var dob = itemlist[j].Date.split(‘/’); } // var newdate=new Date(dob[2], dob[1] * 1 , dob[0]); age = today_year – dob[2]; console.log(‘Age’ + age); if (age >= 0 && age <= 5) { agerange = ‘0-5’; } else if (age > 5 && age <= 10) { agerange = ‘6-10′; } else if (age > 10 && age <= 15) { agerange = ’11-15’; } else if (age > 15 && age <= 18) {
agerange = ’15-18′;
}

sports = itemlist[j].Sports;

}

var Student = {
‘StudentNo’: key[i],
‘ageRange’: agerange,
‘sports’: sports,

};

studentlist.push(Student);

}

var rangelist = [‘0-5’, ‘6-10′, ’11-15′, ’15-18’];
var typelist = [‘Cricket’, ‘Football’, ‘Basketball’, ‘Hockey’];
var count = [];
var countdata = [];
var data = [];

var objdatalist = [];
var pielist = [];

for (var m = 0; m < typelist.length; m = m + 1) {

var objlist = [];

for (var n = 0; n <span data-mce-type=”bookmark” id=”mce_SELREST_start” data-mce-style=”overflow:hidden;line-height:0″ style=”overflow:hidden;line-height:0″ ></span>< rangelist.length; n = n + 1) {

// count[m]=[];
count[n] = _.filter(studentlist, function(o) {
return (o.ageRange == rangelist[n] && o.sports == typelist[m]);

})
.length;

data.push(count[n]);
var Values = {
‘agerange’: rangelist[n],
‘count’: count[n],
};

objlist.push(Values);

}

var objdata = {
‘type’: typelist[m],
‘values’: objlist
};

objdatalist.push(objdata);

countdata[m] = _.filter(studentlist, function(o) {
return (o.sports == typelist[m]);

})
.length;
var piedata = {
‘type’: typelist[m],
‘count’: countdata[m]
};
pielist.push(piedata);

}
console.log(‘Data’ + JSON.stringify(objdatalist));

this.displaydata(component, objdatalist);
this.displaypiechart(component, pielist);
},

displaypiechart: function(component, dataset) {

var pie = d3.pie()
.value(function(d) {
return d.count;
})
(dataset);

var w = 300,
h = 300;

var outerRadius = w / 2;
var innerRadius = 100;

var color = d3.scaleOrdinal()
.range([“#CD853F”, “#6A5ACD”, “#2F4F4F”, “#6B8E23”]);

var arc = d3.arc()
.outerRadius(outerRadius)
.innerRadius(innerRadius);

var svg = d3.select(‘#piechart’)
.append(“svg”)
.attr(“width”, w)
.attr(“height”, h)
.append(“g”)

.attr(“transform”, “translate(” + w / 2 + “,” + h / 2 + “)”);

var path = svg.selectAll(‘path’)
.data(pie)
.enter()
.append(‘path’)
.attr(“d”, arc)
.attr(
“fill”,
function(d, i) {
return color(d.data.type);
}
);

path.transition()
.duration(1000)
.attrTween(‘d’, function(d) {
var interpolate = d3.interpolate({
startAngle: 0,
endAngle: 0
}, d);
return function(t) {
return arc(interpolate(t));
};
});

var restOfTheData = function() {

var text = svg.selectAll(‘text’)
.data(pie)
.enter()
.append(“text”)
.transition()
.duration(200)
.attr(“transform”, function(d, i) {

return “translate(” + arc.centroid(d) + “)”;
})
.attr(“dy”, “.4em”)
.attr(“text-anchor”, “middle”)
.text(function(d) {

return d.data.count;
})
.style(
“fill”, ‘#fff’,
“font-size”, ’10px’
);

var legendRectSize = 20;
var legendSpacing = 7;
var legendHeight = legendRectSize + legendSpacing;

var legend = svg.selectAll(‘.legend’)
.data(color.domain())
.enter()
.append(‘g’)
.attr(

“transform”,
function(d, i) {
//Just a calculation for x & y position

return ‘translate(-35,’ + ((i * legendHeight) – 65) + ‘)’;
}
)
.attr(“class”, ‘legend’);

legend.append(‘rect’)
.attr(
“width”, legendRectSize
)
.
attr(“height”, legendRectSize

)
.attr(“rx”, “20”)
.attr(“ry”, “20”)
.style(
“fill”,
function(d) {
return color(d);
}

);

legend.append(‘text’)
.attr(
“x”, “30”

)
.attr(“y”, “15”)
.text(function(d) {
return d;
})
.style(
“fill”, ‘#929DAF’,
“font-size”, ’14px’
);
};

setTimeout(restOfTheData, 1000);
}
})

[/sourcecode]

Upload the same file format as shown above:

The following Donut chart will be generated. The chart below shows the count of  students playing a particular sports.

Similarly we can also generate Bar Graph similar to the chart above. The graph below depicts the number of students who play a particular sports grouped based on their age group.

Now if we look into the code closely, you will observe that the function generatechartdata() will create a JSON object required to be passed to the d3 code to get the graph in the desired format.

For donut chart the data generated will be similar  in the form:

For Bar Graph the JSON Object generated would be in the format below:

Capture6

Some of the D3 function used are:

d3.arc – Used to generate an arc for the donut chart.

d3.pie –  Used to generate a pie with the JSON data returning another JSON object as shown below.

Capture7
transform.translate(x, y) – Returns a transform whose translation tx1 and ty1 is equal to tx0 + x and ty0 + y, where tx0 and ty0 is this transform’s translation.

d3.legend – Is used to add a legend to the d3 chart.

For generating d3 charts of different types : https://bl.ocks.org/

For complete information about  d3 functions: https://github.com/d3/d3/blob/master/API.md

Find the complete code on:  https://github.com/AryaRamani/d3chartgeneration

So next time you have a functionality to generate reports in your custom lightning components, just refer to the d3.js library.

1 thought on “CSV parsing and generating charts in lightning components”

  1. Nice blog right here! Additionally your site so much up fast! What host are you the use of? Can I am getting your associate link to your host? I wish my site loaded up as fast as yours lol

Leave a Comment

Your email address will not be published. Required fields are marked *

Recent Posts

salesforce world tour essentials dubai
Salesforce World Tour Essentials Dubai | May 16, 2024
simplifying npl the magic of natural language processing
Simplifying NLP: The Magic of Natural Language Processing
streamlining-salesforce deployment with gearset a devops revolution Part 2
Streamlining Salesforce Deployment with Gearset: A DevOps Revolution (Part 2)
streamlining-salesforce deployment with gearset a devops revolution Part 1
Streamlining Salesforce Deployment with Gearset: A DevOps Revolution (Part1)
apache ant with provar automation tool
Apache Ant With Provar Automation Tool
Scroll to Top