1
votes

I'm using Plotly in Vue, and the plots are not properly rendering when I visit a new route. Only after hovering will the plot change to its proper size. When I change to another route with a different grid (for the same plot UUID), the plot seems to have some kind of wrong size, and it's overflowing the container. But if I hover on it, then it seems fine. Here is my code that I use for the plotting and relayout:

  mounted() {
    Plotly.plot(
      this.$refs[this.chartCopy.uuid],
      this.chartCopy.traces,
      this.chartCopy.layout
    );
    this.$refs[this.chartCopy.uuid].on("plotly_hover", this.hover);
    this.$refs[this.chartCopy.uuid].on("plotly_unhover", this.unhover);
  },
  watch: {
    chart: {
      handler: function () {
        Plotly.react(
          this.$refs[this.chartCopy.uuid],
          this.chartCopy.traces,
          this.chartCopy.layout
        );
      },
      deep: true,
    },
  },

In the options and the layout, I set autosize and responsive to true Is there a way to make the plot already the proper size (both height and width) without the need to hover over it? Here is the reproduced example where this behavior can be clearly seen:

https://codesandbox.io/s/vuetify-with-plotly-20nev

Any kind of relayout sort of destroys the set layout of the plot, and only after hovering, it becomes correct.

One more thing is that I can't use Vue wrapper for Plotly (it has other issues), so I have to stick with using plotly.js library only.

2

2 Answers

2
votes

Responsive charts

To make the charts responsive to the window size, set the responsive property of the config option, which is the fourth argument to Plotly.plot():

// Boxplot.vue
export default {
  mounted() {
    Plotly.plot(
      this.$refs[this.chartCopy.uuid],
      this.chartCopy.traces,
      this.chartCopy.layout,
      { responsive: true } 👈
    )
  }
}

Relayout

To perform relayout of the chart, use an IntersectionObsesrver that calls Plotly.relayout(). This should be setup in the mounted hook and torn down in beforeDestroy hook:

// Boxplot.vue
export default {
  mounted() {
    const chart = this.$refs[this.chartCopy.uuid]
    const intersectionObserver = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          requestIdleCallback(() => Plotly.relayout(entry.target, {}))
        }
      })
    })
    intersectionObserver.observe(chart)
    this._unobserveChart = () => intersectionObserver.unobserve(chart)
  },
  beforeDestroy() {
    this._unobserveChart()
  },
}

Edit Vuetify with Plotly (forked)

1
votes

Is there a reason you use tabs to render the routes? The layout is actually not valid. What actually happens in User.vue is that you render views multiple times. This causes the artifacts:

// you render same contents in all tabs many times
<v-tab-item v-for="tab of tabs" :key="tab.id" :value="tab.route">
  <router-view></router-view>
</v-tab-item>

Just use the <router-link> instead of tabs:

// User.vue
<template>
  <div>
    <router-link v-for="tab of tabs" :to="tab.route" :key="tab.id">
      {{ tab.name }}
    </router-link>
    <div>
      <router-view></router-view>
    </div>
  </div>
</template>

And like @tony19 mentioned, make charts responsive:

 Plotly.plot(
      this.$refs[this.chartCopy.uuid],
      this.chartCopy.traces,
      this.chartCopy.layout,
      { responsive: true }
    );

That's it! Check the sandbox