Unable to draw a scatter chart with vue-charts - vue.js

What I end up with is blank page and the error
TypeError: Cannot read property 'getBasePixel' of undefined
and no chart on the page. i'm new to vue.js so might be totally use error but I tried to use mostly the demos from the vue-chartjs page. and chart.js page. It seems as somewhere I screwed up but can't see where. Some people report that chrome. Any assistance with thiis would be appreciated. Trying to bring in a few streams of data..2 to be exact....
CHARTS.JS
import { Scatter, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Scatter,
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)
}
}
Randomchart.vue*
<template>
<div class="small">
<scatter :chart-data="datacollection" :chart-options="options"></scatter>
<button #click="fillData()">Randomize</button>
</div>
</template>
<script>
import Scatter from '#/components/Chart1.js'
export default {
components: {
Scatter
},
data () {
return {
datacollection: null,
options: null
}
},
mounted () {
this.fillData()
},
methods: {
fillData () {
console.log("firing phiil");
this.datacollection = {
datasets: [{
label: 'My First dataset',
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-1',
borderColor: 'rgba(47, 152, 208, 0.2)',
backgroundColor: [
'rgba(47, 152, 208, 0.2)',
],
data: [{
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}]
}, {
label: 'My Second dataset',
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-2',
borderColor: 'rgba(0, 0, 208, 0.2)',
backgroundColor: [
'rgba(47, 152, 208, 0.2)',
],
data: [{
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}]
}]
}
console.log(this.datacollection);
}
}
}
function randomScalingFactor () {
return Math.round(Math.random(-100, 100));
}
</script>
<style>
.small {
max-width: 600px;
margin: 150px auto;
}
</style>

The problem is with extra datasets params xAxisID and yAxisID.
it seems they're not set correctly, so when vue-charts tries getting references to those DOM elements, it gets undefined, thus the error.
You should either remove those params, or add those elements (via options or manually)
Here is a working version of the app
PS: also, if you need to get a random number from the interval [-100, 100], you should use Math.round(Math.random() * 200) - 100.
In your version, it always returns either 1, or 0 (because Math.random takes no parameters and always returns a value between 1 and 0)

Related

ApexCharts y: 0 -> Cannot read properties of undefined

I'm using ApexCharts with vue3 (Composition API). I try to set the series dynamically via a reactive variable. Everything works fine until a dataset is empty - in the sense of its values being 0.
This is the general stub:
<template>
...
<apexchart width="100%" height="400" type="bar" :options="orderOptions" :series="orderSeries" ref="orderChart"></apexchart>
...
</template>
<script setup>
...
const orderSeries = ref([
{
name: 'Orders',
data: []
}
]);
const orderOptions = ref({
xaxis: {
type: 'datetime'
},
yaxis: {
min: 0,
labels: {
formatter: val => val.toFixed(0)
}
},
dataLabels: {
enabled: false
},
markers: {
size: 7,
hover: {
size: 10
}
},
plotOptions: {
bar: {
borderRadius: 4,
borderRadiusApplication: 'end',
columnWidth: '90%'
}
},
title: {
text: 'Orders',
style: {
fontSize: '20px'
}
}
});
...
</script>
This works fine:
orderSeries.value[0].data = [{ x: '2022-03', y: 2 }]
But this:
orderSeries.value[0].data = [{ x: '2022-03', y: 0 }]
Gives me following error:
Uncaught TypeError: Cannot read properties of undefined (reading '0')
at s2 (apexcharts.common.js:10:11991)
...
As long as there is at least one element with a y value different than 0, it will work, but as soon as all y-values are 0, it throws this error.
Can anyone tell me what's wrong with that code?

How can I call a method from options of ApexChart with vue.js

