简体   繁体   中英

update vue-chartjs yaxis max value without re rendering entire vue chart js

I am working on a project where I am implementing some charts from the Vue-Chartjs library. I need the Y-axis max value to change everytime the user changes the filters given. I Import an existing barchart from the vue-chartjs library. In the code there is a javascript file that has some defaults already, to set extra options I can use the extraOptions object as a prop to personalize each chart accordingly. Here is the default component:

import { Bar } from 'vue-chartjs'
import { hexToRGB } from "./utils";
import reactiveChartMixin from "./mixins/reactiveChart";

let defaultOptions = {
  tooltips: {
   tooltipFillColor: "rgba(0,0,0,0.5)",
   tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
   tooltipFontSize: 14,
   tooltipFontStyle: "normal",
   tooltipFontColor: "#fff",
   tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
   tooltipTitleFontSize: 14,
   tooltipTitleFontStyle: "bold",
   tooltipTitleFontColor: "#fff",
   tooltipYPadding: 6,
   tooltipXPadding: 6,
   tooltipCaretSize: 8,
   tooltipCornerRadius: 6,
   tooltipXOffset: 10,
},
legend: {
  display: false
},
scales: {
  yAxes: [{
    ticks: {
      fontColor: "#9f9f9f",
      fontStyle: "bold",
      beginAtZero: true,
      display: false,
      min: 0,
      max: 100
    },
  gridLines: {
    display: false,
    drawBorder: false,
  }
}],
xAxes: [{
  gridLines: {
    display: false,
    drawBorder: false,
   },
 }],
    }
   };
     export default {
        name: 'BarChart',
        extends: Bar,
        mixins: [reactiveChartMixin],
        props: {
        labels: {
        type: [Object, Array],
        description: 'Chart labels. This is overridden when `data` is provided'
    },
    datasets: {
      type: [Object, Array],
      description: 'Chart datasets. This is overridden when `data` is provided'
    },
    data: {
      type: [Object, Array],
      description: 'Chart.js chart data (overrides all default data)'
    },
    color: {
      type: String,
      description: 'Chart color. This is overridden when `data` is provided'
    },
    extraOptions: {
      type: Object,
      description: 'Chart.js options'
    },
    title: {
      type: String,
      description: 'Chart title'
    },
  },
  methods: {
    assignChartData() {
      let { gradientFill } = this.assignChartOptions(defaultOptions);
      let color = this.color || this.fallBackColor;
      return {
        labels: this.labels || [],
        datasets: this.datasets ? this.datasets : [{
          label: this.title || '',
          backgroundColor: gradientFill,
          borderColor: color,
          pointBorderColor: "#FFF",
          pointBackgroundColor: color,
          pointBorderWidth: 2,
          pointHoverRadius: 4,
          pointHoverBorderWidth: 1,
          pointRadius: 4,
          fill: true,
          borderWidth: 1,
          data: this.data || []
        }]
      }
    },
    assignChartOptions(initialConfig) {
      let color = this.color || this.fallBackColor;
      const ctx = document.getElementById(this.chartId).getContext('2d');
      const gradientFill = ctx.createLinearGradient(0, 170, 0, 50);
      gradientFill.addColorStop(0, "rgba(128, 182, 244, 0)");
      gradientFill.addColorStop(1, hexToRGB(color, 0.6));
      let extraOptions = this.extraOptions || {}
      return {
        ...initialConfig,
        ...extraOptions,
        gradientFill
      };
    }
  },
  mounted() {
    this.chartData = this.assignChartData({});
    this.options = this.assignChartOptions(defaultOptions);
    this.renderChart(this.chartData, this.options, this.extraOptions);
  }
}

I use this js file to import the bar chart inside a vue component like you see down below. everytime the input of the form changes i need to re render the chart. I use the onInputChange() method to turn the boolean loaded to false and call the loadData() method. Inside the loadData() method I make an axios request that gets me the right data every time. I also get the maximum value for my Y axis.

Then in the response I call on updateChart() so that I can update the data and the max value of the chart. then i turn the boolean loaded to true again so that my chart renders accordingly.

The problem with this approach is that the chart disappears completely for a split of a second. Before deciding to change the max Value of the Y axis I was able to update the data of my chart without having to use the v-if="loaded".

