vue-chartjs icon indicating copy to clipboard operation
vue-chartjs copied to clipboard

Mixed chart types support

Open ActuallyHappening opened this issue 1 year ago • 5 comments

Would you like to work on this feature?

  • [ ] Check this if you would like to implement a PR, we are more than happy to help you go through the process.

What problem are you trying to solve?

I have a type issue when I try to mix chart types

Describe the solution you'd like

I would like a specific , preferably typed way, of mixing chart types. As shown in official documentation of ChartJS: https://www.chartjs.org/docs/latest/charts/mixed.html

Describe alternatives you've considered

Access the underlying chart JS instance and manually add my own data

Documentation, Adoption, Migration Strategy

<Bar :data="{
        datasets: [{
            type: 'bar',
            label: 'Bar Dataset',
            data: [10, 20, 30, 40]
        }, {
            type: 'line',
            label: 'Line Dataset',
            data: [50, 50, 50, 50],
        }],
        labels: ['January', 'February', 'March', 'April']
    }" />

ActuallyHappening avatar Jul 07 '23 00:07 ActuallyHappening

Bumping this. Is there any work arounds to do mixed chart types at the moment?

palomamtnez avatar Aug 26 '23 23:08 palomamtnez

Hm, after looking for some other answers on Stackoverflow, I was able to generate a bar chart with line charts on top of it.

Example of my component:

<script setup>
import {Chart as ChartJS, Title, LineElement, PointElement, Tooltip, Legend, BarElement, CategoryScale,Filler, LinearScale} from 'chart.js'
import {Bar} from 'vue-chartjs'
import {computed, defineProps} from 'vue'

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

const props = defineProps({
    labels: Array,
    dataRange: Array,
    dataTrend: Array,
    dataAvg: Array,
})

const chartData = computed(() => {
    return {
        labels: props.labels,
        datasets: [
            {
                type: 'line',
                label: "Average ($)",
                borderColor: "#004BA8",
                backgroundColor: "#004BA8",
                borderWidth: 2,
                data: props.dataAvg,
                tension: 0.1,
            },
            {
                type: 'line',
                label: "Trend ($)",
                backgroundColor: "#E57A44",
                borderColor: "#E57A44",
                borderWidth: 2,
                data: props.dataTrend,
                tension: 0.1,
            },
            {
                type: 'bar',
                label: "Min - Max ($)",
                data: props.dataRange,
                backgroundColor: '#188145a8',
                radius:  props.labels.length > 30 ? 0 : 3,
            },
        ]
    }
})

const chartOptions = computed(() => {
    return {
        tooltips: {
            mode: 'index',
            intersect: true,
            displayColors: false,
        },
        interaction: {
            mode: 'index'
        },
        responsive: true,
        title: {
            display: true,
            text: "Sale Price ($)"
        },
        scales: {
            x: {
                stacked: true,
                time: {
                    // Luxon format string
                    tooltipFormat: 'DD T'
                },
                format: "HH mm",
                title: {
                    display: true,
                    text: 'Date'
                }
            },
            y: {
                stacked: false,
                title: {
                    display: true,
                    text: 'Cents per kg'
                },
                suggestedMin: 0,
                suggestedMax: props.dataAvg.length > 0 ? Math.max(...props.dataAvg)+10 : 0
            }
        },
    }
})
</script>

<template>
    <Bar v-if="dataRange?.length" :data="chartData" :options="chartOptions"/>
</template>

Generates this (what I needed): Screen Shot 2023-08-27 at 11 57 28 AM

Im using: "vue-chartjs": "^5.2.0", "chart.js": "^4.4.0", "vue": "^3.2.31",

palomamtnez avatar Aug 26 '23 23:08 palomamtnez

Hm, after looking for some other answers on Stackoverflow, I was able to generate a bar chart with line charts on top of it

Such a brilliant answer! This example have to be included in documentation 🙏🏻

mewforest avatar Sep 11 '23 12:09 mewforest

In case someone is stuck with typescript error: like Type '{ labels: string[]; datasets: ({ data: number[]; label: string; backgroundColor: string; } | { label: string; data: number[]; type: string; pointStyle: string; pointRadius: number; borderColor: string; })[]; }' is not assignable to type 'ChartData<"bar", (number | [number, number] | null)[], unknown>'. for type mismatch

you need to uncomment the "other" type declaration so if you are using <Bar> only leave the "line" type for combining bar and line charts

let bar_dataset_obj = {
    data: data,
    label: 'Label',
    // type: 'bar', //for mixed charts only the "other" type should be declared
    // backgroundColor: '#85bb65' //for some reason other attributes do not work any longer on this object
  }

  let line_dataset_obj = {
    label: 'Label',
    data: data,
    type: 'line' // this is the other 'type'
  }

raam86 avatar Oct 02 '23 22:10 raam86

Thanks to the people above for figuring this out! However, type support is still needed and @raam86 's suggestion didn't work for me.

Here is my implementation where I have to use any to get past the type fences.

Note: This also happens to be an example of using multiple axes

<template>
  <Bar :data="chartData" :options="options" />
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
import { Bar } from "vue-chartjs";

const options = ref<any>({
  scales: {
    ybar: {
      type: "linear",
      position: "left",
    },
    yline: {
      type: "linear",
      position: "right",
    },
  },
});

const datasets = computed(() => {
  let toPlot= [];

  // This is the bar graph
  toPlot.push({
    label: <bar label>,
    data: <bar data>,
    yAxisID: "ybar",
  });
  // This is the line graph
  toPlot.push({
    type: "line",
    label: <line label>,
    data: <line data>,
    yAxisID: "yline",
  });

  return toPlot;
});

// HERE I need to use `any` to be able to pass into the Bar :data prop
const chartData = computed<any>(() => {
  return { labels: <my labels>, datasets: datasets.value };
});
</script>

wcheek avatar Apr 24 '24 00:04 wcheek