Now that we’re done with the table, we can add the chart. The chart should show the bills that have been received in the last 30 days.

A quick search on Google made me find a great solution to use charts in Vue in the vue-chartjs library, which wraps the popular Chart.js charts library.

Add it as a dependency in CodeSandbox, and also add chart.js as a dependency.

You can also install those 2 libraries locally like we did before:

yarn add vue-chartjs chart.js

# or with npm:

npm install vue-chartjs chart.js

In App.vue, let’s change

<Chart />

to

<Chart :bills="bills" />

so the Charts component has all the things it needs to display bills (and also, when bills are updated, the chart is automatically refreshed)

Now we can focus on the Chart.vue file.

Chart.js can make a wide variety of charts. We want a simple bar graph, so we import Bar from the library:

import { Bar } from 'vue-chartjs'

we also import the Moment library, which we’ll use in a second:

import moment from 'moment'

Now we have some pure JavaScript calculations.

Every bar should show a month, and I want to determine what are the last 12 months, so the current line is the current month, and we have other 11 bars.

Here’s the function I came up with. months is the list of month names, and based on the current month, which we determine from the current date, I fill the orderedMonths array, and I return it.

const last12Months = () => {
  const months = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
  ]

  const today = new Date()
  const orderedMonths = []
  let month = today.getMonth() + 1
  if (month === 12) month = 0 //correctly handle december

  for (let i = 0; i < 12; i++) {
    orderedMonths.push(months[month])
    month === 11 ? (month = 0) : month++
  }

  return orderedMonths
}

Hint: if the function seems complicated, try pasting it in the browser console, and call it using last12Months(), and try to change anything in the code until you’re comfortable with it. Adding a bunch of console.log() statements usually helps.

The next function, processBills, accepts an array of bills, filters out bills older than 1 year, and returns the total amount we paid each month:

const processBills = (bills) => {
  const oneYearAgo = moment().subtract(1, 'years')
  const months = last12Months()
  const monthsWithValues = new Array(12).fill(0)

  for (const month of monthsWithValues) {
    monthsWithValues[month] = 0
  }

  for (const bill of bills) {
    if (moment(bill.date).isSameOrBefore(oneYearAgo)) {
      continue
    }
    const monthName = moment(bill.date).format('MMMM')
    const indexOfMonth = months.indexOf(monthName)
    monthsWithValues[indexOfMonth] += parseInt(bill.amount)
  }

  return monthsWithValues
}

Again, this is just plain JavaScript, you can accept this function as working or inspect it as you wish.

Now we’re at the Vue component code.

The component extends the Bar component we imported from vue-chartjs at the beginning of this lesson, and this is why there is no template part in this component: everything is handled for us.

We just need to call the renderChart method of the Bar component. We do so in the mounted and in the bills watcher, so the chart is re-rendered when the component mounts, and when the bills change.

We abstract that in the displayChart method. We pass in the labels, using the last12Months() function we defined above, and also the months’ data, using processBills().

export default {
  extends: Bar,
  props: ['bills'],
  methods: {
    displayChart: function () {
      this.renderChart({
        labels: last12Months(),
        datasets: [
          {
            label: 'Amount',
            backgroundColor: 'lightblue',
            data: processBills(this.bills),
          },
        ],
      })
    },
  },
  mounted: function () {
    this.displayChart()
  },
  watch: {
    bills: function () {
      this.displayChart()
    },
  },
}

The chart is now fully working! In the example CodeSandbox I added a few Tailwind classes to make it look better, I’ll leave it to you as an exercise to find them out and replicate on your own project.

See the app in the current state on CodeSandbox.


Go to the next lesson