Refresh the vue-chart component in a nuxt.js application

I'm currently working on a nuxt project and I need to incorporate some charts into it. I'm using vue-chartjs as a plugin for this purpose. However, the issue I'm facing is that the chart data is fetched after the chart has already been drawn, resulting in an empty chart. I'm struggling to find a way to access the plugin and rerender the chart once the data is available. Below is the code snippet for the plugin:

// plugins/vue-chart.js

import Vue from 'vue'
import { Bar, Doughnut, Line, Pie } from 'vue-chartjs'

const registerComponent = function (name, originalComponent) {
  Vue.component(
    name,
    {
      extends: originalComponent,
      props: ['data', 'options'],
      mounted () {
        this.renderChart(this.data, this.options)
      },
      updated () {
        this.renderChart(this.data, this.options)
      }
    }
  )
}

registerComponent('BarChart', Bar)
registerComponent('DoughnutChart', Doughnut)
registerComponent('LineChart', Line)
registerComponent('PieChart', Pie)

Here's how I utilize the plugin to draw a line chart:

components/Charts.vue

<template>
  <client-only>
    <LineChart :data="lineData" :options="options" />
  </client-only>
</template>

<script>
export default {
  data() {
    return {
      loading: true,
      lineData: {
        labels: [],
        datasets: [
          {
            label: 'Close',
            data: [],
          },
          {
            label: 'High',
            data: [],
          },
        ],
      },
      options: {
        responsive: true,
        scales: {
          x: {
            display: true,
            title: {
              display: true,
            },
          },
          y: {
            display: true,
            title: {
              display: true,
              text: 'Value',
            },
          },
        },
      },
    }
  },
  async mounted() {
    try {
      const response = await this.$axios.get('/api/entries')
      if (response.status === 200) {
        for (let i = 0; i < response.data.length; i++) {
          this.lineData.labels.push(response.data[i].date)
          this.lineData.datasets[0].data.push(response.data[i].close)
          this.lineData.datasets[1].data.push(response.data[i].high)
        }
      }
    } catch (e) {
      console.log(e)
    }
  },
}
</script>

I would greatly appreciate any suggestions or solutions to overcome this dilemma!

Edit I had to downgrade the versions of chart.js and vue-chartjs in order to successfully compile the project.

Below is my package.json

{
  "name": "xyz",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev-fe": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
    "lint": "npm run lint:js"
  },
  "dependencies":
    "@nuxtjs/axios": "^5.13.6",
    "chart.js": "^2.7.1",
    "core-js": "^3.15.1",
    "nuxt": "^2.15.7",
    "vue": "^2.6.14",
    "vue-chartjs": "^3.4.0"
  },
  "devDependencies": {
    "@babel/eslint-parser": "^7.14.7",
    "@nuxtjs/eslint-config": "^6.0.1",
    "@nuxtjs/eslint-module": "^3.0.2",
    "@nuxtjs/tailwindcss": "^4.2.0",
    "eslint": "^7.29.0",
    "eslint-plugin-nuxt": "^2.0.0",
    "eslint-plugin-vue": "^7.12.1",
    "postcss": "^8.3.5"
  }
}

My nuxt.config.js file pretty much follows the default settings except for the addition of

{ src: '~/plugins/vue-chart.js', mode: 'client' }

to the list of plugins.

Answer №1

Okay, I have a functional example set up as follows.

Presenting my vue-chartjs.js plugin

import Vue from 'vue'
import { Bar, Doughnut, Line, Pie, mixins } from 'vue-chartjs'

const registerComponent = function (name, originalComponent) {
  Vue.component(name, {
    extends: originalComponent,
    mixins: [mixins.reactiveProp],
    props: {
      chartData: {
        type: Object,
        default: () => {},
      },
      chartOptions: {
        type: Object,
        default: () => {},
      },
    },
    mounted() {
      this.renderChart(this.chartData, this.chartOptions)
    },
  })
}

registerComponent('BarChart', Bar)
registerComponent('DoughnutChart', Doughnut)
registerComponent('LineChart', Line)
registerComponent('PieChart', Pie)

/pages/index.vue

<template>
  <div>
    <line-chart
      :key="updated"
      :chart-data="lineData"
      :chart-options="options"
    />
  </div>
</template>

<script>
import FakeData from '@/fake.json'

export default {
  data() {
    return {
      updated: 0,
      lineData: {
        labels: [],
        datasets: [
          {
            label: 'Data One',
            backgroundColor: '',
            data: [],
          },
          {
            label: 'Data Two',
            backgroundColor: '',
            data: [],
          },
        ],
      },
      options: {
        responsive: true,
        scales: {
          x: {
            display: true,
            title: {
              display: true,
            },
          },
          y: {
            display: true,
            title: {
              display: true,
              text: 'Value',
            },
          },
        },
      },
    }
  },
  async fetch() {
    const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
    const data = await response.json()
    console.log('fake API title fetched:', data.title)

    const actualData = FakeData.data
    for (let i = 0; i < actualData.length; i++) {
      this.lineData.labels.push(actualData[i].date)
      this.lineData.datasets[0].backgroundColor = actualData[i].color1
      this.lineData.datasets[0].data.push(actualData[i].close)
      this.lineData.datasets[1].backgroundColor = actualData[i].color2
      this.lineData.datasets[1].data.push(actualData[i].high)
    }
    this.updated++
  },
}
</script>

and the fabricated content of my dummy .json API since an actual API was not available to retrieve real data