I'm new with vue and apex charts, basically what I need is to call a method from the apex chart options, I created a file showing the problem I'm having:
https://jsfiddle.net/wr3uo5va/
I need to call the method currencyValue from chartOptions.dataLabels
dataLabels: {
enabled: true,
offsetX: -25,
formatter: function(val) {
return val + " Reais"; <--- This works
// return this.currencyValue(val) <--- This does not work
},
},
Any suggestion ?
The problem is this inside the formatter callback is the chart instance (not the component instance) because it's declared as a regular function.
The solution is to use an arrow function to bind the component instance as the context:
export default {
methods: {
currencyValue(value) {⋯},
loadChartData() {
⋮
this.chartOptions = {
⋮
dataLabels: {
⋮
// ❌ don't use regular function here
//formatter: function(val) {
// return this.currencyValue(val)
//},
// ✅
formatter: (val) => {
return this.currencyValue(val)
},
},
}
}
}
}
updated fiddle
You can put chartOptions in methods instead of in data.
Below is working code
const currencyValue = (val) => {
return "R$" + val;
}
new Vue({
el: "#app",
data() {
return {
series: [450, 300, 500]
}
},
methods: {
chartOptions() {
return {
labels: ['Paid', 'Pending', 'Rejected'],
plotOptions: {
radialBar: {
size: 165,
offsetY: 30,
hollow: {
size: '20%'
},
track: {
background: "#ebebeb",
strokeWidth: '100%',
margin: 15,
},
dataLabels: {
show: true,
name: {
fontSize: '18px',
},
value: {
fontSize: '16px',
color: "#636a71",
offsetY: 11
},
total: {
show: true,
label: 'Total',
formatter: function() {
return 42459
}
}
}
},
},
responsive: [{
breakpoint: 576,
options: {
plotOptions: {
radialBar: {
size: 150,
hollow: {
size: '20%'
},
track: {
background: "#ebebeb",
strokeWidth: '100%',
margin: 15,
},
}
}
}
}],
colors: ['#7961F9', '#FF9F43', '#EA5455'],
fill: {
type: 'gradient',
gradient: {
// enabled: true,
shade: 'dark',
type: 'vertical',
shadeIntensity: 0.5,
gradientToColors: ['#9c8cfc', '#FFC085', '#f29292'],
inverseColors: false,
opacityFrom: 1,
opacityTo: 1,
stops: [0, 100]
},
},
stroke: {
lineCap: 'round'
},
chart: {
dropShadow: {
enabled: true,
blur: 3,
left: 1,
top: 1,
opacity: 0.1
},
},
tooltip: {
x: {
formatter: function (val) {
return val;
},
},
y: {
formatter: function (val) {
return currencyValue(val);
},
},
},
}
}
},
components: {
VueApexCharts
}
})
Methods can't be called in data or computed, they can be called in methods
One thing to be modified in html is below
<vue-apex-charts
type="donut"
:options="chartOptions()"
:series="series">
</vue-apex-charts>

Cannot change anything in chart options

