Search code examples
vuejs3vue-composition-apivue-chartjs

how to re render vuechartjs in composition API


I'm trying to re render a chart through an event on a button @click. The scenario is the following: I have a parent component that pass an object's array with all the info that the son component will paint, I retrieve the info from a http call in the parent component so that's the reason why I put the buttons on the parent component, well the first time the component its loaded everything works as expected, but I don't know how and where make fire the event that update the reactive array that re render the chart. Paste a portion of the parent component and all the son component: Father:

<script lang="ts" setup>
import axios from 'axios'
import { onMounted, ref } from 'vue'
import Pivot from 'quick-pivot'
import type { ChartData, ChartOptions } from 'chart.js'
import type { IChartObject } from '@/interfaces/IChartObject'
import GraficaHijo from './GraficasHijos.vue'
import type { IGrafica } from '@/interfaces/IGrafica'

let gap = ref(0)
let graficas = ref([])

const cpu: IChartObject = {
  labels: [''],
  datasets: [
    {
      label: '',
      backgroundColor: '',
      barThickness: 0,
      borderRadius: 0,
      data: [{}]
    }
  ]
}

const io: IChartObject = {
  labels: [''],
  datasets: [
    {
      label: '',
      backgroundColor: '',
      barThickness: 0,
      borderRadius: 0,
      data: [{}]
    }
  ]
}

const sec: IChartObject = {
  labels: [''],
  datasets: [
    {
      label: '',
      backgroundColor: '',
      barThickness: 0,
      borderRadius: 0,
      data: [{}]
    }
  ]
}


const cpuReactive = ref<ChartData<'bar'>>({
  datasets: []
})


const ioReactive = ref<ChartData<'bar'>>({
  datasets: []
})


const secReactive = ref<ChartData<'bar'>>({
  datasets: []
})

let pivot: any
let initalDataSet: string[][] = [['cuando', 'quien', 'cuanto']]


async function pedirGraficas() {

  try {

    const responseGraficaCpu = await axios.get<IGrafica[]>(`${urlGraficas}Cpu?gap=${gap.value}`)

    console.log(responseGraficaCpu.data)

    readyChartDataSet(responseGraficaCpu.data, cpu)

    cpuReactive.value = cpu as any

  } catch (error) {

    console.log(error)

    console.log('algo fue mal en la primera llamada')

  }

  try {

    const responseGraficaIo = await axios.get<any>(`${urlGraficas}Io?gap=${gap.value}`)

    console.log(responseGraficaIo.data)

    readyChartDataSet(responseGraficaIo.data, io)

    ioReactive.value = io as any

  } catch (error) {

    console.log(error)

    console.log('algo fue mal en la segunda llamada')

  }

  try {

    const responseGraficaSec = await axios.get<IGrafica[]>(`${urlGraficas}Sec?gap=${gap.value}`)

    console.log(responseGraficaSec.data)

    readyChartDataSet(responseGraficaSec.data, sec)

    secReactive.value = sec as any

  } catch (error) {

    console.log(error)

    console.log('algo fue mal en la tercera llamada')

  }

}


const options: ChartOptions = {
  responsive: true,
  maintainAspectRatio: true,
  scales: {
    x: {
      stacked: true
    },
    y: {
      stacked: true
    }
  },
  plugins: {
    legend: {
      labels: {
        font: {
          size: 16
        }
      }
    }
  }
}

onMounted(async () => {
  await pedirGraficas()
  graficas.value = <any>[
    { object: cpuReactive, title: 'CPU' },
    { object: ioReactive, title: 'IO' },
    { object: secReactive, title: 'SEC' }
  ]
})

async function backGap() {
  if (gap.value == 168) {
  } else {
    gap.value += 1
    await pedirGraficas()
    graficas.value = <any>[
      { object: cpuReactive, title: 'CPU' },
      { object: ioReactive, title: 'IO' },
      { object: secReactive, title: 'SEC' }
    ]
  }
}
async function forwardGap() {
  if (gap.value == 0) {
  } else {
    gap.value -= 1
    await pedirGraficas()
    graficas.value = <any>[
      { object: cpuReactive, title: 'CPU' },
      { object: ioReactive, title: 'IO' },
      { object: secReactive, title: 'SEC' }
    ]
  }
}
</script>
<template>
  <VContainer fluid>
    <VRow class="text-center" justify="center" no-gutters>
      <!--
      -->
      <VCol cols="auto" v-for="(item, index) in graficas" :key="index">
        <GraficaHijo :item="item" :options="options"></GraficaHijo>
      </VCol>
    </VRow>
    <v-row justify="center">
      <v-btn @click="backGap" class="botonTiempo" color="blue-darken-4" elevation="5" size="x-large"
        ><v-icon>mdi-arrow-left-bold</v-icon></v-btn
      >
      <v-btn
        @click="forwardGap"
        class="botonTiempo"
        color="blue-darken-4"
        elevation="5"
        size="x-large"
><v-icon>mdi-arrow-right-bold</v-icon></v-btn>
    </v-row>
  </VContainer>
</template>

Son:

<script setup lang="ts">
import {
  Chart as ChartJS,
  Title,
  Tooltip,
  Legend,
  BarElement,
  CategoryScale,
  LinearScale
} from 'chart.js'
import { onMounted } from 'vue'
import { Bar } from 'vue-chartjs'

ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend)
ChartJS.defaults.color = 'white'

const props = defineProps<{
  item: any
  options: any
}>()
onMounted(() => {})
defineEmits<{
  (e: "update:chart", id: string): void;
}>();

