Laravel - How to Display both count and percentage (%) in chartjs pie chart - laravel

In my Laravel-5.8, I have used chartjs to display total male and female counts (total number of male and female)
as shown below:
Controller:
public function report()
{
$userCompany = Auth::user()->company_id;
$userId = Auth::user()->id;
$gender_record = HrEmployee::selectRaw('count(gender_id) as count,gender_id, if (gender_id = 1, "Male", "Female") as gender')->where('company_id', $userCompany)->groupBy('gender_id')->get();
$gender_data = [];
foreach($gender_record as $row) {
$gender_data['gender_label'][] = $row->gender;
$gender_data['gender_data'][] = (int) $row->count;
}
$gender_data['gender_chart_data'] = json_encode($gender_data);
return view('report-default', $gender_data);
}
view
<div class="card-body">
<canvas id="empGender" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 100%;"></canvas>
</div>
<script src="{{ asset('theme/adminlte3/plugins/chart.js/Chart.min.js') }}"></script>
<script>
$(function () {
/* ChartJS
* -------
* Charts using ChartJS
*/
//-------------
//- GENDER-EMPLOYEE CHART -
//-------------
// Get context with jQuery - using jQuery's .get() method.
var genderData = JSON.parse(`<?php echo $gender_chart_data; ?>`);
var genderChartCanvas = $('#empGender').get(0).getContext('2d')
var genderData = {
labels: genderData.gender_label,
datasets: [
{
data: genderData.gender_data,
backgroundColor : [
'#f56954',
'#00a65a',
'#f39c12',
'#00c0ef',
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
],
borderWidth: 1
}
]
}
var genderOptions = {
maintainAspectRatio : true,
responsive : true,
cutoutPercentage: 80,
}
//Create pie or douhnut chart
var genderChart = new Chart(genderChartCanvas, {
type: 'doughnut',
data: genderData,
options: genderOptions
})
})
</script>
What I have as shown above only displays Total counts per Gender as shown above.
How do I make it also to include the percentage per Gender?
Thank you.

