PrimeFaces: ChartJs stacked bar chart label removal - javascript

I am developing a dashboard, and in it I have a chart with some sales rep data. Here's my code and an image.
//xhtml element:
<p:panel header="Oportunidades">
<p:barChart id="graficoOportunidadesRepresentante" model="#{dashboardBean.barModel}" style="width:100%; height:420px"/>
</p:panel>
//Bean generation method:
public void criaGraficoOportunidadesPorRepresentante() {
barModel = new BarChartModel();
ChartData data = new ChartData();
[...]
data.setLabels(labels); //comes from the related sales rep's name
barModel.setData(data); //backend fetches amounts from database
// Options
BarChartOptions options = new BarChartOptions();
CartesianScales cScales = new CartesianScales();
CartesianLinearAxes linearAxes = new CartesianLinearAxes();
linearAxes.setStacked(true);
cScales.addXAxesData(linearAxes);
cScales.addYAxesData(linearAxes);
options.setScales(cScales);
Title title = new Title();
title.setDisplay(true);
title.setText(labelGraficoOportunidadesAtivas);
options.setTitle(title);
Tooltip tooltip = new Tooltip();
tooltip.setMode("index");
tooltip.setIntersect(false);
options.setTooltip(tooltip);
barModel.setExtender("chartOportunidadePorRepresentanteExtender");
barModel.setOptions(options);
}
//Javascript extender:
function chartOportunidadePorRepresentanteExtender() {
var options = $.extend(true, {}, this.cfg.config);
options = {
options: {
plugins: {
labels: {
render: 'value',
position: 'inside'
}
},
title: {
fontSize: 16
}
}
};
$.extend(true, this.cfg.config, options);
};
I want the numbers on the bars to not render, because they are better legible on the element's tooltip, and are stacking on top of one another
as per this screenshot. AFAIK, label refers to the name of the rep, and each dataset also has a ".setLabel(String)" method that's rendering between the chart title and content. I've found some solutions refering to a "legend" property, but it de-rendered the so far called label property.

I've managed it. In the js extender:
labels: {
render: 'value',
fontSize: 0
}
I don't even know why I removed the "position: inside" value, I just did. fontSize = 0 hid the numbers.

Related

how do I clear a apache Echart? I need to re-initialize it

I have Stacked Horizontal Bar chart on a single page, and each chart changes based on what the user selects (Drop Down Select). I have an ajax call that retrieves the data, so the data varies and is dynamic based on user selection. I'm having a problem clearing out old data. If there is no data it should display Horizontal Bar chart. But it displays previous data. it is not getting empty if there is no data. Basically, I just want after each selection to re-initialize the chart and start from scratch. How can I do that?
<script type = "text/javascript" >
var series;
$("#sub_project3").change(function() {
$.ajax({
url: "<?php echo base_url("
Manage_procurement_plan / load_bar_chart ");?>",
type: "POST",
data: {
drop_value: $(this).val()
},
dataType: "text",
cache: false,
success: function(data) {
series = data;
var dom = document.getElementById("main");
var myChart = echarts.init(dom);
var app = {};
var option;
getBarGraph(series);
function getBarGraph(data) {
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
top: '3%',
},
grid: {
top: '28%',
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'value',
},
yAxis: {
type: 'category',
data: ['Actual Avg', 'ADB Min Standard']
},
series: JSON.parse(data),
};
/*if (option && typeof option === 'object') {
myChart.setOption(option);
}*/
myChart.setOption(option);
}
}
});
});
</script>
Each time you make a new selection, you can 're-initialize' your series (make it an empty list) before pushing new data inside it. So if there is no data, the series will remain empty.
Then, you can update your chart using notMerge = true. According to echarts doc :
notMerge Optional. Whether not to merge with previous option. false by
default, means merge, see more details in Component Merging Modes. If
true all of the current components will be removed and new components
will be created according to the new option.
myChart.setOption(option, true);
I had the same problem today.
What I see in your question is that you are using each Ajax call to create a new chart.
var dom = document.getElementById("main");
var myChart = echarts.init(dom);
So that Ajax call is fine for the very first time you create the chart and show the initial data.
The fact is that the Apache Echarts library remembers each drawn chart on it's instances. So, on every call of this success method you are actually creating a new chart.
So, you must create another Ajax call for updating the data, where first, you must find the chart instance, and execute the setOption method, as well said by #A mere dev in the other answer. On the only update Ajax call, you'll don't need to create a new chart, but just receive the new data and pass it
myChart.setOption(option, true);

ChartJS different border color for legend

I need to set a different border color for the legend than the actual chart; whenever I add border color to the chart data, it gets applied to both legend and chart. And I couldn't find any options for setting different border colors for the legend in documentation
Here's a screenshot of what I'm trying to achieve, any idea?:
The only way I have found to do this and some other customisation on the legend is to implement the legend item interface.
The accepted answer here in combination with the legend item interface should be a good start.
Here's a jsfiddle that I made from that answer, but updated for chartjs 3.7.1. The main part to focus on is this:
options: {
plugins: {
legend: {
labels: {
usePointStyle: true,
generateLabels: function(chart) {
var data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map(function(label, i) {
var meta = chart.getDatasetMeta(0);
var ds = data.datasets[0];
return {
text: label,
fillStyle: ds.backgroundColor[i],
strokeStyle: ds.legendOutline[i],
lineWidth: ds.borderWidth,
hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
index: i
};
});
} else {
return [];
}
}
}
}
}
strokeStyle is the outline property, and I've made a field in the dataset called legendOutline. legendOutline and the data from the dataset are both referenced with i, so the legendOutline[i] points to the label representing data[i]

Chart js is it possible to assign space between the legend and the chart? [duplicate]

I have a bar chart where I have drawn 3 vertical lines, each with it's own label at the top. I would like those labels to be above the top of the y-axis (above the 30% line in the example) but below the legend. I can't figure out how to increase the space between the top legend and the chart such that I can have my vertical line labels (15, 24 & 33) be off of the chart itself but below the legend. Any ideas?
If you want do increase spacing in all charts you can put this code before creating :
Chart.Legend.prototype.afterFit = function() {
this.height = this.height + 50;
};
Of course, I don't try but i think you can change it (or copy the original Chart object before, to keep the original padding).
Bye,
If you want to apply padding below legend for some charts only in your app:
ChartJS >= 2.1.0
Chart.plugins.register({
id: 'paddingBelowLegends',
beforeInit: function(chart, options) {
chart.legend.afterFit = function() {
this.height = this.height + 50;
};
}
});
// ----------------------------------
// disable the plugin only for charts
// where you DO NOT WANT the padding
// ----------------------------------
// for raw ChartJS use:
var chart = new Chart(ctx, {
config: {
plugins: {
paddingBelowLegends: false
}
}
});
// for angular-chartjs:
$scope.myChart.options.plugins = { paddingBelowLegends: false }
// then in template:
// <canvas class="chart ..." chart-options="myChart.options" ... />
ChartJS >= 2.5.0
Specific plugins for each chart are supported, it should be possible to do:
var chart = new Chart(ctx, {
plugins: [{
beforeInit: function(chart, options) {
chart.legend.afterFit = function() {
this.height = this.height + 50;
};
}
}]
});
See ChartJS documentation + inspired by this other answer
Unfortunately, since there is no config option to handle this the only way you can achieve the desired result is to extend Chart.Legend and implement the afterFit() callback.
Here is a quick codepen showing how to do just that. To change the spacing, just change the value in line 9 (currently set to 50). Also, this of course only works with the legend at the top. Hopefully, the example is clear enough for you to modify in case you want to move your legend elsewhere.
If anyone is wondering why the afterFit solution is not working in Chart.js 3.3.0 it is because afterFit function was removed from the legend plugin.
If you want to make this work anyway by taking advantage over the fit function, you can try this hacky solution / workaround:
const plugin = {
beforeInit(chart) {
// Get reference to the original fit function
const originalFit = chart.legend.fit;
// Override the fit function
chart.legend.fit = function fit() {
// Call original function and bind scope in order to use `this` correctly inside it
originalFit.bind(chart.legend)();
// Change the height as suggested in another answers
this.height += 15;
}
};
}
I know that this is not an ideal solution, but until we have native support for this legend padding, I'm afraid this is as good we can do right now.
This helped me after 2 days of research.
Chart.Legend.prototype.afterFit = function() {
this.height = this.height + 50;
};
update this in module.ts file
I'm using react-chartjs-2 (but this is just a port and uses the same configurations object) and I was able to achieve that by changing the labels configuration nested on legend configuration:
chartOptions = {
legend: {
labels: {
padding: 50 -> this one.
}
},
You can check the property description here:
https://www.chartjs.org/docs/latest/configuration/legend.html
Hope it helps.
I have tried the above approaches on my react(react-chartjs-2) project but no luck. Here I have one different approach like creating custom legends outside the chart. so you can get more control over it
Hide default legend
Get legend object using any ref method.
Loop and make a custom legend using html and css.
Sample codesanbox
I am aware that OP is not about reactjs component, but as it is a common issue it will help someone.
Orginal reference
.
For ng2-charts#^3.1.0, following this answer works with an addition:
this.options.labels.padding = 40;
//this.height += 15;
or the title.padding config (this'll create an invisible title under the graph so it's a bit hacky):
plugins: {
legend: {
display: true,
position: 'bottom',
title: {
display: true,
padding: 10,
},
Stackblitz
After searching the chart.js file I found out that the height of the labels bar is defined there in
height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight)
OR
this.height = Math.min(height, options.maxHeight || this.maxHeight)
in the fit() function
fit() {
const {options, ctx} = this;
if (!options.display) {
this.width = this.height = 0;
return;
}
const labelOpts = options.labels;
const labelFont = toFont(labelOpts.font);
const fontSize = labelFont.size;
const titleHeight = this._computeTitleHeight();
const {boxWidth, itemHeight} = getBoxSize(labelOpts, fontSize);
let width, height;
ctx.font = labelFont.string;
if (this.isHorizontal()) {
width = this.maxWidth;
height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight);
} else {
height = this.maxHeight;
width = this._fitCols(titleHeight, fontSize, boxWidth, itemHeight);
}
this.width = Math.min(width, options.maxWidth || this.maxWidth);
this.height = Math.min(height, options.maxHeight || this.maxHeight) + 40;}
And I changed it to +40 as shown above. and it worked fine for me so I wanted to share.
If you want to apply padding below legend for some charts only in your app:
ChartJS >= 2.1.0
Note: make sure to add this in plugins and not inside options.plugins.
Chart.plugins.register({
id: 'paddingBelowLegends',
beforeInit: function(chart, options) {
chart.legend.afterFit = function() {
this.height = this.height + 50;
};
}
});
// ----------------------------------
// disable the plugin only for charts
// where you DO NOT WANT the padding
// ----------------------------------
// for raw ChartJS use:
var chart = new Chart(ctx, {
config: {
plugins: {
paddingBelowLegends: false
}
}
});
For React Users using react-chartjs-2:
import { Line } from "react-chartjs-2";
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from "chart.js";
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);
<Line
data={{
datasets: trendsData?.map((trend, idx) => ({
type: "line",
label: trend.domainName,
data: trend.domainTrends.map(d => d.value),
backgroundColor: getDomainColor(idx).backgroundColor,
borderColor: getDomainColor(idx).color,
pointRadius: 0,
tension: 0.3
})),
labels: trendsData?.[0]?.domainTrends.map(d => d.date)
}}
options={{
plugins: {
legend: {
display: true,
align: "start",
labels: {
font: { size: 14 }
}
}
}
}}
plugins={[
{
id: "increase-legend-spacing",
beforeInit(chart) {
// Get reference to the original fit function
const originalFit = (chart.legend as any).fit;
// Override the fit function
(chart.legend as any).fit = function fit() {
// Call original function and bind scope in order to use `this` correctly inside it
originalFit.bind(chart.legend)();
this.height += 20;
};
}
}
]}
/>
I know this is not what OP really wants but at least for newer version, and the one I use - 4.0.1 - there is no way to increase the margin between the legend box and the chart, that I have been able to find at least, so this is an obvious workaround. So in order to avoid this problem:
I had to change the position of the leyend box below the chart with this option configurations:
options = {
layout: {
padding: 30
},
parsing: {
key: 'nested.value'
},
plugins: {
datalabels: {
color: '#36A2EB'
},
tooltip: {
enabled: true
},
legend: {
position: 'bottom',
align: 'center',
labels: {
padding: 20,
}
}
}
};
Note: To add the values at the top of the bars I had to add the npm package chartjs-plugin-datalabels with the following features inside your datasets config:
datasets: [
{
label: "Ejercido",
data: source.map( s => s.ejercidoTotal),
datalabels: {
anchor: 'end',
clamp: true,//this makes one datalabel not visible if it crashed with other one
display: 'auto',
align: 'top',
color: '#333333',
formatter
}
},
...
]
With this as the end result:
If you are using react-chartjs-2 library to show chart in a React app. You can use the below solution:
const plugin = {
beforeInit: function (chart) {
// Get reference to the original fit function
const originalFit = chart.legend.fit
// Override the fit function
chart.legend.fit = function fit() {
// Bind scope in order to use `this` correctly inside it
originalFit.bind(chart.legend)()
this.height += 20 // Change the height
}
}
}
export const ReactChart2Example = (props) => {
const { data = [] } = props;
return (
<div>
<Chart plugins={[plugin]} type ="bar" data={data}/>
</div>
);
};
We have to pass plugins prop separately in React js Chart component and not inside options.
Reference: https://github.com/chartjs/Chart.js/issues/10388#issuecomment-1217363379
You can use layout property under options. This helped me:
layout: {
padding: {
left: 50,
right: 130,
top: 0,
bottom: 0
}
}

