Quasar Error Reading External Library Component - vue.js

I have installed an external library (vuejs-progress-bar) in Quasar and I'm getting an error which says:
boot error: TypeError: Cannot read properties of undefined (reading 'component')
I'm not quite sure what went wrong, here's what I did in my boot file:
import { boot } from "quasar/wrappers";
import { ProgressBar } from "vuejs-progress-bar";
export default boot(async ({ app }) => {
app.use(ProgressBar);
});
here's my progress-bar code (I followed from their docs https://github.com/larsmars/vuejs-progress-bar):
<progress-bar :options="options" value="0" />
script:
export default {
data() {
return {
options: {
text: {
color: "#FFFFFF",
shadowEnable: true,
shadowColor: "#000000",
fontSize: 14,
fontFamily: "Helvetica",
dynamicPosition: false,
hideText: false,
},
progress: {
color: "#2dbd2d",
backgroundColor: "#333333",
inverted: false,
},
layout: {
height: 35,
width: 140,
verticalTextAlign: 61,
horizontalTextAlign: 43,
zeroOffset: 0,
strokeWidth: 30,
progressPadding: 0,
type: "line",
},
},
};
},
methods: {},
created() {},
};

Related

How to hanlde async data with vue-echarts?

I am using vue-echarts along with Apache eCharts.
But I do not know how to load async data values.
I have looked through the examples in the docs and I still can't figure it out.
Can anyone take a look at this SFC and tell me how to update the data value inside the gaugeOption ref?
Currently the gaugeData array loads with a value of 0 and stays that way even though props.totalScore gets updated with a new value.
<template>
<vue-echarts
class="echart-container"
autoresize
:option="gaugeOption"
/>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
const props = defineProps({
totalScore: {
type: Number,
required: true,
},
reportDate: {
type: String,
required: true,
},
});
const gaugeData = [
{
value: props.totalScore,
},
];
const gaugeOption = ref({
series: [
{
emphasis: {
disabled: true,
},
data: gaugeData,
type: 'gauge',
startAngle: 180,
endAngle: 0,
min: 0,
max: 100,
splitNumber: 3,
radius: '95%',
center: ['50%', '95%'],
pointer: {
icon: 'circle',
length: '12%',
width: 50,
offsetCenter: [0, '-90%'],
itemStyle: {
color: '#FFFFFF',
borderColor: scoreHexColor,
borderWidth: 5,
shadowColor: 'rgba(10, 31, 68, 0.5)',
shadowBlur: 2,
shadowOffsetY: 0.1,
},
},
axisLine: {
show: true,
roundCap: true,
lineStyle: {
width: 10,
color: [
[0.48, '#ee6352'],
[0.52],
[0.66, '#ff8b3b'],
[0.7],
[0.83, '#fac05e'],
[0.87],
[1, '#59cd90'],
],
},
},
axisTick: {
length: 2,
lineStyle: {
color: '#8a94a6',
width: 2,
},
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
title: {
show: false,
},
detail: {
rich: {
header: {
fontSize: 36,
fontWeight: 700,
fontFamily: 'Open Sans',
color: '#0a1f44',
},
subHeader: {
fontSize: 16,
fontWeight: 400,
fontFamily: 'Open Sans',
color: '#8a94a6',
},
},
formatter: (value: number) => {
return `{header|${value}}\n{subHeader|${props.reportDate}}`;
},
offsetCenter: [0, '-20%'],
valueAnimation: true,
},
},
],
});
</script>
<style lang="scss" scoped>
.echart-container {
height: 300px;
max-width: 300px;
margin-top: -135px;
}
</style>
EDIT UPDATE:
I can now get the chart to load correctly using another ref and a watch of the props.
But when the props change it draws a new number ON TOP of the old one. But I need it to replace the old number.
See here how it looks on page load (this is correct):
And see here how it looks when I try to change the gaugeDataRef value (this is wrong, the value is drawn on top of the old one):
Here is the updated code:
import { computed, ref, watch } from 'vue';
const props = defineProps({
totalScore: {
type: Number,
required: true,
},
reportDate: {
type: String,
required: true,
},
});
const gaugeDataRef = ref<number[]>([]);
watch(props, () => {
gaugeDataRef.value.slice(0, 1);
gaugeDataRef.value.push(props.totalScore);
});
const gaugeOption = ref({
series: [
{
emphasis: {
disabled: true,
},
data: gaugeDataRef.value,
type: 'gauge',
startAngle: 180,
endAngle: 0,
min: 0,
max: 100,
splitNumber: 3,
radius: '95%',
center: ['50%', '95%'],
pointer: {
icon: 'circle',
length: '12%',
width: 50,
offsetCenter: [0, '-90%'],
itemStyle: {
color: '#FFFFFF',
borderColor: scoreHexColor,
borderWidth: 5,
shadowColor: 'rgba(10, 31, 68, 0.5)',
shadowBlur: 2,
shadowOffsetY: 0.1,
},
},
axisLine: {
show: true,
roundCap: true,
lineStyle: {
width: 10,
color: [
[0.48, '#ee6352'],
[0.52],
[0.66, '#ff8b3b'],
[0.7],
[0.83, '#fac05e'],
[0.87],
[1, '#59cd90'],
],
},
},
axisTick: {
length: 2,
lineStyle: {
color: '#8a94a6',
width: 2,
},
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
title: {
show: false,
},
detail: {
rich: {
header: {
fontSize: 36,
fontWeight: 700,
fontFamily: 'Open Sans',
color: '#0a1f44',
},
subHeader: {
fontSize: 16,
fontWeight: 400,
fontFamily: 'Open Sans',
color: '#8a94a6',
},
},
formatter: (value: number) => {
return `{header|${value}}\n{subHeader|${props.reportDate}}`;
},
offsetCenter: [0, '-20%'],
valueAnimation: true,
},
},
],
});
Figured it out.
I needed to add a watch on the props that first clears the array and then adds the new value back into the array.
import { computed, ref, watch } from 'vue';
const props = defineProps({
totalScore: {
type: Number,
required: true,
},
reportDate: {
type: String,
required: true,
},
});
const gaugeDataRef = ref<number[]>([]);
watch(props, () => {
gaugeDataRef.value.slice(0, 1);
gaugeDataRef.value.push(props.totalScore);
});
const gaugeOption = ref({
series: [
{
emphasis: {
disabled: true,
},
data: gaugeDataRef.value,
type: 'gauge',
startAngle: 180,
endAngle: 0,
min: 0,
max: 100,
splitNumber: 3,
radius: '95%',
center: ['50%', '95%'],
pointer: {
icon: 'circle',
length: '12%',
width: 50,
offsetCenter: [0, '-90%'],
itemStyle: {
color: '#FFFFFF',
borderColor: scoreHexColor,
borderWidth: 5,
shadowColor: 'rgba(10, 31, 68, 0.5)',
shadowBlur: 2,
shadowOffsetY: 0.1,
},
},
axisLine: {
show: true,
roundCap: true,
lineStyle: {
width: 10,
color: [
[0.48, '#ee6352'],
[0.52],
[0.66, '#ff8b3b'],
[0.7],
[0.83, '#fac05e'],
[0.87],
[1, '#59cd90'],
],
},
},
axisTick: {
length: 2,
lineStyle: {
color: '#8a94a6',
width: 2,
},
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
title: {
show: false,
},
detail: {
rich: {
header: {
fontSize: 36,
fontWeight: 700,
fontFamily: 'Open Sans',
color: '#0a1f44',
},
subHeader: {
fontSize: 16,
fontWeight: 400,
fontFamily: 'Open Sans',
color: '#8a94a6',
},
},
formatter: (value: number) => {
return `{header|${value}}\n{subHeader|${props.reportDate}}`;
},
offsetCenter: [0, '-20%'],
valueAnimation: true,
},
},
],
});
import { computed, ref, watch } from 'vue';
const props = defineProps({
totalScore: {
type: Number,
required: true,
},
reportDate: {
type: String,
required: true,
},
});
const gaugeDataRef = ref<number[]>([]);
watch(props, () => {
gaugeDataRef.value.slice(0, 1);
gaugeDataRef.value.push(props.totalScore);
});
const gaugeOption = ref({
series: [
{
emphasis: {
disabled: true,
},
data: gaugeDataRef.value,
type: 'gauge',
startAngle: 180,
endAngle: 0,
min: 0,
max: 100,
splitNumber: 3,
radius: '95%',
center: ['50%', '95%'],
pointer: {
icon: 'circle',
length: '12%',
width: 50,
offsetCenter: [0, '-90%'],
itemStyle: {
color: '#FFFFFF',
borderColor: scoreHexColor,
borderWidth: 5,
shadowColor: 'rgba(10, 31, 68, 0.5)',
shadowBlur: 2,
shadowOffsetY: 0.1,
},
},
axisLine: {
show: true,
roundCap: true,
lineStyle: {
width: 10,
color: [
[0.48, '#ee6352'],
[0.52],
[0.66, '#ff8b3b'],
[0.7],
[0.83, '#fac05e'],
[0.87],
[1, '#59cd90'],
],
},
},
axisTick: {
length: 2,
lineStyle: {
color: '#8a94a6',
width: 2,
},
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
title: {
show: false,
},
detail: {
rich: {
header: {
fontSize: 36,
fontWeight: 700,
fontFamily: 'Open Sans',
color: '#0a1f44',
},
subHeader: {
fontSize: 16,
fontWeight: 400,
fontFamily: 'Open Sans',
color: '#8a94a6',
},
},
formatter: (value: number) => {
return `{header|${value}}\n{subHeader|${props.reportDate}}`;
},
offsetCenter: [0, '-20%'],
valueAnimation: true,
},
},
],
});

Remove empty space around a barchart with no axes showing in Chartjs

Fairly straightforward, but I have a single bar on a barchart that I want to show by itself. I've hidden the axes and their ticks, but there is still all of that space there on the left and top//bottom of chart from the axes and the legend I am presuming.
Is there a way to remove this space without just putting negative margins on the element?
I've tried working with negative padding, but that doesn't work on the top and bottom of the chart.
I made a quick CodeSandbox to show my actual component
Any tips would be greatly appreciated!
Cheers!
Horizontal Stacked Bar Chart
<template>
<div style="width: 100%; height: 100%">
<canvas :id="id" width="100%" height="100%"></canvas>
</div>
</template>
<script>
import { defineComponent, onMounted } from "vue"
import Chart from "chart.js/auto"
import ChartDataLabels from "chartjs-plugin-datalabels"
export default defineComponent({
props: {
kind: {
/**
* #example: 'single', 'stacked'
*/
type: String,
default: "stacked",
},
color: {
type: String,
default: "rgba(255, 99, 132, 1)",
},
labels: {
type: Array,
default: () => ["S1", "S2", "S4", "S6"],
},
datasets: {
type: Array,
default: () => [
{
label: "va_value",
data: [80, 10, 60, 50],
backgroundColor: "#11DCB5",
borderColor: "transparent",
barThickness: 20,
minBarLength: 4,
// This is here to show SOMETHING even if the value is 0
},
{
label: "nva_value",
data: [20, 0, 40, 0],
backgroundColor: "#CA2F4B",
borderColor: "transparent",
barThickness: 20,
minBarLength: 4,
},
],
},
},
setup(props, context) {
const id = Math.random().toString()
onMounted(() => {
Chart.register(ChartDataLabels)
const ctx = document.getElementById(id)
const myChart = new Chart(ctx, {
type: "bar",
data: {
labels: props.labels,
datasets: props.datasets,
},
options: {
layout: {
padding: {
left: -20,
bottom: -20,
top: -20
}
},
plugins: {
legend: {
display: false,
},
datalabels: {
formatter: function (value) {
return props.kind === "stacked" ? `${value}s` : `${value}%`
},
color: "#fff",
anchor: "center",
align: "center",font: {
weight: "bold",
},
},
},
responsive: true,
maintainAspectRatio: false,
indexAxis: "y",
scales: {
y: {
beginAtZero: true,
stacked: true,
ticks: {
color: "#fff",
},
grid: {
drawBorder: false,
display: false,
},
},
x: {
display: props.kind === "stacked" ? true : false,
beginAtZero: true,
stacked: true,
ticks: {
color: "#fff",
},
grid: {
drawBorder: false,
display: false,
},
title: {
display: true,
text: props.kind === "stacked" ? "Step Time (s)" : "",
color: "#fff",
},
},
},
},
})
myChart
})
return { id }
},
})
</script>
<style lang=""></style>
This is because you set barThickness to 20 in your datasets. If you remove this it will show as you want:
https://codesandbox.io/s/modest-wave-60xui0

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"
},
},
},
},
}
},