You can do this by defining a tooltip label callback function as follows.
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
var value = data.datasets[0].data[tooltipItem.index];
var total = data.datasets[0].data.reduce((a, b) => a + b, 0);
var pct = 100 / total * value;
var pctRounded = Math.round(pct * 10) / 10;
return value + ' (' + pctRounded + '%)';
}
}
}
Please have a look at below runnable code snippet.
new Chart('empGender', {
type: 'doughnut',
data: {
labels: ['Male', 'Female'],
datasets: [{
backgroundColor: ['#f56954', '#00a65a'],
borderColor: ['rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)'],
data: [17, 7],
borderWidth: 1
}]
},
options: {
maintainAspectRatio: true,
responsive: true,
cutoutPercentage: 80,
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
var value = data.datasets[0].data[tooltipItem.index];
var total = data.datasets[0].data.reduce((a, b) => a + b, 0);
var pct = 100 / total * value;
var pctRounded = Math.round(pct * 10) / 10;
return value + ' (' + pctRounded + '%)';
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="empGender" height="100"></canvas>

Related

How to hide/show bars differentiate by a colors and by click on labels, using chartjs bar charts?

I am using chartjs to show one month attendance data, what I want to acheive is to show each day data with different colors like Present, absent and leave. I want labels on top and by click on that label user should be able to hide/show that particular label data, below is my code.
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- HTML -->
<canvas id="chartJsDiv"></canvas>
<script>
function attendanceSummaryChart3(attendence)
{
var datasets = [];
var labels_data = [];
var num_of_hour = [];
var bar_colours = [];
var border_colr = [];
var real_data_count = attendence.length;
for (var i = 0; i < real_data_count; i++) {
var mydate = new Date(attendence[i].date);
var month = ["Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec"][mydate.getMonth()];
var str = mydate.getDate() + ' ' +month;
labels_data.push(str);
if(parseFloat(attendence[i].hours.replace(':','.')) < 6.00)
{
if(attendence[i].is_leave != null)
{
var applied_leave_type = attendence[i].is_leave_applied.leave_type.slug;
if(applied_leave_type == "work-from-home")
{
// Full hours in case work from home
num_of_hour.push('9');
bar_colours.push('RGB(25, 135, 84)');
border_colr.push('rgba(3, 126, 25, 0.85)');
// creating datasets
datasets.push({label: 'Work From Home', data: '9', backgroundColor: 'RGB(25, 135, 84)' });
}
else
{
// Leave applied
if(parseFloat(attendence[i].hours.replace(':','.')) == 0)
{
num_of_hour.push('-9');
bar_colours.push('RGB(25, 135, 84)');
border_colr.push('rgba(14, 8, 191, 0.8)');
// creating datasets
datasets.push({label: 'On Leave', data: '-9', backgroundColor: 'RGB(25, 135, 84)'});
}
else
{
num_of_hour.push(parseFloat(attendence[i].hours.replace(':','.')));
bar_colours.push('RGB(25, 135, 84)');
border_colr.push('rgba(14, 8, 191, 0.8)');
// creating datasets
datasets.push({label: 'Half Leave', data: parseFloat(attendence[i].hours.replace(':','.')), backgroundColor: 'RGB(25, 135, 84)'});
}
}
}
else
{
if(parseFloat(attendence[i].hours.replace(':','.')) == 0)
{
// If absent and no leave is applied
num_of_hour.push('-9');
bar_colours.push('RGB(255, 0, 0)');
border_colr.push('rgba(6, 108, 166, 0.8)');
// creating datasets
datasets.push({label: 'Absent (No Leave Applied)', data: '-9', backgroundColor: 'RGB(255, 0, 0)' });
}
else
{
// If present and didn't complete 06 hours in office
num_of_hour.push(parseFloat(attendence[i].hours.replace(':','.')));
bar_colours.push('RGB(255, 0, 0)');
border_colr.push('rgba(255, 99, 132, 1)');
// creating datasets
datasets.push({label: 'Present (Half Time)', data: parseFloat(attendence[i].hours.replace(':','.')), backgroundColor: 'RGB(255, 0, 0)' });
}
}
}
else
{
// Full hours
num_of_hour.push(parseFloat(attendence[i].hours.replace(':','.')));
bar_colours.push('RGB(0, 255, 0)');
border_colr.push('rgba(75, 192, 192, 1)');
// creating datasets
datasets.push({label: 'Present', data: parseFloat(attendence[i].hours.replace(':','.')), backgroundColor: 'RGB(25, 135, 84)' });
}
}
console.log(datasets);
const ctx = document.getElementById('chartJsDiv').getContext('2d');
const myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels_data,
datasets: [
{
label: ['Attendence'],
data: num_of_hour,
backgroundColor: bar_colours,
borderColor: border_colr,
}
]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
},
});
}
</script>
I am attaching a screenshot below currently I only have one label but I want multiple labels based on multiple colors bar
You need to use a second dataset to achieve this:
Then you can fill null values in both datasets where you dont use the values of that dataset, so null values on the places where value is negative in positive dataset and vice versa in other one.
Then you can use the property skipNull to make it so the bars dont take up space:
const options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, null, 3, 5, null, 3],
backgroundColor: 'green'
},
{
label: '# of Points',
data: [null, -11, null, null, -3, null],
backgroundColor: 'red'
}
]
},
options: {
skipNull: true
}
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
</body>
should be this part
label: ['Attendence', 'newLabel', 'xxx'],
under
const ctx = document.getElementById('chartJsDiv').getContext('2d');

How to solve Chart js mismatched x-axes label and value dynamically in Laravel?