function updateChart(event: Event){

}
</script>
<template>
  <h2>{{ item.title }}</h2>
  <Bar :data="item.object" :options="options" class="grafica" @change="$emit('update:chart',updateChart($event))" />
</template>

Solution

  • I found the issue: it was rendering the chart well the first time because the charts objects was in it's default state but the following calls the objects needs to be emptied, so after empty the object and put them into the reactive variables everything work fine, I share the parent component:

    <script lang="ts" setup>
    import axios from 'axios'
    import { onMounted, ref } from 'vue'
    import Pivot from 'quick-pivot'
    import type { ChartData, ChartOptions } from 'chart.js'
    import type { IChartObject } from '@/interfaces/IChartObject'
    import GraficaHijo from './GraficasHijos.vue'
    import type { IGrafica } from '@/interfaces/IGrafica'
    
    let gap = ref(0)
    let graficas = ref([])
    
    let cpu: IChartObject = {
      labels: [''],
      datasets: [
        {
          label: '',
          backgroundColor: '',
          barThickness: 0,
          borderRadius: 0,
          data: [{}]
        }
      ]
    }
    
    let io: IChartObject = {
      labels: [''],
      datasets: [
        {
          label: '',
          backgroundColor: '',
          barThickness: 0,
          borderRadius: 0,
          data: [{}]
        }
      ]
    }
    
    let sec: IChartObject = {
      labels: [''],
      datasets: [
        {
          label: '',
          backgroundColor: '',
          barThickness: 0,
          borderRadius: 0,
          data: [{}]
        }
      ]
    }
    
    
    const cpuReactive = ref<ChartData<'bar'>>({
      datasets: []
    })
    
    
    const ioReactive = ref<ChartData<'bar'>>({
      datasets: []
    })
    
    
    const secReactive = ref<ChartData<'bar'>>({
      datasets: []
    })
    
    let pivot: any
    let initalDataSet: string[][] = [['cuando', 'quien', 'cuanto']]
    
    
    async function pedirGraficas() {
    
      try {
    
        const responseGraficaCpu = await axios.get<IGrafica[]>(`${urlGraficas}Cpu?gap=${gap.value}`)
        readyChartDataSet(responseGraficaCpu.data, cpu)
        cpuReactive.value = cpu as any
    cpu = {
      labels: [''],
      datasets: [
        {
          label: '',
          backgroundColor: '',
          barThickness: 0,
          borderRadius: 0,
          data: [{}]
        }
      ]
    }
      } catch (error) {
        console.log(error)
        console.log('algo fue mal en la primera llamada')
    
      }
    
      try {
    
        const responseGraficaIo = await axios.get<any>(`${urlGraficas}Io?gap=${gap.value}`)
        readyChartDataSet(responseGraficaIo.data, io)
        ioReactive.value = io as any
    io = {
      labels: [''],
      datasets: [
        {
          label: '',
          backgroundColor: '',
          barThickness: 0,
          borderRadius: 0,
          data: [{}]
        }
      ]
    }
    
      } catch (error) {
    
        console.log(error)
    
        console.log('algo fue mal en la segunda llamada')
    
      }
      try {
        const responseGraficaSec = await axios.get<IGrafica[]>(`${urlGraficas}Sec?gap=${gap.value}`)
        console.log(responseGraficaSec.data)
        readyChartDataSet(responseGraficaSec.data, sec)
        secReactive.value = sec as any
    sec = {
      labels: [''],
      datasets: [
        {
          label: '',
          backgroundColor: '',
          barThickness: 0,
          borderRadius: 0,
          data: [{}]
        }
      ]
    }
    
      } catch (error) {
    
        console.log(error)
    
        console.log('algo fue mal en la tercera llamada')
    
      }
    
    }
    
    const options: ChartOptions = {
      responsive: true,
      maintainAspectRatio: true,
      scales: {
        x: {
          stacked: true
        },
        y: {
          stacked: true
        }
      },
      plugins: {
        legend: {
          display: false
        }
      }
    }
    
    onMounted(async () => {
      await pedirGraficas()
      graficas.value = <any>[
        { object: cpuReactive, title: 'CPU' },
        { object: ioReactive, title: 'IO' },
        { object: secReactive, title: 'SEC' }
      ]
    })
    
    async function backGap() {
      if (gap.value == 168) {
      } else {
        gap.value += 1
        await pedirGraficas()
        graficas.value = <any>[
          { object: cpuReactive, title: 'CPU' },
          { object: ioReactive, title: 'IO' },
          { object: secReactive, title: 'SEC' }
        ]
      }
    }
    async function forwardGap() {
      if (gap.value == 0) {
      } else {
        gap.value -= 1
        await pedirGraficas()
        graficas.value = <any>[
          { object: cpuReactive, title: 'CPU' },
          { object: ioReactive, title: 'IO' },
          { object: secReactive, title: 'SEC' }
        ]
      }
    }
    </script>
    <template>
      <VContainer fluid>
        <VRow class="text-center" justify="center" no-gutters>
          <!--
          -->
          <VCol cols="auto" v-for="(item, index) in graficas" :key="index">
            <GraficaHijo :item="item" :options="options"></GraficaHijo>
          </VCol>
        </VRow>
        <v-row justify="center">
          <v-btn @click="backGap" class="botonTiempo" color="blue-darken-4" elevation="5" size="x-large"
            ><v-icon>mdi-arrow-left-bold</v-icon></v-btn
          >
          <v-btn
            @click="forwardGap"
            class="botonTiempo"
            color="blue-darken-4"
            elevation="5"
            size="x-large"
    ><v-icon>mdi-arrow-right-bold</v-icon></v-btn>
        </v-row>
      </VContainer>
    </template>