Search code examples
javascriptvue.jsvuejs2vue-chartjs

Why is vue-chartjs not showing my passed data?


working on a covid app to get familiar with vue2js.

Am now trying to get a graph with vue-chartjs but am failing to pass the data to the graph/chart component.

I make an API request with vuex and passing the data to my component: CountryGraph.vue which contains a Graph.vue with the chart itself.

vuex -> CountryGraph.vue -> Graph.vue

Passing data into CountryGraph.vue works:

Data from vuex into CountryGraph.vue

But when I try to pass my data (countryGraph) as props to my char/Graph.vue component, then it is not done and I get in Graph.vue only the value undefined:

LineChart Undefined

Why?

Below my code, first the CountryGraph.vue:

<template>
    <section class="countryGraph">
        <LineChart
            :chartdata="chartData"
            :options="chartOptions"
        />
    </section>
</template>

<script>
import { mapGetters, mapActions } from "vuex";
import LineChart from "../graph/Graph";

export default {
    name: "CountryGraph",
    components: { LineChart },

    data: () => ({
        chartData: {
            labels: this.countryGraph.map((el) => el.date),
            datasets: [
                {
                    label: "Confirmed",
                    backgroundColor: "#f87979",
                    data: this.countryGraph.map(
                        (el) => el.confirmed
                    ),
                },
            ],
        },
        chartOptions: {
            responsive: true,
            maintainAspectRatio: false,
        },
    }),

    methods: {
        ...mapActions(["selectCountryGraph"]),
    },
    computed: {
        ...mapGetters(["countryGraph"]),
    },
};
</script>

<style></style>

And my chart/Graph.vue component which is made so, that I can reuse it (as stated in vue-chartjs guide):

<script>
import { Bar } from "vue-chartjs";

export default {
    extends: Bar,

    props: {
        chartdata: {
            type: Object,
            default: null,
        },
        options: {
            type: Object,
            default: null,
        },
    },

    mounted() {
        this.renderChart(this.chartdata, this.options);
    },
};
</script>

<style />

When I use mocked data, like instead of

labels: this.countryGraph.map((el) => el.data)

I do labels: ["q", "w", "e", "r", "t"] and instead of

data: this.countryGraph.map(el => el.confirmed)

I do data: [0, 1, 2, 3, 4]

everything works fine.

Also, when I pass my variables directly into the component, like:

<LineChart
            :chartdata="this.countryGraph.map((el) => el.data)"
            :options="chartOptions"
        />

Then I can see the data as props in the child (Graph.vue) component. But in this case I use v-bind: and in the earlier one not. Maybe that is the problem?


Solution

  • A couple issues to note:

    1. It looks like you're mapping a nonexisting property (el.data should be el.date). Possibly just a typo in the question.

      this.countryGraph.map((el) => el.data) ❌
                                          ^
      
    2. data() is not reactive, and cannot rely on computed props, so the countryGraph computed prop will not be available in data() and will not update chartData with changes. One way to fix this is to make chartData a computed prop:

      export default {
          computed: {
              ...mapGetters(["countryGraph"]),
              // don't use an arrow function here, as we need access to component instance (i.e., this.countryGraph)
              chartData() {
                return {
                    labels: this.countryGraph.map((el) => el.date),
                    datasets: [
                        {
                            label: "Confirmed",
                            backgroundColor: "#f87979",
                            data: this.countryGraph.map((el) => el.confirmed),
                        },
                    ],
                }
              }
          }
      }