I need to find a solution where the chart re renders without it completely disappearing from the page. I know some suggested to use computed variables but i don't fully understand how it is supposed to work. Here is the component minus the form fields.

I guess in it's essence what I want is to update the Y axis max value without having to re render the entire chart.

 <template>
      <div>
          <BarChart v-if="loaded" :labels="chartLabels"
                 :datasets="datasets"
                 :height="100"
                 :extraOptions="extraOptions"
          >
          </BarChart>
        <br>
      </div>
    </template>
    <script>
    import BarChart from '../../components/Library/UIComponents/Charts/BarChart'
    import Dropdown from "../../components/Library/UIComponents/Dropdown"
    import GroupedMultiSelectWidget from "~/components/widgets/GroupedMultiSelectWidget"
    import SelectWidget from "../../components/widgets/SelectWidget";
    
    
    export default{
      name: 'PopularChart',
      components: {BarChart, Dropdown, SelectWidget, GroupedMultiSelectWidget},
      data(){
        return {
          loaded:true,
          form:{
              day: 'Today',
              workspace:'',
              machine_family: [],
              duration: [],
              user_group: [],
              dt_start:'',
              dt_end:''
          },
          url: `/api/data_app/job_count_by_hour/`,
          chart_data: [],
          days: [ {day:"Today", id:"Today"},
                  {day:"Monday", id:"0"},
                  {day:"Tuesday",id:"1"},
                  {day:"Wednesday",id:"2"},
                  {day:"Thursday",id:"3"},
                  {day:"Friday",id:"4"},
                  {day:"Saturday",id:"5"},
                  {day:"sunday",id:"6"} ],
          chartLabels: ["00u", "1u", "2u", "3u","4u","5u", "6u", "7u", "8u", "9u", "10u", "11u", "12u", "13u", "14u", "15u","16u", "17", "18u","19u","20u","21u","22u","23u"],
          datasets: [],
          maximumValue: '',
          extraOptions:{}
    
        }
      },
      methods: {
        onInputChange() {
          this.loaded = false
          this.loadData()
        },
        async loadData() {
            await this.$axios.get(`${this.url}?day=${this.form.day}&date_start=${this.form.dt_start}&date_end=${this.form.dt_end}&workspace=${this.form.workspace}&user_group=${this.form.user_group}&machine_family=${this.form.machine_family}`)
              .then(response => {
                this.updateChart(response.data.results,response.data.maximum)
                this.loaded = true
            })
        },
        updateChart(data,maxValue) {
            this.datasets = [{
                  label: ["jobs %"],
                  backgroundColor:"#f93232",
                  data: data
            },]
            this.maximumValue = maxValue
            this.extraOptions = {
              tooltips: {
                callbacks:{
                  label: function (tooltipItems,){
                          if (tooltipItems.value > ((50/100) * maxValue)){
                            return 'busy';
                          }else if (tooltipItems.value < ((30/ 100) * maxValue) ){
                             return ' not busy';
                          }else if ( tooltipItems.value < ((40/ 100) * maxValue )){
                            return 'kind of busy'
                          }
                      }
                }
              },
              scales: {
              yAxes: [{
                  gridLines: {
                    zeroLineColor: "transparent",
                    display: false,
                    drawBorder: false,
                  },
                  ticks: {
                    max: this.maximumValue,
                    display: true,
                  }
              }],
              xAxes: [{
                  gridLines: {
                  zeroLineColor: "transparent",
                  display: false,
                  drawBorder: false,
                  },
              }],
            },
          }
        },
    
      },
      mounted() {
        this.loadData()
      },
    }
    </script>

After checking your code, I noticed that you are using the datasets and maximumValue in data function. To update the chart data based on dataset and maximumValue, you need to use those variables in computed data, not data. For example,

computed: {
   chartData() {
      let chartData = {
         labels: [],
         datasets: [...],
      }
      return chartData;
   },
   maximumValue() {
      return this.maxValue;
   }
},
methods: {
  renderBarChart() {
    this.renderChart(this.chartData, {
      legend: {
        display: false,
      },
      responsive: true,
      maintainAspectRatio: false,
      options: {
        scales: {
          yAxes: [{
              ticks: {
                  max: this.maximumValue
              }
          }],
        },
      }
    });
  },
},

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM