Show/hide dynamic data on button click in vuejs - javascript

I want to dynamically show and hide the line chart on every button click, but when I click on any button, data is not changing. Every time same data is visible to me. Here is my code:
<template>
<div>
<v-btn color="primary" mr-2 #click="changeTab('week')">Week</v-btn>
<v-btn color="error" mr-2 #click="changeTab('month')">Month</v-btn>
<v-btn color="info" mr-2 #click="changeTab('year')">Year</v-btn>
<div v-if="selectedChartData !=null">
<line-chart
:width="650"
:height="400"
:dataSet= "selectedChartData.ChartData"
:labels= "selectedChartData.ChartLabel"
:chartColorsData="selectedChartData.ChartColors"
:label="selectedChartData.ChartData"
>
</line-chart>
</div>
</div>
</template>
<script>
import LineChart from "Components/Charts/LineChart";
import { buySellChartData } from 'Assets/data/buySellChartData.js'
export default {
components:{
LineChart,
},
data() {
return {
buySellChartData,
selectedButton: 'week',
selectedChartData: null,
};
},
mounted(){
this.selectedChart(this.selectedButton);
},
methods:{
selectedChart(selectedButton){
for(var i=0; i< this.buySellChartData.length; i++){
if(this.buySellChartData[i].tag == selectedButton) {
this.selectedChartData = this.buySellChartData[i];
break;
}
}
},
changeTab(selectedBtn){
this.selectedButton = selectedBtn;
this.selectedChart(this.selectedButton);
}
}
};
</script>
where I am assigning selected data to a variable "selectedChartData" on a button click and passing to a line chart component. In "this.buySellChartData[i].tag" tag is having value "year,week or month".
Here is the line chart code:
import { Line } from 'vue-chartjs'
const lineTension = 0.1;
const borderWidth = 3;
const pointRadius = 2;
const pointBorderWidth = 2;
export default {
extends: Line,
props: {
dataSet: {
type: Array
},
label: {
type: Array,
},
labels: {
type: Array
},
chartColorsData:{
type: Array
},
},
data() {
return {
options: {
scales: {
yAxes: [{
gridLines: {
display: true,
drawBorder: true
},
ticks: {
stepSize: 20,
// padding: 5
display:true
}
}],
xAxes: [{
gridLines: {
display: false,
drawBorder: false
},
ticks: {
// padding: 10
display:true
}
}]
},
legend: {
display: false
},
responsive: true,
maintainAspectRatio: false
}
}
},
mounted() {
this.renderChart({
labels: this.labels,
datasets: [
{
label: (this.label[0]).label,
lineTension,
borderColor: this.chartColorsData[0].borderColor,
pointBorderColor: this.chartColorsData[0].borderColor,
pointBorderWidth,
pointRadius,
fill: false,
pointBackgroundColor: '#FFF',
borderWidth,
data: this.dataSet[0].data
},
{
label: this.label[1].label,
lineTension,
borderColor: this.chartColorsData[1].borderColor,
pointBorderColor: this.chartColorsData[1].borderColor,
pointBorderWidth,
pointRadius,
fill: false,
pointBackgroundColor: '#FFF',
borderWidth,
data: this.dataSet[1].data
},
{
label: this.label[2].label,
lineTension,
borderColor: this.chartColorsData[2].borderColor,
pointBorderColor: this.chartColorsData[2].borderColor,
pointBorderWidth,
pointRadius,
fill: false,
pointBackgroundColor: '#FFF',
borderWidth,
data: this.dataSet[2].data
},
]
}, this.options)
}
}
Please open the link to see the screenshot that what kind of chart I am creating https://www.awesomescreenshot.com/image/4110976/35de049e785364eec1006c23301dcf2f. So How it should be possible to show different chart on each button click. If someone needs more info, Please let me know. Any help will be appreciated. Thanks!

vue-chartjs does not provide a live update if you change the datasets. However, vue-chartjs provides two mixins to achieve this.
reactiveProp
reactiveData
so add reactiveProp mixin to you compenent with make a live update of chart on dataset change or update
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Line,
mixins: [reactiveProp],
props: ['options'],
mounted () {
// this.chartData is created in the mixin.
// If you want to pass options please create a local options object
this.renderChart(this.chartData, this.options)
}
}

Related

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

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} />

Is there a way to render same ChartJS component again and again in same page?

I am working with chartJS in react. I have a single chartJs graph component which I want to use more than one. But when page renders it only display first chart and second chart is not displayed.
Below is the Chart Component I want to reuse.
import React, { useEffect } from "react";
import Chart from "chart.js";
var waterFallChart;
export default function WaterFallChart(props) {
useEffect(() => {
if (waterFallChart) {
waterFallChart.destroy();
}
const ctx = document.getElementsByClassName(props.chartClass);
waterFallChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["sun","mon","tue","wed","thurs","fri"],
datasets: [
{
label: 'Good',
data: [10,20,30,40,50,60],
backgroundColor: "#0058FF",
},
]
},
options: {
maintainAspectRatio: false,
showAllTooltips: false,
scales: {
yAxes: [
{
ticks: {
fontColor: "silver",
fontSize: "12",
},
display: true,
gridLines: {
display: true,
zeroLineColor: "white",
zeroLineWidth: "1",
},
},
],
xAxes: [
{
ticks: {
fontColor: "silver",
fontSize: "10",
},
gridLines: {
display: true,
color: "rgb(255,255,255, 0.2)",
zeroLineColor: "white",
zeroLineWidth: "2",
},
},
],
},
legend: {
display: false,
},
},
});
});
return (
<React.Fragment>
<div className="canvasParentDiv">
<canvas className={props.chartClass} />
</div>
</React.Fragment>
);
}
And Main component which uses WaterFallChart component.
import React from 'react'
import WaterFallChart from './WaterFallChart'
function mainComponent() {
return (
<div>
<WaterFallChart chartClass="chart1" />
<WaterFallChart chartClass="chart2" />
<WaterFallChart chartClass="chart2"/>
</div>
)
}
export default mainComponent
Since we can't have same ID for the canvas, I am targeting canvas with className, Yet it just displays one chart and rest two remains hidden.

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