I am using chartjs in a nuxt component.
The problem is, I am getting referrence error, chartLabels and chartDataPoints are not defined.
If I replace chartLabels and chartDataPoints with this.$store.state.chartLabels and this.$store.state.chartDataPoints, my chart gets rendered, but then, it is no longer reactive since I need to use computed for that.
The store has been set up properly, and I am getting the correct data in vue devtools so the store doesn't seem to be the issue.
What am I doing wrong?
<canvas id="daily-chart"></canvas>
The chart is getting called in mounted() as so:
mounted() {
const ctx = document.getElementById("daily-chart")
new Chart(ctx, this.chartData)
},
Here is the chartData:
chartData: {
type: "line",
data: {
labels: chartLabels,
datasets: [
{
data: chartDataPoints,
},
],
},
options: {
responsive: true,
lineTension: 1,
},
}
chartDataPoints and chartLabels are being fetched from the store's state:
computed: {
...mapState(['chartDataPoints', 'chartLabels'])
},
If you are using vue-chart-js you may want to declare your chartData inside a specific js file that you would put in your template file.
Here is an example :
import {Line} from "vue-chartjs"
export default {
extends: Line,
data: function(){
return {
datacollection: {
labels: chartLabels,
datasets: [{
data: chartDataPoints,
}],
},
options: {
responsive: true,
lineTension: 1,
},
},
props:{
chartDataPoints : Array,
},
}
Then put the line chart in your template as :
<template>
<line-chart
:chartDataPoints="charData"
>
</line-chart>
</template>
I would also recommand using MapGetters from vuex instead of mapState and putting this in to a computed properties.
computed: {
...mapGetters({
chartDataPoints : 'chartData/getChartDataPoints'
})
},
And in your chartData.js (store) :
export const state = () => ({
charData: [],
})
export const getters = {
getChartDataPoints: (state) => () => {
return this.charData
}
}
Then, you would change your template to :
<template>
<line-chart
:chartDataPoints="chartDataPoints()"
>
</line-chart>
</template>
Related
I created this chartJS map to showcase the distribution of registrations for this course. However, i can only see US in the map. Is there an issue with the library or should i use a different project ?
Thank you in Advance
Here is the code:
fetch('https://unpkg.com/world-atlas/countries-50m.json').then((r) => r.json()).then((data) = {
const countries = ChartGeo.topojson.feature(data, data.objects.countries).features;
const dataCountries = <?= json_encode($WorldMapData) ?>;
const chart = new Chart(canvas.getContext("2d"), {
type: 'choropleth',
data: {
labels: countries.map((d) => d.properties.name),
datasets: [{
label: 'Course Registrations',
data: countries.map((d) => ({
feature: d,
value: dataCountries[dataCountries.indexOf(d.properties.name) + 1],
})),
}]
},
options: {
legend: {
display: true
},
scale: {
projection: 'equalEarth'
},
geo: {
colorScale: {
display: true,
position: 'bottom',
quantize: 1,
legend: {
position: 'bottom-right',
},
},
},
}
});
});
This is what I see:
enter image description here
I tried to get a map showcasing the entire earth but it is only showing US.
I'm trying to make a simple Vue3 app which show graphs using Chart.js
For this I'm trying to replicate the code shown in the vue-chart-3 plugin doc, which shows an example using a Doughnut chart
My objective is to show a Line graph with a horizontal time axis
The code is a simple App.vue which template is
<template>
<LineChart v-bind="lineChartProps" />
</template>
And the script part:
<script lang="ts">
import { computed, defineComponent, ref } from "vue";
import { LineChart, useLineChart } from "vue-chart-3";
import { Chart, ChartData, ChartOptions, registerables } from "chart.js";
Chart.register(...registerables);
export default defineComponent({
name: "App",
components: { LineChart },
setup() {
const dataValues = ref([30, 40, 60, 70, 5]);
const dataLabels = ref(["Paris", "Nîmes", "Toulon", "Perpignan", "Autre"]);
const toggleLegend = ref(true);
const testData = computed<ChartData<"doughnut">>(() => ({
labels: dataLabels.value,
datasets: [
{
data: dataValues.value,
backgroundColor: [
"#77CEFF",
"#0079AF",
"#123E6B",
"#97B0C4",
"#A5C8ED",
],
},
],
}));
const options = computed<ChartOptions<"doughnut">>(() => ({
scales: {
myScale: {
type: "logarithmic",
position: toggleLegend.value ? "left" : "right",
},
},
plugins: {
legend: {
position: toggleLegend.value ? "top" : "bottom",
},
title: {
display: true,
text: "Chart.js Doughnut Chart",
},
},
}));
const { lineChartProps, lineChartRef } = useLineChart({
chartData: testData,
options,
});
function switchLegend() {
toggleLegend.value = !toggleLegend.value;
}
return {
switchLegend,
testData,
options,
lineChartRef,
lineChartProps,
};
},
mounted() {
if (localStorage.data == undefined) {
localStorage.data = JSON.stringify(this.data);
} else {
this.data = localStorage.data;
}
let dataProcessed = JSON.parse(this.data);
// console.log(JSON.parse(this.data));
console.log(dataProcessed.data);
var dates = [];
// Obtain dates from dataProcessed Array
for (var i = 0; i < dataProcessed.data.length; i++) {
dates.push(dataProcessed.data[i].date);
}
this.testData.labes = dates;
console.log(dates);
},
});
</script>
The objective is that the mounted hook gets certain parameters of the LocalStorage and put them in the "labels" array of the "testData" variable, which is the one which aparently stores the X axis data of the chart.
In the VUE developer tool, it can be seen how this assignation process is done correctly, but in the chart of the left side, the data have not been updated.
Thank you for your help :D
I am using vue-chartjs and chartjs-plugin-colorschemes to style a doughnut graph. I'm trying to allow the user to choose from a select which theme they prefer. I have it 90% working; the user can select a theme, hit update, and the doughnut plus its label correctly change color. What doesn't work though, is on initial page load, the doughnut has a color scheme but the legend does not.
I am currently passing a default theme down as props, and I am using a watch method to watch for changes to the theme. The error occurs inside of this watch method.
How can I dynamically update the legend label colors? Here is a minimal example of my component:
<script>
/* eslint-disable no-new */
import convert from 'convert-units';
import { Doughnut } from 'vue-chartjs';
import Chart from 'chart.js';
import { calculateCategoryWeight } from '~/helpers/functions';
import 'chartjs-plugin-colorschemes';
export default {
extends: Doughnut,
props: {
selected: {
type: Object,
default: () => {}
},
theme: {
type: String,
default: ''
}
},
data () {
const vm = this;
return {
chartData: {
labels: this.selected.categories.map(category => {
return this.$options.filters.truncate(category.name, 20);
}),
datasets: [
{
label: 'Selected Graph',
data: this.selected.categories.map(category => {
return parseFloat(convert(category).from('g').to('oz')).toFixed(2);
})
}
]
},
options: {
cutoutPercentage: 75,
legend: {
display: true,
position: 'right'
},
plugins: {
colorschemes: {
scheme: this.theme
}
},
responsive: true,
maintainAspectRatio: false,
tooltips: {
enabled: false
},
}
};
},
mounted () {
this.renderChart(this.chartData, this.options);
},
watch: {
theme (newVal, oldVal) {
const { chart } = this.$data._chart;
chart.options.plugins.colorschemes.scheme = newVal; //<--- updates chart only
chart.update();
}
}
};
</script>
Well I discovered the fix finally.
Essentially in the watch method, I was digging in too deep into the chart instance. By moving up a level in the chart object, both the legend and chart colors are both updated correctly.
watch: {
theme (newVal, oldVal) {
const chart = this.$data._chart; //<-- changed here
chart.options.plugins.colorschemes.scheme = newVal;
chart.update();
}
}
I have an API end point that returns an array of 24 values that I want to use in my chartjs within a vue component.
when the page loads I get no errors but the bars on the charts just don't show and I don't know why.
EDIT: I noticed that the async function returns a promise instead of the actual data:
async filterData() {
await this.$axios.get('/api/data_app/job_count_by_hour/')
.then(response => {
return this.chart_data = response.data;
})
}
here is the data return code, I have a function that populates the chart_data array :
data(){
return {
form:{
day: 'select day',
workspace:'',
machine_family: [],
duration: []
},
res: [],
total:[],
chart_data: [],
url: '/api/jobs/job_count_by_hour/',
days: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "sunday"],
barChart2: {
labels: ["6h", "7h", "8h", "9h","10h","11h", "12h", "13h", "14h", "15h", "16h", "17h", "18h", "19h", "20h", "21h","22h", "23h", "00h"],
datasets: [{
label: ["popularity"],
backgroundColor:"#f93232" ,
data: this.chart_data
},],
},
}
},
methods: {
async filterData() {
let _url = `${this.url}`
await this.$axios.get(_url)
.then(response => {
this.chart_data = response.data;
})
return this.chart_data
},
},
mounted() {
this.filterData()
}
}
this is the chart component:
<script>
import { Line } from 'vue-chartjs'
export default {
extends: Line,
props: {
chartdata: {
type: Object,
default: null
},
options: {
type: Object,
default: null
}
},
mounted () {
this.renderChart(this.chartdata, this.options)
}
}
in the parent component It looks like this:
en <BarChart :labels="barChart2.labels"
:datasets="barChart2.datasets"
:height="100"
>
</BarChart>ter code here
Turns out that when you try to update nested data, the component doesn't re-render.
This is how I solved it, I put the entire object in an update function and call that function when i get my data from the back end, I hope this helps!:
methods: {
onInput(value) {
this.filterData()
},
updateChart(data) {
this.datasets = [{
label: ["popularity"],
backgroundColor:"#f93232",
data: data
}]
},
async loadData() {
await this.$axios.get(this.url)
.then(response => {
this.updateChart(response.data)
})
},
},
mounted() {
this.loadData()
},
I was trying to update a bar chart dataset by appending newer data to it using Chartjs. According to doc here, I need to update the datasets.data array and labels. I created the following component in Ionic:
chart.component.ts
import { SensorDataService } from './sensor-data.service';
import { Component, Input, AfterViewInit, ViewChild, ElementRef } from '#angular/core';
declare var Chart: any;
#Component({
selector: 'app-chart',
templateUrl: './chart.component.html',
styleUrls: ['./chart.component.scss'],
})
export class ChartComponent implements AfterViewInit {
//#Input() chartId: string;
//#Input() chartTitle: string;
#Input() data: Array<number>;
#Input() labels: Array<string>;
//#Input() datasetLabel: string;
interval: any;
count: number;
#ViewChild('chart') chartRef: ElementRef;
chart: any;
constructor(private dataService: SensorDataService) { }
ngAfterViewInit() {
console.log(this.data);
console.log(this.labels);
this.chart = new Chart(this.chartRef.nativeElement, {
type: 'bar',
data: {
labels: this.labels,
datasets: [{
label: 'label',
data: this.data,
backgroundColor: 'rgb(0, 0, 255)',
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
}
}
});
this.count = 0;
this.interval = setInterval(this.loadData.bind(this), 5000);
}
loadData() {
if (this.count > 10) {
return clearInterval(this.interval);
}
const { data, labels } = this.dataService.getData();
console.log('data loaded');
console.log(this.chart.data.labels);
console.log(this.chart.data.datasets[0].data);
this.chart.data.labels.concat(labels);
this.chart.data.datasets.forEach(ds => ds.data.concat(data));
this.count++;
this.chart.update();
}
}
chart.component.html
<ion-card>
<ion-card-header>
<ion-card-title>Title</ion-card-title>
</ion-card-header>
<ion-card-content>
<canvas #chart></canvas>
</ion-card-content>
</ion-card>
From the console log, I don't see any changes in the size of the array. The graph remains the same as well. I created this as a test before connecting the data from http endpoint where, each request will return newer data and I have to append it to the existing chart. What's wrong with my code as given here and why is the chart not updating?
The Array.concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
Therefore, you should change your loadData() method as follows:
loadData() {
if (this.count > 10) {
return clearInterval(this.interval);
}
const { data, labels } = this.dataService.getData();
this.chart.data.labels = this.chart.data.labels.concat(labels);
this.chart.data.datasets.forEach(ds => ds.data = ds.data.concat(data));
this.count++;
this.chart.update();
}