How to hide the legend in chart.js in a react project? - javascript

I am trying to hide the legend of my chart created with Chart.js.
According to the official documentation (https://www.chartjs.org/docs/latest/configuration/legend.html), to hide the legend, the display property of the options.display object must be set to false.
I have tried to do it in the following way:
const options = {
legend: {
display: false,
}
};
But it doesn't work, my legend is still there. I even tried this other way, but unfortunately, without success.
const options = {
legend: {
display: false,
labels: {
display: false
}
}
}
};
This is my full code.
import React, { useEffect, useState } from 'react';
import { Line } from "react-chartjs-2";
import numeral from 'numeral';
const options = {
legend: {
display: false,
},
elements: {
point: {
radius: 1,
},
},
maintainAspectRatio: false,
tooltips: {
mode: "index",
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return numeral(tooltipItem.value).format("+0,000");
},
},
},
scales: {
xAxes: [
{
type: "time",
time: {
format: "DD/MM/YY",
tooltipFormat: "ll",
},
},
],
yAxes: [
{
gridLines: {
display: false,
},
ticks: {
callback: function(value, index, values) {
return numeral(value).format("0a");
},
},
},
],
},
};
const buildChartData = (data, casesType = "cases") => {
let chartData = [];
let lastDataPoint;
for(let date in data.cases) {
if (lastDataPoint) {
let newDataPoint = {
x: date,
y: data[casesType][date] - lastDataPoint
}
chartData.push(newDataPoint);
}
lastDataPoint = data[casesType][date];
}
return chartData;
};
function LineGraph({ casesType }) {
const [data, setData] = useState({});
useEffect(() => {
const fetchData = async() => {
await fetch("https://disease.sh/v3/covid-19/historical/all?lastdays=120")
.then ((response) => {
return response.json();
})
.then((data) => {
let chartData = buildChartData(data, casesType);
setData(chartData);
});
};
fetchData();
}, [casesType]);
return (
<div>
{data?.length > 0 && (
<Line
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data
},
],
}}
options={options}
/>
)}
</div>
);
}
export default LineGraph;
Could someone help me? Thank you in advance!
PD: Maybe is useful to try to find a solution, but I get 'undefined' in the text of my legend and when I try to change the text like this, the text legend still appearing as 'Undefindex'.
const options = {
legend: {
display: true,
text: 'Hello!'
}
};

As described in the documentation you linked the namespace where the legend is configured is: options.plugins.legend, if you put it there it will work:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
}
]
},
options: {
plugins: {
legend: {
display: false
}
}
}
}
var 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.5.0/chart.js"></script>
</body>
On another note, a big part of your options object is wrong, its in V2 syntax while you are using v3, please take a look at the migration guide
Reason why you get undefined as text in your legend is, is because you dont supply any label argument in your dataset.

in the newest versions this code works fine
const options = {
plugins: {
legend: {
display: false,
},
},
};
return <Doughnut data={data} options={options} />;

Import your options value inside the charts component like so:
const options = {
legend: {
display: false
}
};
<Line data={data} options={options} />

Related

Error when trying to update chart data: 'this undefined'

