Related
I am trying to use vue-chartjs but implement it with composition api. Most examples are either in typescript which I am not familiar with or using options api. I am unable to get chartOptions to work. I am not sure if I should be removing the chart-options and just do options or if there is something else I am missing such as an import. I tried to import ChartOptions in the chart-js import statement but it draws an error. Any help on how to implement this would be very helpful. Thanks!
<template>
<Pie
:chart-options="chartOptions"
:chart-data="chartData"
:chart-id="chartId"
:dataset-id-key="datasetIdKey"
:plugins="plugins"
:css-classes="cssClasses"
:styles="styles"
:width="width"
:height="height"
/>
</template>
<script>
import {ref, defineComponent, onMounted} from 'vue'
import {Pie} from 'vue-chartjs'
import {Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale} from 'chart.js'
ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale)
export default defineComponent({
name: 'SectorPieChart',
components: { Pie },
props: {
chartId: {
type: String,
default: 'pie-chart'
},
datasetIdKey: {
type: String,
default: 'label'
},
width: {
type: Number,
default: 500
},
height: {
type: Number,
default: 500
},
cssClasses: {
default: '',
type: String
},
styles: {
type: Object,
default: () => {}
},
plugins: {
type: Object,
default: () => {}
}
},
setup() {
//stores
const portfolioStore = usePortfolioStore()
const {portfolio} = storeToRefs(portfolioStore)
//dataset
const chartData = ref({
labels: [ 'Basic Materials', 'Consumer Cyclical', 'Financial Services', 'Real Estate', 'Consumer Defensive', 'Healthcare', 'Utilities', 'Communication Services', 'Energy', 'Industrials', 'Technology'],
datasets: [
{
backgroundColor: ['#FF4A4A','#FFAC4A','#FFE9C9','#F9C87C','#F97432','#7a7979','#FFCC00','#FF9900','#86370e','#FFFF66','#ed9e67'],
data: [1,1,1,1,1,1,1,1,1,1,1]
},
{
backgroundColor: ['#FF4A4A','#FFAC4A','#FFE9C9','#F9C87C','#F97432','#7a7979','#FFCC00','#FF9900','#86370e','#FFFF66','#ed9e67'],
data: [1,1,1,1,1,1,1,1,1,1,1]
}
]
})
//chart options to change settings
const chartOptions = ref({
responsive: true,
maintainAspectRatio: true,
legend: {
display: false,
}
})
//methods
const loadData = () => {
}
//add on mount API request
onMounted(() => {
loadData()
})
return {
chartData, chartOptions, loadData
}
}
})
</script>
According to the vue-chartjs there is no ChartOptions object/function that you can import. You're just meant to create your own array of options and bind it to the :chart-options prop, exactly how you're doing already.
In order to properly set chart options you should follow the chart.js documentation. As the documentation there notes, the legend options are namespaced as options.plugins.legend, and includes boolean property display that can turn off the legend display. This means we should format our chart options like so:
const chartOptions = ref({
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: false,
},
},
});
See codesandbox here.
I am using vue-chartjs to create charts for my application. I am passing the chartData as a prop. My chart doesn't render at first but does when I resize the window. Here is my code. First the chart component:
<script>
import { Doughnut, mixins } from "vue-chartjs";
const { reactiveProp } = mixins;
export default {
extends: Doughnut,
mixins: [reactiveProp],
mounted() {
this.render();
},
methods: {
render() {
console.log(this.chartData)
let options = {
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
},
};
this.renderChart(this.chartData, options);
},
},
};
</script>
Now here is the code from the component where the chart is displayed:
template part
<v-container>
<ProjectDoughnutChart :chart-data="chartData" />
</v-container>
script part
components: {
ProjectDoughnutChart,
},
data() {
return {
chartData: {
labels: [],
datasets: [
{
backgroundColor: [],
hoverBackgroundColor: [],
data: [],
},
],
},
};
},
setChartsTimesheets() {
this.timesheets.forEach((timesheet) => {
let typeTotal = 0;
this.timesheets
.filter((timesheet1) => timesheet1.type==timesheet.type)
.forEach((timesheet1) => {
typeTotal+=timesheet1.billableAmount;
});
if (this.chartData.labels.indexOf(timesheet.type) === -1) {
let colors = this.getTaskColors(timesheet.type);
this.chartData.labels.push(timesheet.type);
this.chartData.datasets[0].data.push(typeTotal);
this.chartData.datasets[0].backgroundColor.push(colors.color);
this.chartData.datasets[0].hoverBackgroundColor.push(colors.hover);
}
});
},
Solved the problem using a similar solution as "Chart with API data" from the documentation.
TL;DR: Adding a v-if on the chart
For people, that have similar problem, but not using vue.js or the official solution doesnt cut it. I had to chart.update() the graph to show values, that were added after the graph was created.
See the example. If you comment the chart.update() line, the graph will not refresh until the window is resized.
let chart = new Chart(document.getElementById("chart"), {
type: "line",
data: {
labels: ["a", "b", "c", "d", "e", "f"],
datasets: [{
label: 'Dataset 1',
data: [1, 5, 12, 8, 2, 3],
borderColor: 'green',
}]
},
options: {
interaction: {
mode: 'index',
intersect: true,
},
stacked: false,
responsive: true,
}
});
// adding data to graph after it was created (like data from API or so...)
chart.data.labels.push("new data");
chart.data.datasets[0].data.push(9);
// with chart.update(), the changes are shown right away
// without chart.update(), you need to resize window first
chart.update();
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js"></script>
<canvas id="chart"></canvas>
Im trying to display chart using chartjs by calling API, but unable to do so.
Here s my LineChart.vue:
<script>
import {Line, mixins} from 'vue-chartjs' // We specify what type of chart we want from vue-chartjs and the mixins module
const { reactiveProp } = mixins
export default { //We are extending the base chart class as mentioned above
extends: Line,
mixins: [reactiveProp],
data () {
return {
options: { //chart options
lineTension: 0,
maintainAspectRatio: false,
legend: {
display: false
},
scales: {
yAxes: [
{
scaleLabel: {
display: false
},
ticks: {
beginAtZero: true,
// eslint-disable-next-line no-unused-vars
callback (value, index, values) {
return `${value }%`
}
}
}
],
xAxes: [
{
type: 'time',
time: {
unit: 'month'
},
scaleLabel: {
display: true,
labelString: ''
}
}
]
}
}
}
},
mounted () {
// this.chartData is created in the mixin
this.renderChart(this.chartData, this.options)
}
}
</script>
And here is my Home.vue where i have imported the LineChart:
<template>
<div class="chart">
<line-chart :chart-data="datacollection"></line-chart>
</div>
</template>
<script>
import LineChart from './LineChart'
import axios from 'axios'
import DateTime from 'luxon'
export default {
data () {
return {
date: {},
challenge: {},
datacollection: {}
}
},
component: {LineChart},
created() {
this.fillData()
},
mounted () {
this.fillData()
},
methods: {
fillData () {
axios.get('https://my_api_goes_here')
.then(response => {
const results = response.data
const dateresult = results.map(a => a.date)
const challengeresult = results.map(a => a.challenge)
this.date = dateresult
this.challenge = challengeresult
this.datacollection = {
labels: [this.date].map(labels => DateTime.fromMillis(labels * 1000).toFormat('MMM yyyy')),
datasets: [
{
data: [this.challenge],
label: 'Africa',
borderColor: '#7367F0'
}
]
}
})
.catch(error => {
console.log(error)
})
}
}
}
</script>
Dont know why the chart did not appear even though my other resources have been loaded from the API call, when i checked out my console, this is what error im getting:
TypeError: results.map is not a function
Please check out my logic and let me where the error is.
So I am new to using data visualization in an application and I am trying to set my data coming from my api as the data the doughnut chart uses to display however I cannot figure out how to properly access the data
I have installed vue-chartjs as a way to simplify it for component use
Here is the Chart component
<script>
import { Doughnut } from 'vue-chartjs'
import {mapGetters} from 'vuex'
export default {
name: 'FirmChart',
extends: Doughnut,
computed: {
...mapGetters(['chartEngagements']),
},
mounted () {
this.renderChart({
labels: ['Scanned', 'Recieved', 'Preparation', 'Review', '2nd Review', 'Complete'],
datasets: [
{
label: 'Data One',
borderColor: 'black',
pointBackgroundColor: 'white',
borderWidth: 1,
pointBorderColor: 'white',
backgroundColor: [
'#0077ff',
'#0022ff',
'#1133bb',
'#0088aa',
'#11ffdd',
'#aabbcc',
'#22aabb',
],
data: [
10,
10,
10,
10,
10,
10,
]
},
]
}, {responsive: true, maintainAspectRatio: false});
},
created() {
this.$store.dispatch('retrieveEngagementsChartData')
}
}
</script>
now my data is coming from the chartEngagements getter and this is the display of that data in the console
{Complete: Array(1), Review: Array(3), 2nd Review: Array(1), Recieved: Array(7), Preparation: Array(1), …}
My question is how do I set the Complete: Array(1) etc to the data[] attribute in my this.renderChart() method??
I have tried doing something like this but It will not display anything
mounted () {
this.renderChart({
labels: ['Scanned', 'Recieved', 'Preparation', 'Review', '2nd Review' 'Complete'],
datasets: [
{
label: 'Data One',
borderColor: 'black',
pointBackgroundColor: 'white',
borderWidth: 1,
pointBorderColor: 'white',
backgroundColor: [
'#0077ff',
'#0022ff',
'#1133bb',
'#0088aa',
'#11ffdd',
'#aabbcc',
'#22aabb',
],
data: [
this.chartEngagements.complete,
this.chartEngagements.review,
this.chartEngagements.2ndreview,
this.chartEngagements.preparation,
this.chartEngagements.recieved,
this.chartEngagements.scanned,
]
},
]
}, {responsive: true, maintainAspectRatio: false});
However it doesn't display anything.. any help would be greatly appreciated or a point in the right direction!
Have you checked out the examples in the docs?
https://vue-chartjs.org/guide/#chart-with-api-data
Your problem is, that your data api call is async. So your chart is rendered, even if your data is not fully loaded.
There is also an example with vuex which is a bit outdated https://github.com/apertureless/vue-chartjs-vuex
You have to make sure that your data is fully loaded before you render your chart.
I faced similar issue while implementing Charts with API data in one of my Vue JS apps I was working on. I am using Vuex for state management, a simple solution for this problem is to move "chartData" from "data" to "computed" which would make it reactive. Below is the sample code from my app.
<template>
<line-chart v-if="chartData"
style="height: 100%"
chart-id="big-line-chart"
:chart-data="chartData"
:extra-options="extraOptions"
>
</line-chart>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import * as expenseTypes from '../../store/modules/expense/expense-types';
import * as chartConfig from "../../components/reports/chart.config";
import BarChart from "../../components/reports/BarChart";
export default {
components: {
BarChart,
},
data () {
return {
extraOptions: chartConfig.chartOptionsMain
}
},
computed: {
...mapGetters({
allExpenses: expenseTypes.GET_ALL_EXPENSES,
expenseCount: expenseTypes.GET_EXPENSE_COUNT,
}),
expenseAmount() {
let expenseAmount = [];
this.allExpenses.map((item) => {
expenseAmount.push(item.amount);
})
return expenseAmount;
},
expenseLabels() {
let expenseLabels = [];
this.allExpenses.map((item) => {
expenseLabels.push(item.note);
})
return expenseLabels;
},
chartData() {
return {
datasets: [
{
data: this.expenseAmount
},
],
labels: this.expenseLabels
}
}
},
async mounted() {
await this.getAllExpenses();
await this.fillChartData();
},
methods: {
...mapActions({
getAllExpenses: expenseTypes.GET_ALL_EXPENSES_ACTION,
}),
},
};
</script>
Linechart.js
import { Line } from 'vue-chartjs'
export default {
extends: Line,
props:['chart']
mounted () {
this.renderChart({
labels: ['1','2','3','4','5','6','7'],
datasets: [
{
label: 'Data One',
backgroundColor: '#F64A32',
data: this.chart
}
]
}, {responsive: true, maintainAspectRatio: false})
}
}
I use the props to pass the data
example.vue
<template>
<line-chart :width="370" :height="246" :chart="chartdata"></line-chart>
</template>
<script>
import LineChart from './vue-chartjs/LineChart'
export default {
components: {
LineChart
},
},
data(){
return{
chartdata:[]
}
}
methods:{
getdata(){
this.chartdata=[10,20,30,40,50]
}
}
</script>
when I click the getdata() the chartdata I think it has been passed to the Linechart.js, But why the chart does not update? Still empty
If you want the data to change on the fly, you either need the reactiveMixin http://vue-chartjs.org/#/home?id=reactive-data
Or you have to trigger an chart update by yourself.
This is because, even Vue.js is reactive, Chart.js per se is not.
If you want to update your chart, simply add a watcher to your LineChart.js component and watch for changes in chart. And then call .update()
import { Line } from 'vue-chartjs'
export default {
extends: Line,
props:['chart']
watch: {
chart () {
this.$data._chart.update()
}
}
mounted () {
this.renderChart({
labels: ['1','2','3','4','5','6','7'],
datasets: [
{
label: 'Data One',
backgroundColor: '#F64A32',
data: this.chart
}
]
}, {responsive: true, maintainAspectRatio: false})
}
}