I have a bar chart and I would like to change the font color, border width and some other tings, but it doesn't work. It is in my computed property. In the chartOptions I want to change the y axis min and max value but I don't know if it is correct. Can anyone help me?
Also I want to make a horizontal line in this bar chart. It is the "Enemy's Avarage" and now it is a constant. I set the type to line and now I have a dot in my chart.
This is my chart component:
<script>
import { defineComponent } from 'vue'
import { Bar } from 'vue3-chart-v2'
export default defineComponent({
name: 'ChanceChart',
extends: Bar,
props: {
chartData: {
type: Object,
required: true
},
chartOptions: {
type: Object,
required: false,
},
},
mounted () {
this.renderChart(this.chartData, this.chartOptions)
}
})
</script>
And this is my app:
<template>
<div id="chart">
<ChanceChart :chartData="chartData" :chartOptions="chartOptions" />
</div>
</template>
<script>
import Navigation from "../components/Navigation.vue";
import ChanceChart from "../components/ChanceChart.vue";
import PageLoader from "../components/PageLoader.vue";
export default {
components: {
ChanceChart,
},
computed: {
chartOptions() {
return {
options: {
scales: {
y: {
title: {
display: true,
text: 'Value'
},
min: 0,
max: 100,
ticks: {
stepSize: 10,
}
},
},
elements: {
point: {
radius: 0
},
line: {
borderWidth: 2
}
},
plugins: {
legend: {
labels: {
boxWidth: 0,
font: {
fontColor: "#fff",
color: "#fff"
},
},
},
},
}
}
},
chartData() {
return {
datasets: [
{
type: 'bar',
label: "Enemy's Chance",
borderColor: "#1161ed",
borderWidth: 2,
data: this.emnemyCardsFilled,
},
{
type: 'bar',
label: "My Chance",
borderColor: "#f87979",
borderWidth: 2,
data: this.myCardsFilled,
},
{
type: 'line',
label: "Enemy's Avarage",
borderColor: "rgb(238, 255, 0)",
borderWidth: 2,
data: [50],
},
],
}
},
},
You need to remove the options part in the computed chartOptions prop:
chartOptions() {
return {
scales: {
y: {
title: {
display: true,
text: 'Value'
},
min: 0,
max: 100,
ticks: {
stepSize: 10,
}
},
},
elements: {
point: {
radius: 0
},
line: {
borderWidth: 2
}
},
plugins: {
legend: {
labels: {
boxWidth: 0,
font: {
fontColor: "#fff",
color: "#fff"
},
},
},
},
}
},

Chart.js Doughnut with rounded with Vue3 and vue-chart-3

I want to create a donut graphic with rounded arcs. I can't seem to separate the start and end arcs from changing arcs from my sections of the graph. I have this :
I can't find how to modify arc of my sections of the chart. I have this:
I want it to look like this:
/* In parents */
// template
<DoughnutChart class="simulator__doughnut" :chartData="datacollection"/>
// computed
datacollection() {
return {
datasets: [
{
data: [this.monthlyPayment, this.monthlyRate],
backgroundColor: ["#ff0058", "#ff645a"],
borderWidth: 0,
angle: [50],
borderRadius: [{ innerEnd: 50, outerEnd: 50 }],
},
],
};
},
/* In Children*/
<script>
import { DoughnutController, Chart } from "chart.js";
Chart.register(DoughnutController);
export default {
extends: DoughnutController,
mixins: [reactiveProp],
mounted() {
this.renderChart(this.chartData, this.chartOptions);
},
props: {
chartData: {
type: Object,
default: null,
},
chartOptions: {
type: Object,
default: null,
},
},
};
</script>
/* In parents */
// template
<DoughnutChart class="simulator__doughnut" :chartData="datacollection"/>
// computed
datacollection() {
return {
datasets: [
{
data: [this.monthlyPayment, this.monthlyRate],
backgroundColor: ["#ff0058", "#ff645a"],
borderWidth: 0,
angle: [50],
borderRadius: [{ innerEnd: 50, outerEnd: 50, innerStart: 50, outerStart: 50}],
spacing: -50
},
],
};
},

"Maximum call stack size" Error When Adding Data to Chartjs Using Props With Fetch (Vue/Chartjs)

I am trying to use a method to fetch data from a json file and add it to my chart.js chart. I keep getting a "Maximum call stack size exceeded", this is specifically caused by the this.chartData.push(el.value); line, I've tried changing naming around to no success as well as using this.$data.chartData.
I am using vue3, chart.js v3 and j-t-mcc/vue3-chartjs
here is a codesandbox.io of the code with the error.
Child (chart) component
<template>
<div class="card card-body bg-dark">
<div class="col" id="chart">
<vue3-chart-js
ref="chartRef"
:id="sampleChart.id"
:type="sampleChart.type"
:data="sampleChart.data"
:options="sampleChart.options"
></vue3-chart-js>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import Vue3ChartJs from "#j-t-mcc/vue3-chartjs"
import 'chartjs-adapter-date-fns';
var chartOptions = {
maintainAspectRatio: true,
responsive: true,
animation: {
duration: 500
},
plugins: {
legend: {
display: false,
},
tooltip: {
yAlign: "bottom",
},
},
interaction: {
mode: "index",
intersect: false,
axis: "x",
},
scales: {
x: {
type: "time",
time: {
unit: "minute"
}
},
y: {
beginAtZero: true,
},
},
elements: {
point: {
pointRadius: 5.0,
},
},
layout: {
padding: {
top: 20,
left: 10,
right: 10,
bottom: 10,
},
},
}
export default {
name: "Chart",
components: {
Vue3ChartJs,
},
props: {
chartData: Array,
chartLabels: Array
},
setup(props) {
const chartRef = ref(null)
console.log("area chart data", props.chartData)
const chartDetails = {
labels: props.chartLabels,
fill: true,
datasets: [
{
label: "",
data: props.chartData,
borderColor: "rgb(24, 144, 255)",
tension: 0.1,
fill: true,
},
],
}
const sampleChart = {
id: "line",
type: "line",
data: chartDetails,
options: chartOptions,
}
return {
sampleChart,
chartRef
}
},
watch: {
chartLabels: {
deep: true,
handler() {
this.chartRef.update(250)
}
}
},
}
</script>
<style>
#chart {
position: relative;
margin: auto;
height: 100%;
width: 100%;
}
</style>
Parent component
<template>
<div>
<div class="container-fluid">
<SampleChart :chart-data="chartData" :chart-labels="chartLabels" />
</div>
</div>
</template>
<script>
import SampleChart from "./SampleChart.vue";
export default {
components: { SampleChart },
data() {
return {
chartData: [],
chartLabels: [],
};
},
async beforeMount() {
this.getTimelineData();
},
methods: {
getTimelineData: function () {
fetch("http://localhost:8080/sample.json")
.then((res) => res.json())
.then((data) => {
data.data.forEach((el) => {
this.chartData.push(el.value);
this.chartLabels.push(el.timestamp);
});
});
},
},
};
</script>
Package.json dependencies
"dependencies": {
"#j-t-mcc/vue3-chartjs": "^1.1.2",
"bootstrap": "^5.0.2",
"chart.js": "^3.3.2",
"chartjs-adapter-date-fns": "^2.0.0",
"core-js": "^3.6.5",
"date-fns": "^2.23.0",
"leaflet": "^1.7.1",
"vue": "^3.1.5"
}
The Error Message
Uncaught (in promise) RangeError: Maximum call stack size exceeded
at Object.get (reactivity.esm-bundler.js?a1e9:231)
at toRaw (reactivity.esm-bundler.js?a1e9:743)
at Proxy.instrumentations.<computed> (reactivity.esm-bundler.js?a1e9:223)
at Proxy.value (helpers.segment.js?dd3d:1531)
at Proxy.instrumentations.<computed> (reactivity.esm-bundler.js?a1e9:223)
at Proxy.value (helpers.segment.js?dd3d:1531)
at Proxy.instrumentations.<computed> (reactivity.esm-bundler.js?a1e9:223)
at Proxy.value (helpers.segment.js?dd3d:1531)
at Proxy.instrumentations.<computed> (reactivity.esm-bundler.js?a1e9:223)
at Proxy.value (helpers.segment.js?dd3d:1531)
Sample method without fetch that worked fine
getTestData: function () {
var labels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
var values = [10, 25, 39, 55, 90, 202, 304, 202, 105, 33, 44, 95, 20, 39, 90];
labels.forEach((el) => {
this.chartLabels.push(el);
});
values.forEach((el) => {
this.chartData.push(el);
});
},
Json data sample
{
"data": [
{
"timestamp": 1627382793000,
"value": 121
},
{
"timestamp": 1627383698000,
"value": 203
},
{
"timestamp": 1627387917000,
"value": 15
}
]
}
it's work when adding a simple v-if with a ready property that we turn it true when we finish the foreach of pushing data,
the problem is with your SampleChart.vue componenent , you make chart data inside the setup , so when data changed sampleChart will not be changed in any case , it's already calculated.
you can learn more about computed, ref/reactive
While Hossem's answer will work for the first render, the chart still wont be updated when you add new data.
Oddly enough, downgrading Vue one version from 3.1.5 to 3.1.4 ended up resolving the issue.