I have encountered a problem using chart js when applying it dynamiccally, which means I get a data from my database and output a bar graph using Chart JS. I found this example which works when a value is 0, but on my situation some data on a specific year cannot be found yet on my database, which leads to a null value. How can I set this empty or null value to zero so that I can achieve this example https://jsfiddle.net/17mw40rx/1/. I want also to show my JS code which I copied from the same sample and applied it to my project. The script works fine but when a year data is missing let say no record found in 2002 and 2005, the data are filled automatically by a wrong year data. I hope you understand my problem. Please I need help from someone about this.
JS Script
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.js"></script>
<script>
var year = ['2000','2001','2002','2003','2004','2005','2006','2007','2008','2009','2010','2011','2012','2013','2014','2015','2016','2017','2018','2019','2020'];
var female = <?php echo $female; ?>;
var male = <?php echo $male; ?>;
var entranceDataset = {
label: 'Female',
type: 'bar',
yAxesID : "y-axis-1",
data: female,
backgroundColor: 'rgba(0, 204, 0, 0.2)',
borderColor: 'rgba(0, 204, 0,1)',
borderWidth: 1
};
var dataset = [];
dataset.push(entranceDataset);
var exitDataset = {
label: 'Male',
type: 'bar',
yAxesID : "y-axis-1",
data: male,
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
};
dataset.push(exitDataset);
var ctx = $('#enrollcanvas');
mainThroughputChart = new Chart(ctx, {
type: 'bar',
data: {
labels: year,
datasets: dataset
},
options: {
scales: {
xAxes : [{
gridLines : {
display : false
},
scaleLabel: {
display: true,
labelString: 'Year'
}
}]
},
}
});
</script>
Laravel Controller and query
$female = Enroll::select(DB::raw("SUM(tot_enroll) as count"))
->orderBy(DB::raw('sy'))
->groupBy(DB::raw("(sy)"))
->where('gender','=', 'Female')
->get()->toArray();
$female = array_column($female, 'count');
$male = Enroll::select(DB::raw("SUM(tot_enroll) as count"))
->orderBy(DB::raw('sy'))
->groupBy(DB::raw("(sy)"))
->where('gender','=', 'Male')
->get()->toArray();
$male = array_column($male, 'count');
return view('home')
->with('female',json_encode($female,JSON_NUMERIC_CHECK))
->with('male',json_encode($male,JSON_NUMERIC_CHECK));
Blade Page
<canvas id="enrollcanvas" name="enrollcanvas" height="280" width="600"></canvas>
Actual Bar Chart Result
Database Table where the bar chart is based from
I think the problem with mismatched data of $female and $male with JS year variable.
var year = ['2000','2001','2002','2003','2004','2005','2006','2007','2008','2009','2010','2011','2012','2013','2014','2015','2016','2017','2018','2019','2020'];
var female = <?php echo $female; ?>;
var male = <?php echo $male; ?>;
Pass the '0' if $female OR $male doesn't have value for each year(Let's say 2000). So your $female and $male should be like:
var year = ['2000','2001','2002','2003', '2004'...];
var female = ['0','34', '0','65', '54',...];
var male = ['0','75', '0','34', '0',...];
Update
Try this below code with full snippet of controller side. Replace enroll with your database table name into this query.
$rsltEnrollData = DB::table('enroll')->selectRaw('sy as sy, gender, SUM(tot_enroll) as count')
->groupBy('sy')
->orderBy('sy')
->get();
$arrFemale = array();
$arrMale = array();
$arrYearData = array();
foreach($rsltEnrollData as $key => $objEnrollData){
if(!isset($arrYearData[$objEnrollData->sy])){
$arrYearData[$objEnrollData->sy]['Male'] = 0;
$arrYearData[$objEnrollData->sy]['Female'] = 0;
}
$arrYearData[$objEnrollData->sy][$objEnrollData->gender] = $objEnrollData->count;
$arrFemale = $arrYearData[$objEnrollData->sy]['Female'];
$arrMale = $arrYearData[$objEnrollData->sy]['Male'];
}
Debug
foreach($rsltEnrollData as $key => $objEnrollData){
print('<pre style="color:red;">');
print_r($objEnrollData);
print('</pre>');
}
exit;
this is a snippet of the script in my project. maybe a little different, but maybe someone needs it. and hope it helps in configuring chart js with laravel and database
JAVASCPT
$(document).ready(function() {
var statistics_chart = document.getElementById("myChart").getContext('2d');
fetch("{{url('chart')}}")
.then(response =>response.json())
.then(json=>{
var myChart = new Chart(statistics_chart, {
type: 'line',
data: {
labels: json.labels,
datasets: json.dataset,
},
options: {
legend: {
display: false
},
scales: {
yAxes: [{
gridLines: {
// display: false,
drawBorder: false,
color: '#f2f2f2',
},
ticks: {
beginAtZero: true,
stepSize: 10000,
}
}],
xAxes: [{
gridLines: {
display: false,
tickMarkLength: 15,
}
}]
},
}
});
})
});
Controller
public function chart()
{
$data = Kas::select([
DB::raw("SUM(debit) as total_debit"),
DB::raw("SUM(kredit) as total_kredit"),
DB::raw("MONTH(created_at) as bln"),
// DB::raw("YEAR(created_at) as year")
])
->whereYear('created_at', 2022)
->groupBy([
'bln'
])
->orderBy('bln')
->get();
$arrBln = [1 => 'Jan','Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'];
$totalD = $totalK = [];
foreach ($data as $tot) {
$totalD[$tot->bln] = $tot->total_debit;
$totalK[$tot->bln] = $tot->total_kredit;
}
foreach ($arrBln as $month =>$name){
if(!array_key_exists($month, $totalD)){
$totalD[$month]= 0;
}
if(!array_key_exists($month, $totalK)){
$totalK[$month]= 0;
}
}
ksort($totalD);
ksort($totalK);
return[
'labels' => array_values($arrBln),
'dataset' => [
[
'label' => 'Pemasukan',
'data' => array_values($totalD),
'borderWidth'=> 2,
'backgroundColor'=> 'rgba(63,82,227,.8)',
'borderWidth' => 0,
'borderColor' =>'transparent',
'pointBorderWidth' => 0,
'pointRadius' => 3.5,
'pointBackgroundColor' => 'transparent',
'pointHoverBackgroundColor' => 'rgba(63,82,227,.8)',
],
[
'label' => 'Pengeluaran',
'data' => array_values($totalK),
'borderWidth'=> 2,
'backgroundColor' => 'rgba(254,86,83,.7)',
'borderWidth' => 0,
'borderColor' =>'transparent',
'pointBorderWidth'=> 0,
'pointRadius'=> 3.5,
'pointBackgroundColor'=> 'transparent',
'pointHoverBackgroundColor'=> 'rgba(254,86,83,.8)',
],
]
];
}

