DEV Community

Olowu Abraham
Olowu Abraham

Posted on

Creating line and bar graphs in Vue.js using Chart.js through API calls

I accomplished this task while working on a project, and I believe it would be beneficial to share the template with other developers who may find it useful in the future.

Step-by-Step Guide: Creating a Line Graph with Chart.js in Vue

Step 1: Set Up Your Vue Environment.
Begin by setting up your Vue environment using the Vue CLI. Navigate to the 'src' folder to start your work.

npm create vue@latest
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Component for Your Line Chart (e.g., Chart.vue)

Create a new component to manage your line chart. You can name it, for example, Chart.vue. Before you proceed, ensure that you have installed the Chart.js library in your project, Using this command.

  npm install chart.js
Enter fullscreen mode Exit fullscreen mode

Step 3: Making API Calls

In this step, you'll initiate API calls to fetch data. In my approach, I make the API call in the parent component and pass the retrieved data as a prop to the child component (Chart.js). However, you may opt to make the API call directly within the child component, depending on your preference.

Parent Component (Index.vue):

<template>
  <div>
    <div v-if="isLoading">Loading please wait...</div>
    <Chats :data="prices" v-if="!isLoading && prices.length > 0"></Chats>
  </div>
</template>

<script setup>
import axios from 'axios';
import Chats from "../components/Chats.vue"
import { ref, onMounted } from 'vue';

const prices = ref([]);
const isLoading = ref(true);

onMounted(async () => {
  try {
    const { data } = await axios.get(`http://localhost:5500/prices`);
    console.log('Data fetched successfully:', data);
    prices.value = data;
    isLoading.value = false;
  } catch (error) {
    console.error('Error fetching data:', error);
    prices.value = [];
    isLoading.value = false;
  }
});
</script>

Enter fullscreen mode Exit fullscreen mode

As shown in line 4, I use the v-if directive to display "Loading, please wait" during the API call, ensuring that the Chart component is displayed only when the data is ready. I employ an asynchronous function within the mounted lifecycle hook to consistently call the API when the page is loaded, ensuring the data is always up to date.

It's important to note that I store the retrieved data in line 22 under the "price.value" ref. Storing the data in a ref is crucial as it allows for dynamic updates. I use prices.value = data; to achieve this.

prices.value = data;
Enter fullscreen mode Exit fullscreen mode

Following that, I bind the state [price] to the prop data that the Chart.vue component will utilize.

:data="prices" 
Enter fullscreen mode Exit fullscreen mode

It's essential to ensure that the data passed consists of more than one array, as indicated in
line 5 to eliminate any potential bugs

v-if="!isLoading && prices.length 
Enter fullscreen mode Exit fullscreen mode

This condition ensures that the Chart component is displayed only when there is data available, and the loading process has completed.

step 4:Chart.js

 <template>
  <div class="main">
    <canvas ref="lineChart" :style='{ height: "40rem", width: "100%" }'></canvas>
  </div>
</template>

<script>
import Chart from 'chart.js';

export default {
  props: {
    data: Array,
  },
  data() {
    return {
      chart: null,
    };
  },
  mounted() {
    this.createChart();
  },

  methods: {
    createChart() {
      const data = this.data || [];

      const timestamps = data.map(entry => new Date(entry.timestamp).toLocaleTimeString());
      const values = data.map(entry => entry.value);

      this.chart = new Chart(this.$refs.lineChart.getContext('2d'), {
        type: 'line',
        data: {
          labels: timestamps,
          datasets: [{
            label: 'Value',
            data: values,
            borderColor: 'blue', // change color
            borderWidth: 2,
            fill: false,
          }],
        },
        options: {
          responsive: true,
          maintainAspectRatio: false,
          scales: {
            x: {
              type: 'linear',
              position: 'bottom',
              title: {
                display: true,
                text: 'Timestamp',
              },
            },
            y: {
              type: 'linear',
              position: 'left',
              title: {
                display: true,
                text: 'Value',
              },
            },
          },
        },
      });
    },
  },

  beforeUnmount() {
    if (this.chart) {
      this.chart.destroy();
    }
  },
};
</script>

<style scoped>
.main {
  height: 80vh;
  width: 100%;
}
</style>


Enter fullscreen mode Exit fullscreen mode

First you want to pass your props, import Chart from Chart.js and for this it is advisable you use vUE option api of vue as I did above and I will explain why later.

Now, line 15 to line 68 is provided by chartjs to run the code, but note line 27 and 28 is my x and y axis on the graph so I extracted it from from my api using the map properties. note it varies depending on you api structure.

In your chartjs provided code, there is the object of x and y replace what there with with x and y text in a string. Note it must be a string is "string"

 x: {
               type: 'linear',
                position: 'bottom',
                title: {
                  display: true,
                  text: 'Timestamp',
                },
              },
              y: {
                type: 'linear',
                position: 'left',
                title: {
                  display: true,
                  text: 'Value',
                }
Enter fullscreen mode Exit fullscreen mode

To display your graph insert component into your desired div with the ref. Dont forget your ref as shown below for line
line 3

  <canvas ref="lineChart"></canvas>
Enter fullscreen mode Exit fullscreen mode

The destroy() method is a Chart.js function that cleans up and removes the chart, releasing any resources associated with it.

  beforeUnmount() {
      if (this.chart) {
        this.chart.destroy();
      }
    }

Enter fullscreen mode Exit fullscreen mode

Bars

if you want to use bar rather than line chart use the child code below. change the type and ref.

<template>
  <div class="main">
    <canvas ref="barChart" :style='{height:"40rem", width:"100%"}'></canvas>
  </div>
</template>

<script>
import Chart from 'chart.js';

export default {
  props: {
    data: Array,
  },
  data() {
    return {
      chart: null,
    };
  },
  mounted() {
    this.createChart();
  },
  methods: {
    createChart() {
      const data = this.data || [];

      const timestamps = data.map(entry => new Date(entry.timestamp).toLocaleTimeString());
      const values = data.map(entry => entry.value);

      this.chart = new Chart(this.$refs.barChart.getContext('2d'), {
        type: 'bar', // Change type to 'bar'
        data: {
          labels: timestamps,
          datasets: [{
            label: 'Value',
            data: values,
            backgroundColor: 'blue', // Change background color for bars if you want to
            borderWidth: 2,
          }],
        },
        options: {
          responsive: true,
          maintainAspectRatio: false,
          scales: {
            x: {
              type: 'linear',
              position: 'bottom',
              title: {
                display: true,
                text: 'Timestamp',
              },
            },
            y: {
              type: 'linear',
              position: 'left',
              title: {
                display: true,
                text: 'Value',
              },
            },
          },
        },
      });
    },
  },
  beforeUnmount() {
    if (this.chart) {
      this.chart.destroy();
    }
  },
};
</script>

<style scoped>
.main {
  height: 80vh;
  width: 100%;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Finally, I opted for using the Vue Option API without the setup function. The chart was originally designed to seamlessly integrate with the Vue Option API, and I encountered issues when attempting to modify the code using the Composition API. Therefore, my advice is to stick with the Option API for smoother integration and to avoid potential bugs.

         `   this.chart = new Chart(this.$refs.lineChart.getContext('2d')`
Enter fullscreen mode Exit fullscreen mode

line 30 was giving refs bug with Vue compostion api. that is

<script ></script>
instead of
<script setup></script>
as used in the parent component. `

Outcome

Image description

Top comments (2)

Collapse
 
ozovehe profile image
Obansa John

This was really helpful...thanks

Collapse
 
thetruevillian profile image
Olowu Abraham

ooh thank you for your comment.