old yAxis ticks do not get removed (chartjs, react-chartjs-2 wrapper) - javascript

When i draw a chart with 4 lines, each with its own data ofc, i programmatically create the options for the LineChart that has 4 Yaxis, first one on the left and the rest on the right side. Now, after the chart is drawn and i de-select some datasources from the list (less lines to draw), the now-obsolete yAxis ticks stay there, even when the chart correctly draws only the selected lines, and the options are updated as well correctly. I cant think of a way to remove them!
I have googled for 2 days and cant find a solution. I am using react in functional style and it makes things more complicated because every advice seems to be in the classic style.
I am using react-chartjs-2 wrapper as well, if this helps.
I am also quite new to react, and asking in Stackoverflow, so please cut me some slack :)
I assume the chart is being re-rendered or something because the amount of lines etc do change.
In the images, the "createYaxis" that is shown in the console.log is the generated yAxes- part of the options object (which is functional otherwise). The problem yAxises are on the right side in red and yellow. Images show before and after situation.
Image of the options-object generated by the code below the img:
var yAxisItems = [];
function createYaxises (num){
var arr = [];
for (var i=0;i<num.length;i++){
if (i===0){
arr.push({
display: true,
id: i,
type: 'linear',
position: 'left',
gridLines: {
display:false,
//color: 'blue'
},
ticks: {
fontColor: lineColourArray[i],
fontSize: 14,
}
})
}
else {
arr.push({
display: true,
id: i,
type: 'linear',
position: 'right',
gridLines: {
display:false,
//color: 'blue'
},
ticks: {
display:true,
fontColor: lineColourArray[i],
fontSize: 14,
}
})
}}
yAxisItems = arr;
console.log("createyaxis arr: " , arr);
console.log("createyaxis: " , yAxisItems); //JSON.stringify(yAxisItems));
}
//get data for selected sensors and set it to chart data
const handleGetSelectedSensorData = function () {
var d = getSelectedSensorData();
console.log("d: ", d);
var dSets = [];
if (d[0]){
d.map((dItem,index)=> {
var newDsetData =[];
if (dItem.data){
dItem.data.map((innerDataItem)=> {
var dSet = {};
dSet.x = innerDataItem.timestamp;
dSet.y = innerDataItem.v;
newDsetData.push(dSet);
})
var newset = {
data: newDsetData,
label: dItem.sensorTag,
borderColor: lineColourArray[index],
fill: false,
pointRadius: 1.5,
backgroundColor:lineColourArray[index],
borderWidth: 2,
showLine: true,
pointHoverRadius: 5,
lineTension: 1,
};
dSets.push(newset);
}})
var dDataTemp = {};
var optionsTemp = new Object();
dDataTemp.datasets =dSets;
//create yaxises only once
createYaxises(dDataTemp.datasets);
//more than one set (TODO)
//console.log("dDataTemp.datasets : ", dDataTemp.datasets)
if (dDataTemp.datasets.length >1){
console.log("dset > 1");
for(var i=0;i< dDataTemp.datasets.length;i++) {
dDataTemp.datasets[i].yAxisID = i;
console.log("setting options");
optionsTemp ={
tooltips: {
enabled: true,
intersect:false,
mode:'x',
callbacks: {
title: function(tooltipItem, data) {
var toSplit = tooltipItem[0].label.split(",");
return (toSplit[0]);
},
label: function (tooltipItem) {
var split = tooltipItem.xLabel.split(',');
//return ( Number(tooltipItem.yLabel).toFixed(3));
return (split[2] + " : " + Number(tooltipItem.yLabel).toFixed(3));
}
},
},
hover: {
mode: 'nearest',
intersect: true,
},
title:{
display:true,
text:'Valittu sensoridata',
fontSize:20
},
legend:{
display:true,
position:'right'
},
scales: {
xAxes: [{
display: true,
type: 'time',
ticks: {
}
}],
yAxes:
yAxisItems
}
}
}
setOptions(optionsTemp);
console.log("options: " , optionsTemp);
setdData(dDataTemp);
}}
else {
console.log("error in handleGetSelectedSensorData()");
}
}
And the Line is just added like this:
<Line data={dData} options = {options} />

Instead of setting display: true set display: 'auto', this will make the axis dissapear as long as there is no dataset visable that is linked to that scale, as soon as a dataset becomes visable that is linked to that scale it will show the scale again.
Doc: https://www.chartjs.org/docs/master/axes/cartesian/#common-options-to-all-axes

Related

Chart.js - change which data the legend uses for the coloured box