"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.

ECharts - how to inject value from props?

I have the following Chart Component:
<template>
<div class="gauge-chart">
<chart :options="options"></chart>
</div>
</template>
<script>
export default {
props: {
chartValue: { type: Number, required: true },
chartName: { type: String, required: true },
},
data: () => ({
options: {
series: [
{
type: "gauge",
startAngle: 180,
endAngle: 0,
min: 1,
max: 5,
splitNumber: 8,
axisLine: {
lineStyle: {
width: 6,
color: [
[0.25, "#7CFFB2"],
[0.5, "#58D9F9"],
[0.75, "#FDDD60"],
[1, "#FF6E76"],
],
},
},
pointer: {
icon: "arrow",
offsetCenter: [0, "-30%"],
itemStyle: {
color: "auto",
},
},
axisTick: {
length: 12,
lineStyle: {
color: "auto",
width: 1,
},
},
splitLine: {
length: 20,
lineStyle: {
color: "auto",
width: 5,
},
},
title: {
fontSize: 30,
},
data: [
{
value: this.chartValue,
name: this.chartName,
},
],
},
],
},
}),
};
</script>
As you can see I am tryng to inject chartValue and chartName props into options.series.data.value and options.series.data.name respectively.
The values for the properties are coming from
<GaugeChart chartName="Sleep" :chartValue="2" />
At the moment the values are hardcoded, but eventually they will be dynamic.
However it keep throwing the following error:
"TypeError: Cannot read property 'chartName' of undefined"
"TypeError: Cannot read property 'chartValue' of undefined"
I have done a colsole.log of both properties and they come up as "Sleep" and 2. I have also done a typeof on both property names and they both come up as String and Number, respectively.
Could somebody tell me where I am going wrong please?
Many thanks in advance.
You cannot use 'this' operator inside an arrow function so define your data section as a normal function
<script>
export default {
props: {
chartValue: { type: Number, required: true },
chartName: { type: String, required: true },
},
data() {
return {
options: {
series: [
{
type: "gauge",
startAngle: 180,
endAngle: 0,
min: 1,
max: 5,
splitNumber: 8,
axisLine: {
lineStyle: {
width: 6,
color: [
[0.25, "#7CFFB2"],
[0.5, "#58D9F9"],
[0.75, "#FDDD60"],
[1, "#FF6E76"],
],
},
},
pointer: {
icon: "arrow",
offsetCenter: [0, "-30%"],
itemStyle: {
color: "auto",
},
},
axisTick: {
length: 12,
lineStyle: {
color: "auto",
width: 1,
},
},
splitLine: {
length: 20,
lineStyle: {
color: "auto",
width: 5,
},
},
title: {
fontSize: 30,
},
data: [
{
value: this.chartValue,
name: this.chartName,
},
],
},
],
},
};
}
};
</script>
You should use the prop in following manner:
<GaugeChart chart-name="Sleep" chart-value="2" />
Documentation
HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase.