I am using Primefaces 6 for my companys charting needs, which relies on jQplot.
For a project, I am trying to overlay a line chart on a stacked bar chart with negative values, to obtain something like this:
The problem is that when I try to add a linechartseries to the same model as the two barchartseries , the linechart becomes a part of the stack when setting setStacked(true); on the model, because Primefaces seems to not allow individual disabling of stacking on series, only per model. So I end up with this when rendering the chart with
<p:chart type="bar" model="#{backingBean.cartesianChartModel}"/>
After some investigation I have notoced that jQplot is capable of disabling Stacking on individual series by passing disableStack : true in the JS options, so the question is if it's posssible to override this in some way on the rendered page,either via PF or via some JS hack? I feel that using the extender only apples to the entire model?
Related issues: Disable individual stacking
By pouring through the documentation I found a solution to the problem, if not the question:
It seems that Primefaces allows for individual series to be excempt from the stack in the series creation in this version, by passing
LineChartSeries.setDisableStack(true);
Simple as that.
I guess it may be possible. I used the extender functionality for some jqPlot hacks in the past.
In my case, for example, I had a Donut Chart defined with an extender function as follows:
private void createDonutModel() {
donutModel = new DonutChartModel();
donutModel.setLegendPosition("s");
donutModel.setLegendPlacement(LegendPlacement.OUTSIDE);
donutModel.setSliceMargin(4);
donutModel.setDataFormat("value");
donutModel.setShadow(false);
donutModel.setExtender("donutExtender");
donutModel.setSeriesColors("B81C40, FFA600, 79B54A");
}
The corresponding javascript was doing some changes to the jqPlot:
/**
* Customized jqPlot JQuery layout of the Donut Chart for Status Dashboard.
*/
function donutExtender() {
this.cfg.seriesDefaults = {
// make this a donut chart.
renderer:$.jqplot.DonutRenderer,
rendererOptions:{
thickness: 26,
ringMargin: 0,
fill: true,
padding: 0,
sliceMargin: 4,
// Pies and donuts can start at any arbitrary angle.
startAngle: -90,
showDataLabels: false,
// By default, data labels show the percentage of the donut/pie.
// You can show the data 'value' or data 'label' instead, or 'percent'
dataLabels: 'value',
shadow: false
}
}
this.cfg.gridPadding = {
top: 0, right: 0, bottom: 0, left: 0
}
this.cfg.legend = {
show: false
}
this.cfg.grid = { drawBorder: false,
shadow: false,
background: "transparent"
};
}
So you may try something like this in your case ?
Leave the extension configuration of your series empty, except for the one you are interested in...
function chartExtender() {
this.cfg.series = [
{ //...
},
{ // ...
},
{
disableStack: true
}
]
}
Worth having a shot ...
Related
I am facing a rather troublesome problem with chart as follows:
I want to remove the outermost border and keep the inner lines, but the chart doesn't allow me to do that. I tried using 'drawBorder: false', but it still doesn't work. Is there any way to handle this?
How to remove the value of the origin x-Axis and Y-Axis?
please refer to the image
How to edit the value of A-Xis? I want it to increase by 1000 each time on the X-Axis.
image
Setting the scales option like this:
scales: {
y: {
grid: {
drawBorder: false, // <-- this removes y-axis line
lineWidth: function (context) {
return context?.index === 0 ? 0 : 1; // <-- this removes the base line
}
}
},
x: {
grid: {
drawBorder: false,
lineWidth: 0 // <-- this removes vertical lines between bars
}
}
}
should achieve the desired look. Check out this code sandbox with an example.
You can find more information about styling the chart here.
What I have
I've created 2 graphs like shown in the first image below. I need these 2 graphs to be perfectly aligned on the 0 axis (which is shown in the section below).
Wanted result
In the image below, the graphs are perfectly aligned and should therefore contain 2 different datasets. One should be placed on the left grid and the other one on the right grid.
The options I thought of
I have not seen a way to implement this in ChartJS. Therefore, I thought of some possible solutions that could solve this issue in a different way.
Because both datasets don't have negative values, I could convert the left dataset to negative values (on the x-axis). The downside is that the range would be incorrect. Also, this would give issues with the popup message if you hover your mouse on a value.
Maybe, two canvas objects can be combined that could result in the wanted result
Question
Is there a way to achieve the wanted result within ChartJS? Or can I perform some wizardry to combine two charts nicely into one? Or should I move to the D3.js library for this particular graph?
I did not find a way to "nicely" implement this. I've changed the source code of the ChartJS library to implement this.
This method uses 2 different graphs that render in 2 different canvas objects. These canvas objects are placed side-by-side to let it look like one graph.
This is my result (The code in here is not exactly like the graph in the picture because I changed it to my specific needs).
I've added a custom variable in the options.layout category named sidePadding.
The code of creating the graph:
new Chart(document.getElementById(this.element), {
type: 'scatter',
data: {
datasets: [{
borderColor: this.lineColor,
borderWidth: 1,
pointBackgroundColor: '#000',
pointBorderColor: '#000',
pointRadius: 0,
fill: false,
tension: 0,
showLine: true,
data: [],
}],
},
options: {
aspectRatio: 0.3,
maintainAspectRatio: false,
responsive: true,
layout: {
sidePadding: {
left: 3,
right: -3,
}
}
}
});
The padding on the sides is by default 3 so it does not touch the border of the canvas. I wanted it to touch the border of the canvas, and therefore set it to -3.
Two different graphs need to be created, one for the left side and one for the right side.
The left graph should have the sidePadding.right set to -3. The right graph should have the sidePadding.left set to -3.
Changes in the ChartJS library
I've worked with ChartJS version v2.9.3. This method will possibly not work anymore in a new version.
I've changed the following in the Chart.js file:
Replace (line number around 11800):
// Adjust padding taking into account changes in offsets
// and add 3 px to move away from canvas edges
me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3;
me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3;
to:
// Adjust padding taking into account changes in offsets
// and add 3 px to move away from canvas edges
var layoutOptions = chart.options.layout || {};
var sidePadding = helpers$1.options.toPadding(layoutOptions.sidePadding);
me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + sidePadding.left;
me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + sidePadding.right;
Also you must add the sidePadding.left and sidePadding.right parameter to ChartJS.
This can be done by changing (line number around 7180):
core_defaults._set('global', {
layout: {
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
// Custom added parameter
sidePadding: {
left: 3,
right: 3,
}
}
});
to:
core_defaults._set('global', {
layout: {
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
// Custom added parameter
sidePadding: {
left: 3,
right: 3,
}
}
});
Note
This is probably not the best way to achieve this. Changing the ChartJS library is bad-practice and updating the library could revert these changes.
If someone has a better way to achieve this, please post your methods or comment below.
Following is my resultant chart
Here the value of legends Happy and Very Happy is 0, hence it is overlapping each other and unable to read. So, How can I hide these values and strike through the legends while loading itself like in the below image? And yes, it is a dynamically loaded chart.
Link - Reference Pie Chart
Thanks in advance.
I am posting this answer hoping that, it will be helpful for someone later. You can also post a better solution if found.
After some deep diving into the library the files, I realised that is there are no direct answers to my question. But we can achieve that by emptying the label text in case of 0 data values.
For that, we must edit the chart options as follows,
public pieChartOptions: ChartOptions = {
responsive: true,
legend: {
position: 'top',
},
plugins: {
datalabels: {
formatter: (value, ctx) => {
const label = ctx.chart.data.labels[ctx.dataIndex];
if (ctx.dataset.data[ctx.dataIndex] > 0)
return label + " : " + ctx.dataset.data[ctx.dataIndex];
else
return "" // retun empty if the data for label is empty
},
}
},
showLines: true,
spanGaps: true,
cutoutPercentage: 1,
rotation: 15, // rotate the chart
};
Here in the function returns empty value in case the data for the corresponding label is 0. Also I rotate the chart 15deg to make the labels horizontal align in most cases.
Reference - Chart.js documentation
Hence I achieved a better view to the user and the overlapping issues are resolved. Thanks.
Good afternoon, I'm putting together a chart that uses Highcharts stackLabels, it's exactly the same as this: JSFiddle however I do not want it to display the sum of the column values, I want it to appear item by item one on top of the other, same as the print I simulated below:
In order to set your stackLabels as you expecting, you need to use stackLabels.formatter function to calculate the labels values manually.
Next thing you've to do is make a little shifting on the labels. You are able to do it by stackLabels.y field.
At the end, don't forget about the stackLabels.useHTML parameter set equal to true. It makes the <span> and </br> elements will appear correctly. Please take a look at code below:
yAxis: {
stackLabels: {
style: {
color: 'red'
},
enabled: true,
useHTML: true,
formatter: function() {
var firstLabel = Math.round((this.points[0][1] - this.points[0][0]) * 10) / 10,
secondLabel = this.points[0][0]
return `<span>${firstLabel}</span></br>
<span>${secondLabel}</span>`
},
y: -15
}
}
You can read more about parameters mentioned above here:
https://api.highcharts.com/highcharts/yAxis.stackLabels.formatter
https://api.highcharts.com/highcharts/yAxis.stackLabels.y
https://api.highcharts.com/highcharts/yAxis.stackLabels.useHTML
Live example: http://jsfiddle.net/r01nkgyn/
I'm using showArea: true but can't find the appropriate setting to make that fill all the way. Example:
I assume this is because I don't have any data points after where it ends, but I don't want the green line to extend all the way across the top. Is there another way to accomplish this?
You're using showArea:true to render the area. But as you've noted, showArea fills the area associated only with the drawn line.
What you're looking for is an additional area without a line.
In order to achieve this effect, you'll need to create two different lines: The first line will have showArea: false and extend to W3 as shown in your example. This will render the light green line as you have already.
The second line will be invisible have showLine: false and showArea: true and extend all the way across the top to W8.
In other words, you're actually looking to render two different things. One is a line, and one is a fill area.
I guess that the key solution to your problem is to use display:inline-block;
for example:
div.page {
color: white;
background: black;
margin: auto;
padding: 1em;
display:inline-block;
}
In order for the area to highlight you need to insert appropriate data. The showArea property extend as much as the data it has. Here is a proof of concept:
/* Add a basic data series with six labels and values */
var data = {
labels: [1, 2, 3, 4, 5],
series: [
[1, 5, 10, 15, 20],
[1, 20]
]
};
/* Set some base options (settings will override the default settings in Chartist.js *see default settings*). We are adding a basic label interpolation function for the xAxis labels. */
var options = {
showArea: true,
axisX: {
labelInterpolationFnc: function (value) {
return 'Week ' + value;
}
}
};
/* Initialize the chart with the above settings */
new Chartist.Line('.ct-chart', data, options);
.ct-chart {
width: 450px;
height: 250px;
}
<link href="https://rawgit.com/gionkunz/chartist-js/master/dist/chartist.min.css" rel="stylesheet"/>
<script src="https://rawgit.com/gionkunz/chartist-js/master/dist/chartist.min.js"></script>
<div class="ct-chart ct-square"></div>
The two areas are highlighted within the data they represent.
Hope this helps.