I am working in Vue. My goal is create dynamic stepsize for timeline chart. Here my
TimelineChart.vue:
<template>
<div class="chart chart-timeline">
<ChartLegend
:items="legend"
:allowFilterCreation="allowFilterCreation"
#highlight="highlighted = $event"
#toggle="toggleDatasetVisibility($event)"
#apply-filter="$emit('apply-filter', $event)"
v-if="legend"/>
<div class="chart-container" v-show="hasData">
<canvas ref="chart"></canvas>
<hr> <hr> <hr> Stepsize Y
<input type="number" id ="ySteps">
<hr> Stepsize X
<input type="number" id ="xSteps">
</div>
<h2 class="subtitle is-5" v-if="!hasData">
No data
</h2>
</div>
</template>
<script>
import _ from "lodash";
import Chart from "chart.js";
import Moment from "moment";
import ChartLegend from "#/components/charts/ChartLegend";
import ChartMixin from "#/components/charts/ChartMixin";
import Deferred from "#/Deferred";
export default {
props: ["data", "multiMode", "labelConverter", "valueConverter", "allowFilterCreation"],
components: { ChartLegend },
mixins: [ChartMixin],
data() {
return {
chart: Deferred.create(),
highlighted: null
};
},
computed: {
timeline() {
return this.data ? _.map(this.data.parameter, it => new Moment(it)) : [];
},
keys() {
return this.data ? _.map(this.data.datasets, it => it.name) : [];
},
labels() {
const converter = this.labelConverter || _.identity;
return _.map(this.keys, converter);
},
datasets() {
return this.data ? _.map(this.data.datasets, it => it.data) : [];
},
datasetCount() {
return this.datasets.length;
},
legend() {
if (!this.multiMode) {
return null;
}
return _.zipWith(this.keys, this.labels, this.palette, (key, text, color) => ({
id: key,
text,
color: color.normal,
hidden: _.includes(this.hiddenDatasets, key)
}));
},
chartColors() {
const highlightIndex = this.highlighted !== null && !_.includes(this.hiddenDatasets, this.highlighted)
? _.indexOf(this.keys, this.highlighted)
: null;
const colors = [];
for (const index of _.keys(this.datasets)) {
const color = this.palette[index];
if (highlightIndex !== null) {
if (index == highlightIndex) {
colors.push({ backgroundColor: color.strongMuted, borderColor: color.strong });
} else {
colors.push({ backgroundColor: color.weakMuted, borderColor: color.weak });
}
} else {
colors.push({ backgroundColor: color.normalMuted, borderColor: color.normal });
}
}
return colors;
}
},
mounted() {
this.chart.resolve(new Chart(this.$refs.chart, {
type: "line",
data: {
datasets: [],
labels: []
},
options: {
maintainAspectRatio: false,
legend: { display: false },
scales: {
xAxes: [{
type: "time",
time: {
isoWeekday: true,
displayFormats: {
hour: "HH:mm",
day: "DD-MM",
week: "DD-MM",
month: "MM-YYYY"
}
},
tooltips: {
},
ticks: { source: "labels",
// stepSize: 2
}
}],
yAxes: [{
ticks: { min: 0, precision: 0,
stepSize: 1
}
}]
},
spanGaps: false,
tooltips: {
callbacks: {
label: item => {
const converter = this.valueConverter || _.identity;
const label = this.multiMode ? (this.labels[item.datasetIndex] + ": ") : "";
return label + converter(this.datasets[item.datasetIndex][item.index]);
}
}
}
}
}));
// method that change stepsize
const ySteps = document.getElementById('ySteps');
ySteps.addEventListener('input', (e) => {
// eslint-disable-next-line no-undef
stepSize(this.$refs.chart, e)
});
function stepSize(chart) {
this.chart.resolve.scales.yAxes.stepSize = ySteps.value;
this.$refs.chart.update()
}
watch: {
data() {
this.resetDatasetVisibility();
}
}
}
</script>
When I alter stepsize nothing happen. In debug mode I have noticed the following pattern:
When I create chart 'this.chart' is not undefined. But when I use function stepSize I have an error, that this.chart is undefined. I have read a documentation about it, but I still do not understand how to connect to instance and change the property in the chart.
this.chart inside stepSize function will not be referring to the chart object inside your data node, because stepSize is not a vue compoenent method, its a javascript function. So this wont be refering to your vue scope.
Since you are already passing your chart object as a parameter to stepSize function as stepSize(this.$refs.chart, e), you can make use of the same inside your stepSize function like
function stepSize(chart) {
chart.resolve.scales.yAxes.stepSize = ySteps.value;
chart.update();
}

Highcharts React Pie Chart rendering multiple time

My code this
import React, { useEffect, useRef, useState } from "react";
import * as Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
export const PieChart = (props: any) => {
const [chartIsLoaded, setChartIsLoaded] = useState(false);
const series: any = [{
innerSize: '80%',
name: props.name,
colorByPoint: true,
data: props.chartData
}]
useEffect(() => {
setChartIsLoaded(true);
}, [])
const chartComponentRef = useRef<HighchartsReact.RefObject>(null);
const options: Highcharts.Options = {
chart: {
backgroundColor: "#0c0c0c",
borderColor: "#0c0c0c",
plotBorderWidth: 0,
plotShadow: false,
type: 'pie',
height: "70%",
},
title: {
style : {
display : 'none'
}
},
tooltip: {
pointFormat: '<b>{point.percentage:.1f}%</b>',
backgroundColor: "#1B1B1B",
borderColor: "transparent",
valueDecimals: 2,
borderRadius: 0,
style: {
color: "#fff",
fontSize: "15px"
}
},
accessibility: {
point: {
valueSuffix: '%'
}
},
legend: {
itemStyle: {color: "#fff", fontWeight: "400", fontFamily: "teragon-sans"}
},
plotOptions: {
pie: {
borderColor: "#0c0c0c",
borderWidth: 6,
allowPointSelect: true,
color: "#fff",
cursor: 'pointer',
dataLabels: {
enabled: false,
},
showInLegend: true
}
},
series: series
};
return (
<div>
{chartIsLoaded &&
<HighchartsReact
highcharts={Highcharts}
options={options}
ref={chartComponentRef}
oneToOne={true}
/>
}
</div>
);
};
chartData coming from this code:
let data = sheetData[0].data;
let invesment = await groupData(data, "Investment Type");
Problem: Chart rendering multiple times. Also, I have Bar Chart it's happening on that. There is no problem with Line Chart. The data is preparing with reduce function but its async waiting awaits. Moreover I tried with promises. Unfortunately, It was rendered multiple times again. How can I fix this situation?
Your chart options are initiated after every component update which directs to the chart update on every component update. I recommend you to keep chart options in a stare or memorize them. For example:
const PieChart = ({ name, chartData }) => {
const [chartOptions, setChartOptions] = useState(false);
useEffect(() => {
const series = [
{
innerSize: "80%",
name,
colorByPoint: true,
data: chartData
}
];
const options = {
...,
series
};
if (chartData) {
setChartOptions(options);
}
}, [chartData]);
return (
<div>
{chartOptions && (
<HighchartsReact highcharts={Highcharts} options={chartOptions} />
)}
</div>
);
};
Live demo: https://codesandbox.io/s/highcharts-react-demo-fz6rr6?file=/demo.jsx
Docs: https://www.npmjs.com/package/highcharts-react-official#optimal-way-to-update

How to add onclick event on chart label in react-chartjs-2?

I want open a dialog when clicking on chart js label. This is the dataset code:-
const data = {
datasets: [
{
label: 'Reviews',
backgroundColor: theme.palette.primary.main,
data: dataProp.reviews,
barThickness: 12,
maxBarThickness: 10,
barPercentage: 0.5,
categoryPercentage: 0.5
},
{
label: 'Talents',
backgroundColor: theme.palette.secondary.main,
data: dataProp.talents,
barThickness: 12,
maxBarThickness: 10,
barPercentage: 0.5,
categoryPercentage: 0.5
}
],
labels
};
This is the screenshot the chart created.
I know how to set onclick on legend but how can i set an onClick on labels ?
I Tried this in option but it is not working and giving me error
const options = {
responsive: true,
maintainAspectRatio: false,
animation: false,
cornerRadius: 20,
legend: {
display: false
},
layout: {
padding: 0
},
scales: {
xAxes: [
{
}
],
yAxes: [
{
}
]
},
tooltips: {
},
onClick: function(evt, element) {
if (element.length > 0) {
console.log(element);
// you can also get dataset of your selected element
data.datasets[element[0]._datasetIndex].data[element[0]._index];
}
}
};
All you need to do is just add onClick callback in graph options property
options={{
.....
onClick: function(evt, element) {
if(element.length > 0) {
console.log(element,element[0]._datasetInde)
// you can also get dataset of your selected element
console.log(data.datasets[element[0]._datasetIndex])
}
}}
You need to get ref, and add event getElementAtEvent.
import { Bar } from 'react-chartjs-2'
import { Chart } from 'chart.js'
const BarChart = () => {
const chartRef = useRef<HTMLCanvasElement>(null)
...
return ( <Bar
type='horizontalBar'
data={chartData}
ref={chartRef}
getElementAtEvent={(i: any, event: any) => {
if (chartRef.current) {
const chart = Chart.getChart(chartRef.current)
const clickedElements = chart!.getElementsAtEventForMode(event, 'y',{axis: 'x', intersect: false}, true)
if (clickedElements.length > 0) {
console.log(clickedElements[0].index) // Here clicked label | data index
}
}
}}
options={options}/>
)
}

update vue-chartjs yaxis max value without re rendering entire vue chart js

I am working on a project where I am implementing some charts from the Vue-Chartjs library. I need the Y-axis max value to change everytime the user changes the filters given. I Import an existing barchart from the vue-chartjs library. In the code there is a javascript file that has some defaults already, to set extra options I can use the extraOptions object as a prop to personalize each chart accordingly. Here is the default component:
import { Bar } from 'vue-chartjs'
import { hexToRGB } from "./utils";
import reactiveChartMixin from "./mixins/reactiveChart";
let defaultOptions = {
tooltips: {
tooltipFillColor: "rgba(0,0,0,0.5)",
tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
tooltipFontSize: 14,
tooltipFontStyle: "normal",
tooltipFontColor: "#fff",
tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
tooltipTitleFontSize: 14,
tooltipTitleFontStyle: "bold",
tooltipTitleFontColor: "#fff",
tooltipYPadding: 6,
tooltipXPadding: 6,
tooltipCaretSize: 8,
tooltipCornerRadius: 6,
tooltipXOffset: 10,
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
fontColor: "#9f9f9f",
fontStyle: "bold",
beginAtZero: true,
display: false,
min: 0,
max: 100
},
gridLines: {
display: false,
drawBorder: false,
}
}],
xAxes: [{
gridLines: {
display: false,
drawBorder: false,
},
}],
}
};
export default {
name: 'BarChart',
extends: Bar,
mixins: [reactiveChartMixin],
props: {
labels: {
type: [Object, Array],
description: 'Chart labels. This is overridden when `data` is provided'
},
datasets: {
type: [Object, Array],
description: 'Chart datasets. This is overridden when `data` is provided'
},
data: {
type: [Object, Array],
description: 'Chart.js chart data (overrides all default data)'
},
color: {
type: String,
description: 'Chart color. This is overridden when `data` is provided'
},
extraOptions: {
type: Object,
description: 'Chart.js options'
},
title: {
type: String,
description: 'Chart title'
},
},
methods: {
assignChartData() {
let { gradientFill } = this.assignChartOptions(defaultOptions);
let color = this.color || this.fallBackColor;
return {
labels: this.labels || [],
datasets: this.datasets ? this.datasets : [{
label: this.title || '',
backgroundColor: gradientFill,
borderColor: color,
pointBorderColor: "#FFF",
pointBackgroundColor: color,
pointBorderWidth: 2,
pointHoverRadius: 4,
pointHoverBorderWidth: 1,
pointRadius: 4,
fill: true,
borderWidth: 1,
data: this.data || []
}]
}
},
assignChartOptions(initialConfig) {
let color = this.color || this.fallBackColor;
const ctx = document.getElementById(this.chartId).getContext('2d');
const gradientFill = ctx.createLinearGradient(0, 170, 0, 50);
gradientFill.addColorStop(0, "rgba(128, 182, 244, 0)");
gradientFill.addColorStop(1, hexToRGB(color, 0.6));
let extraOptions = this.extraOptions || {}
return {
...initialConfig,
...extraOptions,
gradientFill
};
}
},
mounted() {
this.chartData = this.assignChartData({});
this.options = this.assignChartOptions(defaultOptions);
this.renderChart(this.chartData, this.options, this.extraOptions);
}
}
I use this js file to import the bar chart inside a vue component like you see down below.
everytime the input of the form changes i need to re render the chart. I use the onInputChange() method to turn the boolean loaded to false and call the loadData() method.
Inside the loadData() method I make an axios request that gets me the right data every time. I also get the maximum value for my Y axis.
Then in the response I call on updateChart() so that I can update the data and the max value of the chart. then i turn the boolean loaded to true again so that my chart renders accordingly.
The problem with this approach is that the chart disappears completely for a split of a second. Before deciding to change the max Value of the Y axis I was able to update the data of my chart without having to use the v-if="loaded".
I need to find a solution where the chart re renders without it completely disappearing from the page. I know some suggested to use computed variables but i don't fully understand how it is supposed to work. Here is the component minus the form fields.
I guess in it's essence what I want is to update the Y axis max value without having to re render the entire chart.
<template>
<div>
<BarChart v-if="loaded" :labels="chartLabels"
:datasets="datasets"
:height="100"
:extraOptions="extraOptions"
>
</BarChart>
<br>
</div>
</template>
<script>
import BarChart from '../../components/Library/UIComponents/Charts/BarChart'
import Dropdown from "../../components/Library/UIComponents/Dropdown"
import GroupedMultiSelectWidget from "~/components/widgets/GroupedMultiSelectWidget"
import SelectWidget from "../../components/widgets/SelectWidget";
export default{
name: 'PopularChart',
components: {BarChart, Dropdown, SelectWidget, GroupedMultiSelectWidget},
data(){
return {
loaded:true,
form:{
day: 'Today',
workspace:'',
machine_family: [],
duration: [],
user_group: [],
dt_start:'',
dt_end:''
},
url: `/api/data_app/job_count_by_hour/`,
chart_data: [],
days: [ {day:"Today", id:"Today"},
{day:"Monday", id:"0"},
{day:"Tuesday",id:"1"},
{day:"Wednesday",id:"2"},
{day:"Thursday",id:"3"},
{day:"Friday",id:"4"},
{day:"Saturday",id:"5"},
{day:"sunday",id:"6"} ],
chartLabels: ["00u", "1u", "2u", "3u","4u","5u", "6u", "7u", "8u", "9u", "10u", "11u", "12u", "13u", "14u", "15u","16u", "17", "18u","19u","20u","21u","22u","23u"],
datasets: [],
maximumValue: '',
extraOptions:{}
}
},
methods: {
onInputChange() {
this.loaded = false
this.loadData()
},
async loadData() {
await this.$axios.get(`${this.url}?day=${this.form.day}&date_start=${this.form.dt_start}&date_end=${this.form.dt_end}&workspace=${this.form.workspace}&user_group=${this.form.user_group}&machine_family=${this.form.machine_family}`)
.then(response => {
this.updateChart(response.data.results,response.data.maximum)
this.loaded = true
})
},
updateChart(data,maxValue) {
this.datasets = [{
label: ["jobs %"],
backgroundColor:"#f93232",
data: data
},]
this.maximumValue = maxValue
this.extraOptions = {
tooltips: {
callbacks:{
label: function (tooltipItems,){
if (tooltipItems.value > ((50/100) * maxValue)){
return 'busy';
}else if (tooltipItems.value < ((30/ 100) * maxValue) ){
return ' not busy';
}else if ( tooltipItems.value < ((40/ 100) * maxValue )){
return 'kind of busy'
}
}
}
},
scales: {
yAxes: [{
gridLines: {
zeroLineColor: "transparent",
display: false,
drawBorder: false,
},
ticks: {
max: this.maximumValue,
display: true,
}
}],
xAxes: [{
gridLines: {
zeroLineColor: "transparent",
display: false,
drawBorder: false,
},
}],
},
}
},
},
mounted() {
this.loadData()
},
}
</script>
After checking your code, I noticed that you are using the datasets and maximumValue in data function.
To update the chart data based on dataset and maximumValue, you need to use those variables in computed data, not data.
For example,
computed: {
chartData() {
let chartData = {
labels: [],
datasets: [...],
}
return chartData;
},
maximumValue() {
return this.maxValue;
}
},
methods: {
renderBarChart() {
this.renderChart(this.chartData, {
legend: {
display: false,
},
responsive: true,
maintainAspectRatio: false,
options: {
scales: {
yAxes: [{
ticks: {
max: this.maximumValue
}
}],
},
}
});
},
},