Renaming flot pie chart ID

I'm trying to create a pie chart through the use of flot charts. I have successfully managed to create one with the following code:
HTML:
<div class="row">
<div class="col-md-6">
<div id="pie-chart"></div>
</div>
</div>
Javascript:
var data = [];
data[0] = { label: "Vertification successful", data: 9 };
data[1] = { label: "Vertification failed", data: 2 };
var series = Math.floor(Math.random() * 10) + 1;
$.plot($("#pie-chart"), data,
{
series: {
pie: {
show: true,
}
},
grid: { hoverable: true },
});
And it displays just fine.
The thing is, if I change the ID of the div element to "pie-chart1" (rather than "pie-chart")
and update the javascript accordingly:
$.plot($("#pie-chart1"), data,
I get the following error:
Uncaught Invalid dimensions for plot, width = 501, height = 0
What on earth could be causing this? I simply wanna rename the ID which apparently for some reason is impossible.
It's very likely that there is some CSS or possibly JS elsewhere on your site that expects the div to be called pie-chart. You need to ensure that it still applies to the new div. For example, if you had:
#pie-chart {
width: 400px;
height: 300px;
}
When you change the ID of the div, you need to update that reference too, or else the placeholder's height and width become undefined, which Flot cannot handle.
If your goal in adding that number is to create several charts, then you should use a class to apply the styles rather than an ID.