How to draw Normalize Stack chart using C3 chart?

I want to draw normalize chart using C3 Chart library.
my current code is
var chart = c3.generate({
bindto: '#column-chart-main',
size: {
height: $('.chart-area').height()
},
data: {
rows:
chartFinalData
,
type: 'bar',
labels: {
format: d3.format('%')
},
colors: chartFinalColors,
transition: {
duration: 100
}
},
zoom: {
enabled: true
},
axis: {
y: {
show: false,
max: 1,
min: 0,
padding: {bottom:0}
},
x: {
type: 'category',
categories: chartFinalBrands
},
rotated: setChartType
},
tooltip: {
format: {
value:d3.format('%')
}
},
legend: {
show: $scope.chartTrans.showHideLegends,
position: 'inset',
inset: {
anchor: legendPosition,
x: 10,
y: 10,
step: legendSteps,
}
}
});
Above code generate simple bar stack chart.
but i need normalize bar stack chart
my current chart is - current-chart
i need chart as per - required-chart
Thanks in advance
If you want a normalised stack bar chart then you need to normalise the data first, c3 doesn't have a chart setting itself to work that out
e.g.
var data = [
['data1', 30, 200, 200, 400, 150, 250],
['data2', 130, 100, 100, 200, 150, 50],
['data3', 230, 200, 200, 300, 250, 250]
]
;
// Normalise
var scount = data.length;
for (var n = 1; n < data[0].length; n++) {
var total = 0;
for (var m = 0; m < scount; m++) {
total += data[m][n];
}
var ratio = 1.0 / total;
for (var m = 0; m < scount; m++) {
data[m][n] *= ratio;
}
}
var chart = c3.generate({
data: {
columns: data,
type: 'bar',
groups: [
['data1', 'data2', 'data3']
]
},
tooltip: {
format: {
value:d3.format('%')
}
},
axis : {
y : {
//max: 0.95, // for some reason this shows the last tick y as 100%, while 1.0 makes the last y tick 110%, don't know why
// thanks to a.n.onymous who figured out this worked better
max: 1,
padding: 0,
tick: {
format: d3.format("%")
}
}
}
});
http://jsfiddle.net/697p6hw5/6/

Chartjs2 Legend Not working

