vue-plotly
vue-plotly copied to clipboard
Plots not updating on data changes
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"
}]
};
},
};
Try plotly in lower case:
<plotly
:data="plot_data"
:layout="layout"
/>
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
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:
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.
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
Ok ok, that's weird because I use the graphs in my app with a lot of reactivity
ahhhh ok that's good to know. Must just be me then.
Well thanks for your help @ped59430. I've actually switched to another solution for charting now anyways after a bit more experience with Vue
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!
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?
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...
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
Thanks!
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>
Yeah! And it redraws on data changes?
ahhhhh nope, I need to add that today actually 😅
What's your email? I'd like to DM you a question.
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 , did you try to remove this watch?
this.$watch('data', () => { this.react() }, { deep: !this.watchShallow })
@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
I mean the watch in the code sample you share here as you wouldn't need to watch data by yourself.
Also, having a look, you use this.$nextTick don't you?
yes.
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/
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"
@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?
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 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);
}
}
}
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>
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.
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);
});
I have the same exact issue. When data added to the X and Y arrays, the plot is not updated.