Highcharts Column Series Point Object Click Function

Recently I posted a question on Highcharts column charts drilldown. I have found that in click event of point object you can find out which column was clicked. I have implemented the same in my code but I am not getting alert in my code. Please find below my code. First is my chart options variable -
var columnoptions = {
chart: {
renderTo: 'container',
type: 'column'
},
title: {
text: 'Column Chart'
},
xAxis: {
categories: []
},
yAxis: {
title: {
text: 'Exposure'
}
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function() {
alert ('here');
}
}
}
}
},
series: []
};
I am filling the series dynamically with below statements in a loop -
columnoptions.xAxis.categories.push(categoryArray[index]);
seriesOptions.data.push(valueArray[index]);
Finally I display my chart like this -
chart = new Highcharts.Chart(columnoptions);
But I am not getting any alert on column click. I get error javascript error messages in IE. I am using IE8. Kindly help me with this. Highcharts official example with static data works fine and I have seen that. My chart displays correctly without any issues. But I need to know which column was clicked to implement drilldown functionality. Please help.
---Edit
Here is the full function I am using to draw the charts-
function displayColumnChart(){
columnoptions.series = [];
columnoptions.xAxis.categories = [];
var seriesOptions = {
name: 'Column Chart',
data: [],
};
/* category array contains x axis category values
value array contains y axis numeric values */
for(index = 0; index < categoryArray.length; index++){
columnoptions.xAxis.categories.push(categoryArray[index]);
seriesOptions.data.push(valueArray[index]);
}
columnoptions.series.push(seriesOptions);
chart = new Highcharts.Chart(columnoptions);
}
I am reading my data from an XML doc and then creating value and category array. Chart comes fine but not getting alert on point click. Please help. Thanks. :) Sorry for delay in posting the code.
I only added the latest version of the highcharts and point click is working for me. Thanks.

Categories