vuejs3vue-composition-apivue-chartjs

Click event on vue bar chartjs with composition API


I'm trying to create an event that fires when the user click on a single bar, I've read the Chartjs docs and look for others with the same needs but there nothing specific about how to handle this kind of matter I think this method would be the one but don't know how to locate the chart instance. I share my code:

<script setup lang="ts">
import type { IChartObject } from '@/interfaces/IChartObject'
import type { IGrafica } from '@/interfaces/IGrafica'
import axios from 'axios'
import {
  Chart as ChartJS,
  Title,
  Tooltip,
  Legend,
  BarElement,
  CategoryScale,
  LinearScale,
  type ChartData
} from 'chart.js'
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { Bar } from 'vue-chartjs'
import Pivot from 'quick-pivot'

ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend)
ChartJS.defaults.color = 'white'
let initalDataSet: string[][] = [['cuando', 'quien', 'cuanto']]
let pivot: any
let itemReactive = ref<ChartData<'bar'>>({
  datasets: []
})
let temporizador: any = null

const props = defineProps<{
  url: string
}>()

watch(
  () => props.url,
  async (newUrl) => {
    await getDataset(newUrl)
  },
  { immediate: true }
)

const options = {
  responsive: true,
  maintainAspectRatio: true,
  scales: {
    x: {
      stacked: true
    },
    y: {
      stacked: true
    }
  },
  plugins: {
    legend: {
      display: false,
    }
  },
}
let object: IChartObject = {
  labels: [''],
  datasets: [
    {
      label: '',
      backgroundColor: '',
      barThickness: 0,
      borderRadius: 0,
      data: [{}]
    }
  ]
}

onMounted(() => {
  getDataset(props.url)
  temporizador = setInterval(paintChart, 300000)
})

onUnmounted(() => {
  clearInterval(temporizador)
})

async function paintChart() {
  await getDataset(props.url)
}

async function getDataset(url: string): Promise<void> {
  const response = await axios.get<IGrafica[]>(url)
  readyChartDataSet(response.data, object)
  itemReactive = object as any
  emptyObject()
}

function emptyObject(): void {
  object = {
    labels: [''],
    datasets: [
      {
        label: '',
        backgroundColor: '',
        barThickness: 0,
        borderRadius: 0,
        data: [{}]
      }
    ]
  }
}

function dameColorAleatorio(): string {
  //method that give a color to the label...
}

function readyChartDataSet(data: any, chart: IChartObject): void {
  //method that fullfill the dataset...
}

function clickHandler(e: any){
  const chartInstance = document.getElementById('barChart')
}
</script>
<template>
  <Bar :data="itemReactive" id="barChart" @click="clickHandler" :options="options" class="grafica" />
</template>

Solution

  • I did find an answer to my question: the key was in the options object that we inject on the <Bar :data="itemReactive" :options="options" class="grafica" ref="chart "/>. In first place I get the barChart instance by ref template then, I create a global variable to save the instance like this ->

    const chart = ref<HTMLInputElement | null>(null)
    

    after that on onMounted() I use the directive ->

    chart.value?.focus
    

    Finally, there a reserved keyword to the event to capture it on the options object of the bar Chart, I share the code ->

    const options = {
      responsive: true,
      maintainAspectRatio: true,
      scales: {
        x: {
          stacked: true
        },
        y: {
          stacked: true
        }
      },
      plugins: {
        legend: {
          display: false
        }
      },
      onClick: (e: any) => {
        let chartR = Object.assign({}, chart.value) as Object as { chart: ChartJS }
        const points = chartR.chart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, false)
        if (points.length) {
          const firstPoint = points[0]
          label = chartR.chart.data.labels![firstPoint.index] as number
          router.push({ name: 'detalleConsumo', params: { grafica: props.type, label } })
        }
      }
    }
    

    this way every time the user click on a bar we get the label info on the variable label.