Search code examples
vuejs3vue-composition-apivue-chartjs

Vue chart reactive example


I'm trying to make a vue chart with vue-chartjs in vue 3 and composition API, the issue comes when I try to make a chart like this onechart example On this chart I need to put several labels with it's value of consumption on the same bar to difference the consumption of cpu of every user tagged as "label" at the same time.I'm doing it with reactive data so every time I do the http call the chart will rerender it, I look for internet I did not found any similar example

I need to pivot the data from a API call to put 0's when a user is not using cpu in a specific time that's why I use quick-pivot to ready the dataSet that I use on the :data="cpuReactive" as an argument. I share the approach:

<script lang="ts" setup>
import axios from 'axios'
import { onMounted, reactive, ref } from 'vue'
import Pivot from 'quick-pivot'

import {
  Chart as ChartJS,
  Title,
  Tooltip,
  Legend,
  BarElement,
  CategoryScale,
  LinearScale,
  type ChartData
} from 'chart.js'
import { Bar } from 'vue-chartjs'

ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend)

const cpu = {
  labels: [''],
  datasets: [
    {
      label: 'Consumo',
      backgroundColor: '#f87979',
      barThickness: 40,
      borderRadius: 5,
      data: [{}]
    }
  ]
}

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

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

const urlGraficas: string =
  import.meta.env.VITE_APP_ROOT_API + import.meta.env.VITE_BACKEND_DASHBOARD_DESA + '/graficas'

async function pedirGraficas() {
  const { data } = await axios.get<any>(urlGraficas + 'Cpu')

  readyChartDataSet(data)
}

function readyChartDataSet(data: any) {
  data.forEach((element: { cuando: string; usu: string; cpu: number }) => {
    const object: any[] = [element.cuando, element.usu, element.cpu]

    initalDataSet.push(object)
  })

  pivot = new Pivot(initalDataSet, ['quien'], ['cuando'], 'cuanto', 'sum')

  cpu.labels = [...pivot.data.table[0].value]
  cpu.labels.shift()
  cpu.labels.pop()
 {label:string, data: any[]}
  let datasetChart = [...pivot.data.table]
  let finalDataSetChart: { label: string; data: any[] }[] = []
  datasetChart.shift()
  datasetChart.pop()

  datasetChart.forEach((element) => {
    for (let index = 0; index < element.value.length; index++) {
      if (element.value[index] == '') {
        element.value[index] = 0
      }
    }

    const item = {
      label: element.value[0],
      data: element.value
    }

    item.data.shift()
    item.data.pop()
    cpu.datasets[0].data.push(item)
  })
  console.log(cpu.datasets[0].data)
  cpu.datasets[0].data.shift()

}

const options = {
  type:'bar',
  data: cpu.datasets[0].data ,
  responsive: true,
  maintainAspectRatio: true,
  scales: {
    x: {
      stacked: true,
      gridLines: { display: false }
    },
    y:{
      stacked: true
    } 
  }
}

onMounted(async () => {
  await pedirGraficas()
  cpuReactive.value = cpu as any
})
</script>
<template>
  <v-container fluid>
    <v-row class="text-center" justify="center" no-gutters>
      <v-col cols="auto">
        <h2>CPU</h2>
        <Bar :data="cpuReactive" :options="options" />
      </v-col>
    </v-row>
  </v-container>
</template>

If I put the cpu object to reactive mode a execution of mounted hook occurs so that's why i leave it static, any idea on how can I achieve this?


Solution

  • I achieve the effect that I wanted, I share my code so it could help someone:

    <script lang="ts" setup>
    import axios from 'axios'
    import { onMounted, ref } from 'vue'
    import Pivot from 'quick-pivot'
    
    import {
      Chart as ChartJS,
      Title,
      Tooltip,
      Legend,
      BarElement,
      CategoryScale,
      LinearScale,
      type ChartData
    } from 'chart.js'
    import { Bar } from 'vue-chartjs'
    import type { IChartObject } from '@/interfaces/IChartObject'
    //import  GraficaHijo  from '@/components/GraficaHijo.vue'
    
    ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend)
    ChartJS.defaults.color = 'white'
    let gap: number = 5
    
    
    function dameColorAleatorio() {
      let simbolos: string, color: string
      simbolos = '0123456789ABCDEF'
      color = '#'
      for (let i = 0; i < 6; i++) {
        color = color + simbolos[Math.floor(Math.random() * 16)]
      }
      return color
    }
    
    const cpu: IChartObject = {
      labels: [''],
      datasets: [
        {
          label: '',
          backgroundColor: '',
          barThickness: 0,
          borderRadius: 0,
          data: [{}]
        }
      ]
    }
    
    
    const cpuReactive = ref<ChartData<'bar'>>({
      datasets: []
    })
    
    let pivot: any
    let initalDataSet: string[][] = [['cuando', 'quien', 'cuanto']]
    
    const urlGraficas: string =
      import.meta.env.VITE_APP_ROOT_API + import.meta.env.VITE_BACKEND_DASHBOARD_DESA + '/graficas'
    
    async function pedirGraficas() {
      const responseGraficaCpu = await axios.get<any>(`${urlGraficas}cpu?gap=${gap}`)
      console.log(responseGraficaCpu.data)
      readyChartDataSet(responseGraficaCpu.data, cpu)
    
    }
    
    function readyChartDataSet(data: any, chart: IChartObject) {
      initalDataSet = [['cuando', 'quien', 'cuanto']]
      data.forEach((element: { cuando: string; usu: string; cpu: number }) => {
        const object: any[] = [element.cuando, element.usu, element.cpu]
        //Preparamos el array que hay que pivotar que posteriormente se pintará en la gráfica
        initalDataSet.push(object)
      })
    
      //Creamos el objeto de la librería quicl-pivot que formatea los datos como deseamos
      pivot = new Pivot(initalDataSet, ['quien'], ['cuando'], 'cuanto', 'sum')
    
      //agregamos los labels, quitando la primera y la ultimas posiciones que es info sobrante
      chart.labels = [...pivot.data.table[0].value]
      chart.labels.shift()
      chart.labels.pop()
    
      // Agregamos el dataSet que es un array de objetos con formato {label:string, data: any[]}
      let datasetChart = [...pivot.data.table]
      datasetChart.shift()
      datasetChart.pop()
    
      datasetChart.forEach((element) => {
        for (let index = 0; index < element.value.length; index++) {
          if (element.value[index] == '') {
            element.value[index] = 0
          }
        }
        const item = {
          label: element.value[0],
          data: element.value,
          barThickness: 12,
          borderRadius: 5,
          backgroundColor: dameColorAleatorio()
        }
        item.data.shift()
        item.data.pop()
        chart.datasets.push(item)
      })
      //console.log(cpu.datasets[0].data)
      chart.datasets[0].data.shift()
      chart.datasets.shift()
    
      //console.log(cpu)
    }
    
    const options = {
      responsive: true,
      maintainAspectRatio: true,
      scales: {
        x: {
          stacked: true
        },
        y: {
          stacked: true
        }
      },
      plugins: {
        legend: {
          labels: {
            font: {
              size: 16
            }
          }
        }
      }
    }
    
    onMounted(async () => {
      await pedirGraficas()
      cpuReactive.value = cpu as any
    })
    </script>
    <template>
      <v-container fluid>
        <v-row class="text-center" justify="center" no-gutters>
          <v-col cols="auto">
            <h2>CPU</h2>
            <Bar :data="cpuReactive" :options="options" class="grafica" />
          </v-col>
    
        </v-row>
      </v-container>
    </template>