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

Plots not updating on data changes

Open Tehsurfer opened this issue 5 years ago • 28 comments

Hi there! I think the calls to Plotly.react may be broken? (or I could be changing them incorrectly ... I just started Vue.js)

Try this in your App.vue to recreate it

<template>
  <div id="app">
    <plotly :data="plot_data" :layout="layout"></plotly>
    <dat-gui closeText="Close controls" openText="Open controls" closePosition="bottom">
      <dat-string v-model="layout.title" label="Title" />
      <dat-number v-model="plot_data[0].y[0]" :min="-100" :max="100" :step="1" label="Offset y[0]"/>
    </dat-gui>
    <h3>{{layout}}</h3>
<h3>Y values are currently: {{this.plot_data[0].y}}</h3>

  </div>
</template>

<script>
// the javascript below goes here
</script>
import {Plotly}from "vue-plotly";
import Vue from "vue";
import DatGui from "@cyrilf/vue-dat-gui";
Vue.use(DatGui);

export default {
  name: "app",
  components: { Plotly },
  data() {
    return {
      layout: {
        title: 'Plot update test'
      },
      plot_data: [{
        x: [1,2,3,4],
        y: [10, 15, 13, 17],
        type: "scatter"
      }]
    };
  },
};

Tehsurfer avatar Nov 26 '19 04:11 Tehsurfer

Try plotly in lower case:

<plotly
  :data="plot_data"
  :layout="layout"
/>

ped59430 avatar Jan 13 '20 16:01 ped59430

captured Sorry I should have left something to explain what my issues was in more detail. Here is a gif that should explain the chart not updating

Tehsurfer avatar Jan 14 '20 22:01 Tehsurfer

Oh! And do you have Vue Devtools installed? You could see what the values are in your component, in the controls component and in the Plotly component, just by opening the Development tools like this: image

If you would make a codepen or a jsfiddle, I could have a look, but I cannot see why it is not updating at first glance.

Either there is a reactivity problem related to using data-gui, either something else I do not understand for the moment.

ped59430 avatar Jan 14 '20 23:01 ped59430

Yeah I do! Data is is reactive in the 'Plotly' component but not reactive on the chart. I'm thinking there are some issues with the underlying plotly.react call

Tehsurfer avatar Jan 14 '20 23:01 Tehsurfer

Ok ok, that's weird because I use the graphs in my app with a lot of reactivity

ped59430 avatar Jan 14 '20 23:01 ped59430

ahhhh ok that's good to know. Must just be me then.

Tehsurfer avatar Jan 14 '20 23:01 Tehsurfer

Well thanks for your help @ped59430. I've actually switched to another solution for charting now anyways after a bit more experience with Vue

Tehsurfer avatar Jan 14 '20 23:01 Tehsurfer

Ok, the issue was created a long time ago! Just as a side note, I've also tried the other vue plugin (the one from statnett) and experienced some problems with responsiveness. I would be interested to know about your solution, maybe directly by mail if you prefer!

ped59430 avatar Jan 14 '20 23:01 ped59430

So I actually found statnett's one to be quite responsive, I'll share with you how I used his one.

I'm now leaning away from the wrappers though and just calling Plotly directly in a small component. What solution do you currently use?

Tehsurfer avatar Jan 14 '20 23:01 Tehsurfer