I'm trying to plot a lineal graphic that gets data from ajax using Chartjs 2.5.
This is the main function for this procedure. The main problem is that the legend doesn't appear.
function initDigitalValueChart(divName, ids) {
if ($(divName)) {
/*for (var j=0; j<ids.length; j++) {
console.log(j);
datasetValue[j] = {
fillColor: 'rgba(220,220,220,0.5)',
strokeColor :'rgba(220,220,220,1)',
title :'2013',
data : [Math.round(Math.random() * 1),Math.round(Math.random() * 1)]
}
}*/
var datasetValue = [];
var calls = [];
for (var j = 0; j < ids.length; j++) {
var labels = []
URL = "*****" + ids[j];
calls.push($.getJSON(URL, function(response) {
var values = []
if (response != null) {
$.each(response, function(index, data) {
labels.push(data["timestamp"]);
values.push(data["value"]);
title = data["sensorName"]
});
datasetValue.push({
label: title,
lineTension: 0,
borderColor: "rgba(" + Math.floor(Math.random() * 255) + ", " + Math.floor(Math.random() * 255) + ", " + Math.floor(Math.random() * 255) + ", 0.70)",
backgroundColor: "transparent",
data: values,
radius: 0,
pointHitRadius: 0,
steppedLine: true,
});
}
}))
}
$.when.apply($, calls).then(function() {
var ctx = document.getElementById('digital').getContext("2d");
var lineChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: datasetValue
},
options: {
legend: {
display: true,
labels: {
fontColor: "white",
fontSize: 18
}
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Data Hora'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Valor'
}
}]
}
}
});
//document.getElementById('digital-legend').innerHTML = lineChart.generateLegend();
});
}
}
I tried to add the position: 'top' option but in this case the console returns
Uncaught TypeError: Cannot read property 'call' of undefined
Has anyone else had problems with this?

How to make a multiple table header

I am trying to make a table with 2 headers merged. At the moment i made 2 seperate tables with 2 seperate headers and it looks okay, but when the table width expands the first table header does not expand. How can i merge the 2 headers or can i make 1 table with 2 tableheaders. Please see picture (how the table is at the moment with 2 tableheaders)
Here is my code :
function createPDF(){
/** START PDF INSTANCE */
//var doc = new jsPDF('p', 'pt');
var doc = new jsPDF('l', 'pt');
var row = 80;
addPdfHeader(doc, row, vm.translate("REPORT.LEGALFORMS ")+" "+vm.activeCompanyYear);
doc.setFillColor(33, 150, 243);
var columns = [ " ",vm.activeCompanyYear,vm.activeCompanyYear-1,vm.activeCompanyYear-2];
var rows = [];
var description = "";
for(var j=0; j<vm.reportData.length; j++){
var obj = vm.reportData[j];
description = obj.descriptionEng;
if(description == "total"){
description = vm.translate("REPORT.REGISTRY.TOTAL");
}
var singleRow = [description,
obj.year3Total,
obj.year3Local,
obj.year3International,
obj.year2Total,
obj.year2Local,
obj.year2International,
obj.year1Total,
obj.year1Local,
obj.year1International
]
rows.push(singleRow);
}
doc.autoTable(columns, [], {
theme : 'grid',
styles: {
halign: 'right'
},
headerStyles: {
fillColor: [33, 150, 243],
halign:'center',
lineWidth: 1,
lineColor: [221, 221, 221]
},
columnStyles:{
0: {columnWidth: 266}
},
margin : {
top : 100
}
});
var columns2 = [ vm.translate("MENU.SETTINGS.LEGALFORM"),
vm.translate("REPORT.REGISTRY.TOTAL"),
vm.translate("REPORT.REGISTRY.LOCAL"),
vm.translate("REPORT.REGISTRY.INTERNATIONAL"),
vm.translate("REPORT.REGISTRY.TOTAL"),
vm.translate("REPORT.REGISTRY.LOCAL"),
vm.translate("REPORT.REGISTRY.INTERNATIONAL"),
vm.translate("REPORT.REGISTRY.TOTAL"),
vm.translate("REPORT.REGISTRY.LOCAL"),
vm.translate("REPORT.REGISTRY.INTERNATIONAL")
];
doc.autoTable(columns2, rows, {
theme : 'grid',
styles: {
halign: 'right'
},
headerStyles: {
halign:'center',
lineWidth: 1,
lineColor: [221, 221, 221]
},
margin : {
top : 120
},
columnStyles:{
0: {halign:'left'}
},
createdCell: function(cell, data) {
if(data.row.raw[0] === vm.translate("REPORT.REGISTRY.TOTAL")) {
cell.styles.fontStyle = 'bold';
cell.styles.fillColor = [255,251,204];
}
}
});
doc.save();
};
Something like this (v3 and up):
let head = [
[
{content: 'People', colSpan: 3, styles: {halign: 'center', fillColor: [22, 160, 133]}},
{content: 'Data', colSpan: 2, styles: {halign: 'center', fillColor: [22, 160, 133]}}
],
['ID', 'Name', 'Email', 'City', 'Sum'],
];
doc.autoTable({
startY: 60,
head: head,
body: body,
theme: 'grid'
});

Resources