Sunday, May 27, 2018

Published 8:59 PM by with 0 comment

How To Use Plotly In Vue

In looking into Vue, I was a bit surprised by the lack of clear examples using plotly so I went ahead and wrote one...



Basic Setup

This example is intended to show how to call plotly from a Vue instance, and how to maintain multiple, independent plots with plotly events supported in each.

There are two plots and one slider in the Vue instance. One plot is a scatter plot with lines. The other is a scatter plot with markers. Each plot has hover events re-routed to an alert in the Vue instance. If you move the slider, it will update each plot. The line plot gets the slider value, and the marker plot gets -1*slider value.

You can try the code here.


Plotly Component

I wrote a very minimal plotly component for this. It is called 'plotly-graph'. It contains props for data (data in the plot), layout (layout of the plot), and divId (id of the div created) along with some props for styling. The data has a watch on it that updates the plot when the data prop changes:

this.$watch('data', this.Plot, { deep: true });

Plotly has some built-in events. All can be managed roughly the same way, so I just handled 'plotly_hover' here as an example. The basic way it is handled is:
  • register plotly's 'plotly_hover' event in 'mounted()' and call the hover method when the event is triggered
  • create a hover method that emits the event name ('hover') and some useful data (points hovered over and id of plot that contains them)
Now, an instance that contains this component can get this event by implementing a method for it (later in this example). The code for registering the event is:

$('#' + this.divId).on('plotly_hover', this.hover);

The hover method called when the event is triggered is:

hover: function (event, eventData) {
    this.$emit('hover', eventData.points, this.divId);
}

And that's it for plotly events. Note that the jQuery selector will not work if you move this compnent to a .vue file and import it. However, using document.getElementById(this.divId) will.

Two more things worth noting...to make it responsive, I am catching the window resize event using basically the same technique as the hover event so that the relative height and width stay the same on window resize:

$(window).bind('resize', this.onResize);


onResize() {
 var d3 = Plotly.d3;
 gd3 = d3.select('#' + this.divId);
 gd3.style({
  width: '100%',
  height: this.height + '%'
 });
 Plotly.Plots.resize(gd3[0][0]);
}

To allow basic formatting of the plots in my calling app, I have style binding in my template: ':style='{ height: height +  \"%\", gridcolumn: columnNumber }'...this lets the caller set the height in % and column number for the grid.


Vue

The Vue has data for each plot and the slider's value. It also has two methods...one is called when a hover event is triggered, and one is called when the slider's value changes. The slider change isn't unique to Vue, so I'll focus on the hover.

In the 'plotly-graph' component, the hover method I covered previously emits an event on hover that contains the points hovered over and the plot's id. It has three arguments...the first is just the event. The method called in the Vue instance that handles that event then just has the other two arguments in it:

hover(points, id) {
    alert(id + "...x: " + points[0].x + ", y: " + points[0].y);
}

That's the other side of propagating the plotly events.


HTML

The HTML is very simple, but there are a couple of interesting things to note:

<div id="plotlyExample" style="grid-template-columns: 50% 50%; display: grid; grid-template-rows: 400px 100px">
 <plotly-graph @hover="hover" v-bind:data="linePlotData" div-id="plot1" v-bind:height="90" :column-number="1"></plotly-graph>
 <plotly-graph @hover="hover" v-bind:data="scatterPlotData" div-id="plot2" v-bind:height="90" :column-number="2"></plotly-graph>
 <input type="range" min="-10" max="10" v-model="currentVal" @change="sliderUpdate" style="grid-column-start: 1; grid-column-end: 3; width: 50%; margin-left: auto; margin-right: auto" />
</div>

First...the component's 'data' prop is set to one of the data members in the Vue instance for each plot. This allows those to be linked. There are other ways to do this and I'm very new to Vue so this probably isn't even the best way, but it's functional.

Second, you can see how simply the hover event is handled. The event handling was the least transparent part of all of this when I was trying to figure it out, so it's nice that it ended up so clean, and I hope this lays out the three pieces clearly enough.


Summary

Again, this was pretty easy. Vue seems quite nice so far.



      edit

0 comments:

Post a Comment