I have a bar chart that always shows 4 bars. The bars are coloured dynamically. It looks like the coloured box takes its colour from the first data. I would like to use the colour from the 4th (last) data value. Maybe the options:plugins:legend:label:sort function helps but I don't understand what it does.
options
const options = {
scales: {
x: {
grid: {
display: false,
color: 'rgba(0,0,0)'
}
},
y: {
display: false,
min: 0,
max: 4
},
},
plugins: {
legend: {
position: 'bottom'
}
}
}
So I don't know if I can change the data that the box color comes from, or if there is a config option somewhere where I can change it manually.
You can use the generateLabels function as described here.
Please take a look at below runnable sample code and see how it works.
new Chart('myChart', {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow'],
datasets: [{
label: 'My Dataset',
data: [300, 50, 100],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56']
}]
},
options: {
responsive: false,
plugins: {
legend: {
labels: {
generateLabels: chart => {
let ds = chart.data.datasets[0];
let color = ds.backgroundColor[ds.backgroundColor.length - 1];
return [{
datasetIndex: 0,
text: ds.label,
fillStyle: color,
strokeStyle: color
}];
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js"></script>
<canvas id="myChart" height="180"></canvas>
Following up with #uminder's answer, if you want to keep the hide/show chart and the line-through style after clicking on the legend, you can add the following line:
options: {
responsive: false,
plugins: {
legend: {
labels: {
generateLabels: chart => {
let ds = chart.data.datasets[0];
let color = ds.backgroundColor[ds.backgroundColor.length - 1];
return [{
datasetIndex: 0,
text: ds.label,
fillStyle: color,
strokeStyle: color,
+ hidden: !chart.isDatasetVisible(0)
}];
}
}
}
}
}

Add HTML to label of bar chart - chart js

I am using the chart js to display a bar graph. It's working correctly on normal instances, but I am willing to change the color or a small portion of the label i.e, I want to include some HTML on the label of the bar chart. But, it isn't rendering the HTML instead it is showing plain HTML text.
If it is not possible, it's okay for me if there is another way to achieve this like, change the color of the price and keep the name as it is.
let $js_dom_array = ["43.28", "93.13"];
let $js_am_label_arr = ["<span>$0</span> None", "<span class='text-danger'>$23.63</span> Handicap Accessible"];
let ctx2 = document.getElementById("barChart").getContext("2d");
let chart = new Chart(ctx2, {
type: 'bar',
data: {
labels: $js_am_label_arr,
datasets: [{
label: 'Amenity Name',
data: $js_dom_array,
backgroundColor: 'rgba(26,179,148,0.5)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
legendCallback: function(chart) {
var text = [];
for (var i=0; i<chart.data.datasets.length; i++) {
console.log(chart.data.datasets[i]); // see what's inside the obj.
text.push(chart.data.datasets[i].label);
}
return text.join("");
},
tooltips: {
"enabled": false
},
scales: {
xAxes: [{
stacked: false,
beginAtZero: true,
ticks: {
stepSize: 1,
min: 0,
autoSkip: false,
callback: function(label, index, labels) {
if (/\s/.test(label)) {
return label.split(" ");
}else{
return label;
}
}
}
}]
},
animation: {
duration: 0,
onProgress: function() {
var chartInstance = this.chart,
ctx = chartInstance.ctx;
ctx.font = Chart.helpers.fontString(16, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function(dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
meta.data.forEach(function(bar, index) {
if (dataset.data[index] > 0) {
let data = dataset.data[index];
ctx.fillText('$'+Math.round(data), bar._model.x, bar._model.y - 5);
}
});
});
}
},
}
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<canvas id="barChart" height="140"></canvas>
</div>
#Note: Here, you might see that the data in $js_am_label_arr is already an HTML element, but if there is something from where I could pass the array of the raw values and the convert in HTML than I could pass the raw value (without) as well.
Currently $js_am_label_arr is created as:
if($avg_amount < 0){
$text_color = 'text-danger';
$avg_amount_text = "<span class='text-danger'>$".abs($avg_amount)."</span>";
}else{
$text_color = '';
$avg_amount_text = "<span>$".abs($avg_amount)."</span>";
}
$am_label_arr[] = $avg_amount_text.' '.$fv['amenity_name'];
Update:
Expected Output
So if the value is negative for example in the above case, its -$23.63. In this case, I want the label to be ($23.63)[in color red] followed by the name Handicap Accessible. This can be seen at the result as well, text-danger classes is added to show that part in red color.
As you are open to any plugin so i suggest you to use HighCharts to achieve above case . In below demo code i have just passed the label value to categories in xAxis and done a little change to span tag . i.e : i have added inline css to span where you need to display color red .
Here is demo code :
var chart;
let $js_dom_array = [43.28, 93.13];
let $js_am_label_arr = ["<span>$0</span> None", "<span style='color:red'>$23.63</span> Handicap Accessible"];
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'chart_container', //under chart_container chart will display
defaultSeriesType: 'bar', //bar grapgh
backgroundColor: '#CCCCCC',
type: 'column' //to display in columns wise
},
plotOptions: {
bar: {
colorByPoint: true,
dataLabels: {
enabled: false
}
}
},
title: {
text: 'Something.... '
},
xAxis: {
categories: $js_am_label_arr, //for value in labels
},
series: [{
name: 'Amenity Name',
data: $js_dom_array //array value to plot data
}]
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/gantt/highcharts-gantt.js"></script>
<div id="chart_container"></div>
You can change the color when you hover the label, with the tooltips callback
Move your mouse over the bar
let $js_dom_array = [43.28, 93.13];
let $js_am_label_arr = ["$0", "$23.63"];
let ctx2 = document.getElementById("barChart").getContext("2d");
let chart = new Chart(ctx2, {
type: 'bar',
data: {
labels: $js_am_label_arr,
datasets: [{
backgroundColor: 'rgba(26,179,148,0.5)',
label: 'Amenity Name',
data: $js_dom_array,
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
tooltips: {
enable: true,
callbacks: {
labelTextColor: function(tooltipItem, chart) {
if(tooltipItem.index === 1)
return 'red';
}
}
},
scales: {
xAxes: [{
stacked: false,
beginAtZero: true,
ticks: {
stepSize: 1,
min: 0,
autoSkip: false,
fontColor: "red",
callback: function(label, index, labels) {
if (/\s/.test(label)) {
return label.split(" ");
}else{
return label;
}
}
}
}]
}
}
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<canvas id="barChart" height="140"></canvas>
</div>
As far as i know, its not possible to do what you want.
With the current version (v2.9.3) its not even possible to change the color for specifics X ticks labels, you can only change the color for every label with:
options: {
scales: {
yAxes: [{
ticks: {
fontColor: "red",
}
}],
xAxes: [{
ticks: {
fontColor: "red",
}
}]
}
}
There is a workaround with version v2.6.0 (as you tagged this version, i imagine you are using it) wich you can pass an array of colors to fontColor, like: fontColor: ["red","green"], but you need to change some code lines of chartjs and yet you cannot change just a specific part of the text of axis tick label as you want.
If you have interested in this solution, you can check it here Add possibility to change style for each of the ticks
But again, this solution is related to an older version of chartjs and i dont know what features you lose here.
Looks like it will be able to change the color for each ticks in version 3.0, but this version isnt released yet.
UPDATED
I done a simple example here with c3/d3 js. but it has some points:
It take a little time to show Labels updated when chart is rendered.
On changeTickLabel i did a harded code to check value and then append the text (like 'Handicap Accessible'). So here you will need to find logic that is better to you.
var chart = c3.generate({
onrendered: function () { changeTickLabel() },
data: {
columns: [
['data1', 43.28, 93.13]
],
type: 'bar',
},
axis: {
x : {
type: 'category',
categories: ["$0", "$23.63"],
tick: {
multiline:false,
culling: {
max: 1
},
},
},
}
});
function changeTickLabel(){
d3.selectAll('.c3-axis-x .tick tspan')
.each(function(d,i){
var self = d3.select(this);
var textValue = self.text();
if(textValue !== '$0'){
self.style("fill", "red");
self.append('tspan').style("fill", "black").text(' Handicap Accessible');
}
else {
self.append('tspan').text(' None');
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chart"></div>

Bar chart legend adjustments - Chart JS

I'm new in Chart JS and I have a bar chart with the legend displayed bellow the chart:
var data = {
labels: [],
datasets: [{
label: 'Disk C',
backgroundColor: "#000080",
data: [80]
}, {
label: 'Disk D',
backgroundColor: "#d3d3d3",
data: [90]
},
{
label: 'Memory',
backgroundColor: "#add8e6",
data: [45]
}]
};
var ctx = document.getElementById("mybarChart");
ctx.height = 300;
var mybarChart = new Chart(ctx, {
type: 'bar',
responsive: true,
data: data,
options: {
legend: {
display: true,
position: 'bottom'
},
scales: {
yAxes: [{
display: false,
ticks: {
beginAtZero: true
},
gridLines: {
color: "rgba(0, 0, 0, 0)",
}
}],
xAxes: [{
display: false,
gridLines: {
color: "rgba(0, 0, 0, 0)",
},
barPercentage: 0.5,
categoryPercentage: 0.5
}]
}
}
});
But the legend should be something like :
It is possible to make the colors rectangles much more smaller and the values to be displayed one below another because in my example the labels are displayed in a single row?
Unfortunately, there is no way to customize the default legend in the manner that you are wanting. Fortunately, however, chart.js thought of this and provided a mechanism for you to generate and style your own legend outside of the canvas object (using regular html/css).
You can use the legendCallback options property to define a method that generates your legend html and then call the chart .generateLegend() prototype method to place into your page. Here is what I mean.
HTML for my page.
<div style="width:25%;">
<canvas id="mybarChart"></canvas>
<div id="legend"></div>
</div>
Then I define how the legend will look in the 'legendCallback' option property.
legendCallback: function(chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend">');
for (var i = 0; i < chart.data.datasets.length; i++) {
text.push('<li><div class="legendValue"><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"> </span>');
if (chart.data.datasets[i].label) {
text.push('<span class="label">' + chart.data.datasets[i].label + '</span>');
}
text.push('</div></li><div class="clear"></div>');
}
text.push('</ul>');
return text.join('');
}
Finally, I add the legend html to my page.
$('#legend').prepend(mybarChart.generateLegend());
As always, here is a codepen example showing a working solution. You can change your legend look and feel simply by changing the html that is generated by the callback and using css.

Flotr2 different colors for two sets of data

I've merged positive and negative data to one data set called yAxis (multiple columns for both positive and negative). Here's the code:
//...
var yAxis = [wololoTotalValues];
var xAxis = wololoTotalKeys;
window.onload = function () {
Flotr.draw(document.getElementById("chart"), yAxis, {
title: "wololo",
bars: {
show: true,
barWidth: 0.8
},
yaxis: {
min: 0,
max: wololoMaxValue + 10,
tickDecimals: 0
},
xaxis: {
ticks: xAxis
}
});
};
//...
<div id='chart' style="width:1200px;height:500px;"></div>
I would like 'bad' and 'ble' to be red. I found some manuals how to handle this problem with flot, but not flotr(flotr2).
Is there any way to make it somehow like below? Or maybe I must split the data like here?
colors: (yAxis[0-lastGood] : "#0000FF"), (yAxis[lastGood+1-lastBad] : "#FF0000")
Okay, found the solution, splitting the data into two arrays helped. Code is below:
var gud = [];
for(i=0; i<wololoLengthGood; i++){
gud.push([i, wololoValuesGood[i]]);
}
var bad = [];
for(i=wololoLengthGood; i<wololoLengthGood+wololoLengthBad; i++){
bad.push([i, wololoValuesBad[i-wololoLengthGood]]);
}
window.onload = function () {
Flotr.draw(document.getElementById("chart"),
[{ // replacement of 'yAxis' from here
data: gud,
color: "#0000FF"
},{
data: bad,
color: "#FF0000"
}], // till here.
//...

jqplot grid on top of plot

I've been tinkering with a jqplot graph where the grid lines are on top (or in front depending how you look at it) of the graph as opposed to the default background area. I've made several attempts to get it to overlay the grid using the z-index. However, each attempt renders the entire graph non-functional and I receive no error for some unknown reason.
I'm working with a stacked bar chart that actually fills the entire grid, so I don't get to see any of the grid lines, they are all hidden beneath (or behind) the graph.
Here is the code:
<script type="text/javascript" language="javascript">
$.jqplot.config.enablePlugins = true;
var plot;
var data1 = [];
var data2 = [];
var index = 0;
var num = 0;
var delta = 0;
$(document).ready(function(){
for (i=0; i<100; i++) {
num = getRandomNumber();
delta = 100 - num;
index++;
data1.push([ index, num]);
data2.push([ index, delta]);
}
plot = $.jqplot('graph', [data1, data2],{
title: 'my title',
animate: true,
stackSeries: true,
seriesDefaults:{
renderer:$.jqplot.BarRenderer,
rendererOptions: { highlightMouseDown: true },
pointLabels: {show: true}
},
series: [ {label: 'one'}, {label: 'two'} ],
seriesColors:['#ff0000', '#0000ff'],
legend: {
show: true,
location: 'e',
placement: 'outsideGrid'
},
grid: {
gridLineColor: '#333333',
borderWidth: 0
},
axesDefaults: {
pad: 0,
padMin: 0
},
axes: {
xaxis: {
showTicks: false,
pad: 0,
padMin: 0,
rendererOptions: { forceTickAt0: true, forceTickAt100: true }
},
yaxis: {
pad: 0,
padMin: 0,
rendererOptions: { forceTickAt0: true, forceTickAt100: true }
}
}
});
});
getRandomNumber = function(){
return Math.floor(Math.random()* 100);
};
</script>
Anyone run into this requirement and know how to get the grid lines to show up on top of the graph? Thanks
With a little DOM manipulation you can do this BUT you need to make sure to set the grid background color transparent for it to work. After your plot call:
gridCanvas = $($('.jqplot-grid-canvas')[0])
seriesCanvas = $($('.jqplot-series-canvas')[0])
gridCanvas.detach();
seriesCanvas.after(gridCanvas);​
Here's a sample fiddle.

Categories

Resources