Skip to content

Vue-chartjs

Easy and beautiful charts with Chart.js and Vue.js

Vue SPC

<script lang="ts">
import {Component, Prop} from 'vue-property-decorator';
import {mixins} from 'vue-class-component'
import {Bar, mixins as chartJsMixins} from 'vue-chartjs';

const {reactiveProp} = chartJsMixins;

@Component
export default class BarChart extends mixins(Bar, reactiveProp) {
  @Prop({type: Object})
  readonly options!: object;

  mounted () {
    this.renderChart(this.chartData, this.options)
  }
}
</script>

Chart.js 3.xx 지원을 위한 직접 구현

chart.js@3 부터 vue-chartjs가 빌드 안되더라..

그래서 vue-chart-3를 써봤는데 NODE_ENV=Production 빌드시 에러가 발생되더라..

결국 vue-chartjs를 참조하여 직접 구현했다:

<template>
  <div>
    <canvas
        ref="canvas"
        :width="width"
        :height="height"
    ></canvas>
  </div>
</template>

<script lang="ts">
import {Vue, Component, Prop, Ref, Watch, Emit} from 'vue-property-decorator';
import type {
  ChartType,
  ChartData,
  ChartOptions,
  ChartConfiguration,
  Plugin,
} from 'chart.js';
import {Chart as ChartJS} from 'chart.js';

const DEFAULT_TYPE = 'line';
const DEFAULT_WIDTH = 480;
const DEFAULT_HEIGHT = 480;

@Component
export default class Chart extends Vue {
  @Prop({type: String, default: DEFAULT_TYPE})
  readonly type!: ChartType;

  @Prop({type: Number, default: DEFAULT_WIDTH})
  readonly width!: number;

  @Prop({type: Number, default: DEFAULT_HEIGHT})
  readonly height!: number;

  @Prop({type: Array, default: () => []})
  readonly plugins!: Array<Plugin>;

  @Prop({type: Object, default: () => { return {}; } })
  readonly chartData!: ChartData;

  @Prop({type: Object, default: () => { return {}; } })
  readonly options!: ChartOptions;

  @Ref()
  readonly canvas!: HTMLCanvasElement;

  chart?: ChartJS;

  beforeDestroy () {
    if (this.chart) {
      this.chart.destroy();
    }
  }

  renderChart() {
    if (this.chart) {
      this.chart.destroy();
    }

    const context = this.canvas.getContext('2d');
    if (context === null) {
      throw Error('Canvas context is null');
    }

    const config = {
      type: this.type,
      data: this.chartData,
      options: this.options,
      plugins: this.plugins,
    } as ChartConfiguration;
    this.chart = new ChartJS(context, config);
  }

  @Watch('chartData')
  onChangeChartData(newData, oldData) {
    if (oldData && typeof this.chart !== 'undefined') {
      let chart = this.chart;

      const newDatasetLabels = newData.datasets.map(d => d.label);
      const oldDatasetLabels = oldData.datasets.map(d => d.label);

      const oldLabels = JSON.stringify(oldDatasetLabels)
      const newLabels = JSON.stringify(newDatasetLabels)

      // Check if Labels are equal and if dataset length is equal
      if (newLabels === oldLabels && oldData.datasets.length === newData.datasets.length) {
        newData.datasets.forEach((dataset, i) => {
          // Get new and old dataset keys
          const oldDatasetKeys = Object.keys(oldData.datasets[i]);
          const newDatasetKeys = Object.keys(dataset);

          // Get keys that aren't present in the new data
          const deletionKeys = oldDatasetKeys.filter((key) => {
            return key !== '_meta' && newDatasetKeys.indexOf(key) === -1;
          })

          // Remove outdated key-value pairs
          deletionKeys.forEach((deletionKey) => {
            delete chart.data.datasets[i][deletionKey];
          })

          // Update attributes individually to avoid re-rendering the entire chart
          for (const attribute in dataset) {
            if (dataset.hasOwnProperty(attribute)) {
              chart.data.datasets[i][attribute] = dataset[attribute];
            }
          }
        });

        if (newData.hasOwnProperty('labels')) {
          chart.data.labels = newData.labels;
          this.labelsUpdate();
        }
        if (newData.hasOwnProperty('xLabels')) {
          chart.data['xLabels'] = newData.xLabels;
          this.xlabelsUpdate();
        }
        if (newData.hasOwnProperty('yLabels')) {
          chart.data['yLabels'] = newData.yLabels;
          this.ylabelsUpdate();
        }
        chart.update();
        this.chartUpdate();
      } else {
        if (chart) {
          chart.destroy();
          this.chartDestroy();
        }
        this.renderChart();
        this.chartRender();
      }
    } else {
      if (this.chart) {
        this.chart.destroy();
        this.chartDestroy();
      }

      this.renderChart();
      this.chartRender();
    }
  }

  @Emit('labels:update')
  labelsUpdate() {
  }

  @Emit('xlabels:update')
  xlabelsUpdate() {
  }

  @Emit('ylabels:update')
  ylabelsUpdate() {
  }

  @Emit('chart:update')
  chartUpdate() {
  }

  @Emit('chart:destroy')
  chartDestroy() {
  }

  @Emit('chart:render')
  chartRender() {
  }
}
</script>

사용 방법:

      <chart
          type="bar"
          :chart-data="chartData"
          :options="chartOptions"
      ></chart>

See also

Favorite site