Chart.js is not Dynamically Updating when the firebase databse is updating. (Vue.js)

This is the Visual.vue code that takes in the data from the firebase database and puts it into the chart.
<template>
<div class="container">
<chart :chartData="chartData" class="charts" :width="900" :height="500"></chart>
</div>
</template>
<script>
import Chart from "../Chart/Chart.js";
import firebase from "firebase/app"
import "../Firebase/firebaseinit.js"
export default {
components: {
Chart
},
data () {
return {
values: [],
timelabels: [],
chartData: null,
};
},
created() {
},
mounted() {
this.chartData = {
labels: this.timelabels,
datasets: [
{
label: "Water Level ",
borderColor: "white",
fill: false,
borderWidth: 1,
data: this.values
}
]
}
firebase.database().ref("level").limitToLast(10)
.on("child_added", snap => {this.values.push(snap.val().Water_Level)
});
firebase.database().ref("level").limitToLast(10)
.on("child_added", snap => {this.timelabels.push(snap.val().Time)
});
},
method: {
},
}
</script>
<style scoped>
.charts {
background-color: #1c1c1c;
width: 900px;
margin-left: 20px;
}
</style>
This is the Chart.js file containing all the configuration of the chart.
import {
Line,
mixins
} from 'vue-chartjs'
export default {
extends: Line,
mixins: [mixins.reactiveProp],
props: ['chartData'],
data() {
return {
options: {
layout: {
padding: {
left: 20,
right: 20,
bottom: 20,
top: 10
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
fontColor: 'white',
maxTicksLimit: 10,
},
gridLines: {
display: true,
zeroLineColor: 'red',
color: '#313131',
}
}],
xAxes: [{
ticks: {
fontColor: 'white'
},
gridLines: {
display: true,
zeroLineColor: 'red',
color: '#313131',
}
}]
},
legend: {
display: true,
labels: {
fontColor: 'white'
}
},
responsive: false,
maintainAspectRatio: false
}
}
},
mounted() {
this.renderChart(this.chartData, this.options)
}
}
Can anyone tell me why the chart is not updating? Also, when I refresh the page, the data in the chart disappears. Why? I checked the console, it states 'cannot read property "skip" of undefined'. When i hover over the chart, the number of next to the warning increases as I move the cursor over the chart.
The Chart isn't updating because you are not updating chartData anywhere you are just setting it once in mounted then nothing. you need to update it every time you update values and timelabels maybe like this:
mounted() {
this.chartData = {
labels: this.timelabels,
datasets: [
{
label: "Water Level ",
borderColor: "white",
fill: false,
borderWidth: 1,
data: this.values
}]
}
firebase.database().ref("level").limitToLast(10)
.on("child_added", snap => {
this.values.push(snap.val().Water_Level)
this.chartData.labels = this.values
});
firebase.database().ref("level").limitToLast(10)
.on("child_added", snap => {
this.timelabels.push(snap.val().Time)
this.chartData.datasets[0].data = this.timelabels
});
}
As for why the chart disappears when refreshing the page? because of the error cannot read property "skip" of undefined my biggest guess is because you are setting chartData: null at the beginning. define it like this
chartData: {
labels: [],
datasets: [{}]
}

Categories

Resources