Ok for statnett! I must have found a special case. I found another one with this wrapper too anyway (I ve posted my "special" case in this issue #11) I actually use this wrapper and build my own component around it to fetch the data. So now you handle the call to plot or replot on your own, and you have a complete control on options, resposiveness? Might be a good idea for me too...

ped59430 avatar Jan 14 '20 23:01 ped59430

Have a look at this demo for what I used to make statnett's version reactive

The code of interest is here (albeit a bit messy 😨): https://github.com/Tehsurfer/vue-plotsvy/blob/netlify-hosting/src/App.vue

Tehsurfer avatar Jan 14 '20 23:01 Tehsurfer

Thanks!

ped59430 avatar Jan 14 '20 23:01 ped59430

So now you handle the call to plot or replot on your own, and you have a complete control on options, resposiveness? Might be a good idea for me too...

Yeah I started switching to that yesterday, I'm hoping it should give more control.

<template>
  <div class="plot-container">
      <div id='plot'></div>
  </div>
</template>

<script>

import CsvManager from "./csv_manager"
var csv = new CsvManager()
import Plotly from 'plotly.js-dist/plotly'

export default {
  name: "PlotVuer",
  beforeCreate: function() {
  },
  methods: {
    loadURL: function(url) {
      csv.loadFile(url).then( () => {
        this.pdata[0].x = csv.getColoumnByIndex(0).shift();
        this.pdata[0].y = csv.getColoumnByIndex(1).shift();
        this.pdata[0].type = csv.getDataType()
        this.items = csv.getHeaders();
        this.plot_channel(csv.getHeaderByIndex(1))
        return true
      });
    },
    plot_channel: function(channel){
      this.layout.title = channel
      window.pdata = this.pdata
      this.pdata[0].y = csv.getColoumnByName(channel)
      window.ppplot = this.plot
      Plotly.plot('plot', this.pdata)
    },
    react() {
      return Plotly.react('plot', this.pdata)
    }
  },
  props: { url: String},
  data: function() {
    return {
      items: ['first', 'second', 'third'],
      pdata: [{ x: ['1', '2', '3', '4'], y: [10, 25, 20, 50], type: 'scatter' }],
      layout: {
        title: "edit this title"
      },
      channel: 'Select a channel',
      plot: undefined
    };
  },
  computed: {
  },
  created(){
    this.loadURL(this.url)
  },
  mounted(){
    this.$watch('data', () => {
      this.react()
    }, { deep: !this.watchShallow })
    this.$watch('url', () => {
      this.loadURL(this.url)
    }, { deep: !this.watchShallow })
  }
};
</script>

Tehsurfer avatar Jan 14 '20 23:01 Tehsurfer

Yeah! And it redraws on data changes?

ped59430 avatar Jan 15 '20 00:01 ped59430

ahhhhh nope, I need to add that today actually 😅

What's your email? I'd like to DM you a question.

Tehsurfer avatar Jan 15 '20 00:01 Tehsurfer

Edit I just edited the above snippet so that it now redraws on changes. I copied statnet's idea for it and used this.$watch.

Tehsurfer avatar Jan 15 '20 01:01 Tehsurfer

@Tehsurfer , did you try to remove this watch?

this.$watch('data', () => { this.react() }, { deep: !this.watchShallow })

David-Desmaisons avatar Jan 15 '20 01:01 David-Desmaisons

@Tehsurfer , did you try to remove this watch?

Hmmmm, no I didn't modify the library if that's what you mean?

Also, having a look, you use this.$nextTick don't you?

https://github.com/David-Desmaisons/vue-plotly/blob/0360a0cdf6e8b496ad3ee089c119ef429c2a8682/src/components/Plotly.vue#L90

Tehsurfer avatar Jan 15 '20 01:01 Tehsurfer

I mean the watch in the code sample you share here as you wouldn't need to watch data by yourself.

David-Desmaisons avatar Jan 15 '20 02:01 David-Desmaisons

Also, having a look, you use this.$nextTick don't you?

yes.

David-Desmaisons avatar Jan 15 '20 02:01 David-Desmaisons

I mean the watch in the code sample you share here as you wouldn't need to watch data by yourself.

Oh yeah, that example updates successfully, check it out here: https://vue-plotsvy-demo.netlify.com/

Tehsurfer avatar Jan 15 '20 03:01 Tehsurfer

I'm also having a problem with that my plot is not updating when the data is changing.

Here is my App.Vue

<template>
  <div id="app">
    <PlotlyTest></PlotlyTest>
  </div>
</template>

<script>
import PlotlyTest from "./components/PlotlyTest.vue";
export default {
  components: {
    PlotlyTest
  },
  computed: {
    code() {
      const fromAttr = Object.keys(this.data.attr)
        .map(key => `:${key}="${this.data.attr[key]}"`)
        .join(" ");
      return `<plotly :data="data" :layout="layout" ${fromAttr}/>`;
    }
  }
};
</script>

and here is my test component:

<template>
  <div>
    <plotly :data="data" :layout="layout" />
    <button v-on:click="button_click_func">You clicked me {{ count }} times.</button>
  </div>
</template>

<script>
import { Plotly } from "vue-plotly";

export default {
  components: {
    Plotly
  },
  data: function() {
    return {
      count: 0,
      message: 'not updated',
      data:[{ x: [1,2,3,4], y: [10,15,13,17], type:"scatter" }],
      attr: { displayModeBar: true },
      layout: { title: "My graph :)" }
    };
  },
  methods: {
    button_click_func: function() {
      console.log("Clicked the button: " + this.count, " y[0] = ", this.data[0].y[0]);
      this.count += 1;
      // See https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
      this.$set(this.data[0].y, 0, this.data[0].y[0] + 1)
    },
    react: function() {
      console.log("I am reacting")
    }
  },
  mounted() {
    this.$watch("count", () => {
      console.log("Watching data!");
      this.react()},  { deep: !this.watchShallow })
  }
};
</script>

I was trying to call Plotly.react directly in the react function. I tried this here

return Plotly.react('plot', this.data)

But this only gave me an error [Vue warn]: Error in callback for watcher "count": "TypeError: vue_plotly__WEBPACK_IMPORTED_MODULE_0__.Plotly.react is not a function"

rkube avatar Apr 09 '20 01:04 rkube

@Tehsurfer I'm trying your approach of using Plotly directly within Vue components. Using your code from above I get the error "Error: "No DOM element with id 'plot' exists on the page.". Do you know why it can't find the element from the template there?

rkube avatar Apr 09 '20 02:04 rkube

Yes I think I have an idea why. I wasn't using global DOM names instead of passing in a vue reference to the DOM (We need to do the latter).

I now call react like so:

<template>
  <div ref="plotContainer" ></div>
</template>
react() {
  return Plotly.react(this.$refs.plotContainer, this.data, this.layout, this.getOptions())
 },

Tehsurfer avatar Apr 17 '20 03:04 Tehsurfer

@Tehsurfer Oh, I see. I didn't know how this works. So vue can't resolve references by value (equivalent to passing the string "plotContainer" in the Plotly.react call). Instead it creates a reference to the DOM component, called this.$refs.plotContainer". This is also explained in the vue docs

I solved it by passing the ref tag for the plotly plot as a prop. My vue code is inside a component, that way I can instantiate multiple components on the same page and each component has a differently named plot div.

The example below uses websockets. In the created() function I register a socket callback on the event "new_data". This callback updates the plotly plot, as references by the prop "plotid".

<template>
  ...
  <div class="column" style="background-color:#bbb;">
    <div :id="plotid"></div>
  </div>
</template>

And the component code updates the plot like this

export default {
...
  props: ["plotid"],
  created() {
      // I need to reference this in a local variable for access in the socket.on call
      var vm = this;
      socket.on("new_data", function(msg) {
        
        Plotly.restyle(vm.$props.plotid, update);
     }
  }
}

rkube avatar Apr 17 '20 12:04 rkube

Hello!! I am with a similar problem, although it is a 3D graph made with plotly.js and has a dat.gui controller that modifies one of the parameters. My problem is that when changing the parameter, it makes me a new graph over the previous one, it does not refresh the graph. I hope you can help me.


<head>
	<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
    <script src="dat.gui.js"></script>
</head>

<body>
	<div id='myDiv'></div>
<script >

var button_layer_1_height = 1.2
var button_layer_2_height = 1.1
var annotation_offset = 0.04


	//Defino parámetro a controlar con dat.gui		
    

var zPts = []; 
var xPts = [];
var yPts = [];
  //Control GUI



   
  var updateGraphFunc = function(){

var G=0.1;

for(x=-3; x<=3; x+= 0.01) {
  let zdat = [];
  let ydat = [];
  let xdat = [];
  for (y=0; y<=0.5; y+=0.01) {
    zdat.push( 2*0.0625*[Math.sqrt(2/ (Math.PI* (params.G**2 + (y**2 / params.G**2)))) * Math.exp(-2*((x**2)+0.25) / [params.G**2 + (y**2/params.G**2)])] * [Math.cosh(2*x/(params.G**2 + (y**2/params.G**2))) + Math.cos( (2*x*y) / params.G**4 + y**2)] );
    ydat.push(y);
    xdat.push(x);
  }
  zPts.push(zdat);
  yPts.push(ydat);
  xPts.push(xdat);
  }

var data = [{
    z: zPts,
    x: xPts,
    y: yPts,
    type: 'surface',
    colorscale:'Jet',
    contours: {
    z: {
      show:true,
      usecolormap: true,
      highlightcolor:"#42f462",
      project:{z: true}
    }
  }
 
  }];  
  Plotly.newPlot('myDiv', data, layout);
  }
  
  var updateGraph = function() { updateGraphFunc(); }
  
  var updatemenus=[


    {
        buttons: [
            {
                args: ['colorscale', 'Viridis'],
                label: 'Viridis',
                method: 'restyle'
            },
            {
                args: ['colorscale', 'Electric'],
                label:'Electric',
                method:'restyle'
            },
            {
                args: ['colorscale', 'Earth'],
                label:'Earth',
                method:'restyle'
            },
            {
                args: ['colorscale', 'Hot'],
                label:'Hot',
                method:'restyle'
            },
            {
                args: ['colorscale', 'Portland'],
                label:'Portland',
                method:'restyle'
            },
            {
                args: ['colorscale', 'Blackbody'],
                label:'Blackbody',
                method:'restyle'
            },
        ],
        direction: 'left',
        pad: {'r': 10, 't': 10},
        showactive: true,
        type: 'buttons',
        x: 0.15,
        xanchor: 'left',
        y: button_layer_1_height,
        yanchor: 'top'
    },
]
var annotations = [

    {
      text: 'Colorscale:',
      x: 0,
      y: button_layer_1_height - annotation_offset,
      yref: 'paper',
      align: 'left',
      showarrow: false
    },
]

  var layout = {
    autosize: false, 
    width: 1300,
    height: 600,
    margin: {t: 130, b: 0, l: 0, r: 0},
    updatemenus: updatemenus,
    annotations: annotations,
    scene: {
        xaxis:{
            gridcolor: 'rgb(255, 255, 255)',
            zerolinecolor: 'rgb(255, 255, 255)',
            showbackground: true,
            backgroundcolor:'rgb(230, 230,230)'
        },
        yaxis: {
            gridcolor: 'rgb(255, 255, 255)',
            zerolinecolor: 'rgb(255, 255, 255)',
            showbackground: true,
            backgroundcolor: 'rgb(230, 230, 230)'
        },
        zaxis: {
            gridcolor: 'rgb(255, 255, 255)',
            zerolinecolor: 'rgb(255, 255, 255)',
            showbackground: true,
            backgroundcolor: 'rgb(230, 230,230)'
        },
        aspectratio: {x: 1, y: 1, z: 0.7},
        aspectmode: 'manual'
  }
  };
  

         gui = new dat.GUI();
          params ={G:0.1};
  
         var folder0 = gui.addFolder('Parameters');
           var GGUI= folder0.add(params, 'G');
            folder0.open();
       	
	
          //folder0.G++;
          //GGUI.updateDisplay()
	        GGUI.onChange( updateGraphFunc );

	
	updateGraphFunc();
  



</script>
</body>

Tamara-33 avatar Apr 27 '21 15:04 Tamara-33

I think I have the same issue.

Whenever I add values to the data[0].x and data[0].y arrays, the chart does not update.

When I add a value to the x and y arrays, I log them in the console. When such a message is added to the console, the x and y arrays as shown in the vue devtool both grow by a single entry. However, line in the chart never updates and stays the same.

2022-02-19_14-14 2022-02-19_14-15

The relevant parts of the code:

<template>
  <div>
    <plotly :data="data" :layout="layout" :displaylogo="false"></plotly>
  </div>
</template>

<script lang="ts">
...
@Component({
  components: { Plotly },
})
export default class GraphView extends Vue {
  data = [];
  layout = {};
...
  @Watch('topicValue')
  valueChanged(value: TopicValue) {
    console.log("add : %s = %s", value.timestamp.format(), value.value);
    this.data[0].x.push(value.timestamp);
    this.data[0].y.push(value.value);
  }
...
  mounted() {
    axios
      .get(.....)
      .then((response) => {
        let data = {
          x: [],
          y: [],
          type: "scatter",
          line: {
            width: 1,
          },
        };
        response.data.values.forEach((record) => {
          data.x.push(record.timestamp);
          data.y.push(record.value);
        });

        this.data.push(data);
      });

x12a1f avatar Feb 19 '22 13:02 x12a1f

I have the same exact issue. When data added to the X and Y arrays, the plot is not updated.

masimo12358 avatar Mar 18 '23 00:03 masimo12358