{
  "data": [
    {
      "date": "Jan",
      "color1": "#EC368D",
      "color2": "#51E5FF",
      "close": "0.4",
      "high": "0.7"
    },
    {
      "date": "Feb",
      "color1": "#EC368D",
      "color2": "#51E5FF",
      "close": "0.2",
      "high": "0.5"
    },
    {
      "date": "Mar",
      "color1": "#EC368D",
      "color2": "#51E5FF",
      "close": "0.6",
      "high": "0.8"
    }
  ]
}

I simulated an API call, so it will serve well as a demonstration for you.

The concept behind this approach includes utilizing the key workaround, which may not be ideal, but with limited support from the maintainer.

Nonetheless, this is currently the most viable solution.

Here is the end outcome along with associated details in the Github repository.

https://i.sstatic.net/Apxtp.png

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Detecting URL changes, excluding just the hash, with JavaScript/jQuery

It's interesting to note that some websites are built using a "one-page system". In this type of system, when a user clicks on a link, the site doesn't take them to a new page but instead reloads the existing page with Ajax and updates the URL. ...

The browser does not automatically set the Cookie

Trying to login involves making an API call using a POST HTTP request. post( postLogin(email), JSON.stringify({password: passwd}), { headers: { "Content-Type":"application/json" }, credentials: 'include' // also attempted with &a ...

Efficiently search and filter items across multiple tabs using a single search bar in the Ionic 2

I am currently working on implementing a single search bar that can filter lists in 2 different tabs within Ionic 2. The search bar is functional, and I have a method for filtering through objects. However, my goal is to allow users to select different tab ...

Vue.js - When trying to access the `use` property, it throws an error saying it is undefined

I'm encountering an issue with Vue.js 3 while trying to implement router functionality. Here is my main.js file: import { createApp } from 'vue' import App from './App.vue' import VueRouter from 'vue-router' // ad ...

Remove background image when input form field is in focus

I am currently experimenting with the following approach: $('input').on('click focusin', function() { $('.required').hide(); }); However, it appears that this logic is not functioning as intended. Here is an ...

In a REST api, what is the appropriate response for a property that is missing a value?

Is it better for a property with no value assigned to be returned as null, or should the REST API skip such properties entirely? Let's consider a user object example with first_name and last_name. (In the below example, last_name is not necessarily a ...

What is the best way to utilize an Angular service method from a controller?

As a newcomer to Angular, I have created an 'Employee Search' Service module. Let's take a look at the code: // Employee Search Service app.service('employeeSearchService', function($http, resourceServerAddress){ this.empList ...

How to display a video with full height and width using CSS or JavaScript

I am trying to incorporate an html5 video (using the video tag) with 100% width and 100% height to play in the background. I have seen an example using an image, but I want to achieve the same effect with a video. Here is the code I have so far: #video { ...

Operate on Nested JSON Arrays

I have the following JSON array: var salesData = [ { "products": "Cars", "solddate" : "2022-01-01", "noofitems" : " ...

Is there a way to use ng-click to switch the ng-src of one image with that of another?

*I made updates to the plunkr and code to reflect my localhost version more accurately. It turned out that the AngularJS version was not the issue even after fixing the previous plunkr.* Let me start by saying that I am facing some challenges with Angular ...

Include a parent class within the style tags in your CSS code

Currently, I am facing an issue with a web application that I am developing. On the left side of the page, there is an editable area, and on the right side, there is a live preview. The live preview area contains an HTML file with specific fields that can ...

JavaScript method overloading involves defining multiple functions with the same name

In my JavaScript code, I have implemented method overloading using the following approach: function somefunction() { //1st function } function somefunction(a) { //2nd function } function somefunction(a,b) { //3rd function } somefunction(); // ...

Tips for retrieving specific information from Wikipedia using AJAX

Is there a way to retrieve the information consistently displayed in the right box during searches using AJAX? I've tried using the Wikipedia API, but haven't been able to find the specific information I need. https://i.sstatic.net/wqJEc.png ...

Invoking functions with JavaScript objects

Can anyone help me figure out what is wrong with the following JavaScript code? <form> What is 5 + 5?: <input type ="number" id ="num_answer;"/> </form> <script> function basic_math(){ var num_val = document.getElem ...

Disabling a chosen option within a dropdown menu

`Hello there, I am just starting out with JavaScript and jQuery. Currently, I am developing a web application for ordering food and drinks. I have a dropdown menu for selecting breakfast items and the quantity. When the "add" button is clicked, a dynamic ...

Mobile page sliding mechanism

My website contains a div that is mostly off the page, but on hover it translates onto the main page. You can check out my website. However, this method doesn't work well on mobile devices. Hovering is not effective and I often have to click multipl ...

Angular animation triggered when a specific condition is satisfied

I am working on an animation within my Angular application @Component({ selector: 'app-portfolio', templateUrl: 'portfolio.page.html', styleUrls: ['portfolio.page.scss'], animations: [ trigger('slideInOut&apo ...

Trick to keep horizontal element alignment intact when scroll bar appears

Currently, all my pages are designed with the standard fixed-width-margin-left-right-auto layout. .container{ width:900px; margin:0 auto; } An issue arises when some of these pages exceed the height of the window, requiring a vertical scroll ba ...

Table Header Stays Put Without Shrinking or Expanding with Window Adjustment

I have a sticky table header that stays at the top when scrolling down on my web page. To achieve this effect, I followed the solution provided on css-tricks.com/persistent-headers/. However, I encountered an issue where the sticky table header does not ...

Changing an AngularJS Protractor promise from a string to a decimal number - how to do it?

I am currently working with Angular.js Protractor to retrieve the values of cells in a grid. Although I can successfully retrieve these values, they are strings and I need to perform calculations with them. Upon attempting this: ptor.findElements(protrac ...