Dealing with client-side state in d3 graphics

54 views
Skip to first unread message

Brandon Hamilton

unread,
Mar 25, 2015, 2:59:37 PM3/25/15
to d3...@googlegroups.com

Hi folks,

I have used d3 for a while for building one-off visualizations, but I am just now starting to work it into a fairly complex project where I am building a fully interactive dashboard.

One feature of the dashboard is that clicking on one of the bars of a bar graph will cause other visualizations (like say, a pie chart) to be updated from the server. Similarly, clicking on a a slice of a pie chart will cause the bar chart to be refreshed with new data from the server.

To understand the effect I am looking for, look at nvd3.js (they have a good example on their website) and how their legends act as filters for the chart. I want a similar feature, except that it will fetch new data from the server for the other visualizations, as opposed to just filtering the existing data that was used to initialize the charts.

The question I have is how to manage the state of each chart (the state being which bars/pie slices are selected) while fetching new data from the server.

When I look at the source for nvd3.js' legends, I see that they modify the __data__ for the selected series and redraw everything. This seems like a good strategy, save for the fact that __data__ will disappear once new data comes back from the server.

Have any of you dealt with this?

Thanks!

Andy Thornton

unread,
Mar 25, 2015, 3:09:01 PM3/25/15
to d3...@googlegroups.com

It sounds to me like you may be approaching the need for a framework like Angular, Ember, Backbone, etc. You can let these framework do what they are good it, which is managing the business logic and state of the data, and D3 can do what it is good at, which is handling the visualization.

The framework can talk to the server for you, control the state of the data, handle non visualization user interaction, etc. Then, you can use the reusable pattern in D3 for your charts. This way, you can have a fully encapsulated data visualization component that can plug into whichever framework you choose. Essentially, you are creating an API for your chart. You pass in the new data, and the chart(s) can update accordingly. I haven’t dug into NVD3, but modifying data directly scares me a bit. That is essentially bypassing D3’s API. I would be a little worried that bad things could happen if you aren’t careful.

Andy


--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Brandon Hamilton

unread,
Mar 25, 2015, 3:34:30 PM3/25/15
to d3...@googlegroups.com
So first, I don't mean to say that they go around the d3 API.  It's something more in the spirit of this:

series.on("click", function (d) { d.disabled = !d.disabled});
chart(selection);  // the redraw

Second, I am a little unclear on how a framework would help with this problem, could you or someone else elucidate?

Basically it seems to me that the problem is that the data from the server by itself is not enough to render the chart correctly, you also need the current client-side state of the charts. This could be a code smell, or it could be that most interactive dashboards have this issue, and there are patterns to deal with it, or as you suggest, potentially it's a problem a framework could help solve, though I am less clear on how.  Could you explain?

Thanks for your reply,
BH

Andy Thornton

unread,
Mar 25, 2015, 3:57:41 PM3/25/15
to d3...@googlegroups.com
I think the main thing that a framework offers is the concept of a controller. Essentially, a place to manage the state of the current visualizations, get data from the server, hold active filters, handle user inputs, etc. However, I think the key to a controller being really useful is following the reusable chart API. If each chart on your dashboard is fully encapsulated, and can re-render when passed new data or a new filter, you can manage the state of the entire dashboard outside the chart. I think this typically makes life much easier.

You certainly don't need a framework to coordinate multiple charts, but they can help if the app gets really complex. 

I guess its a bit of a complex topic, not sure how well I am explaining myself :). 

Gordon Woodhull

unread,
Mar 25, 2015, 4:03:36 PM3/25/15
to d3...@googlegroups.com
Another way to deal with it, if you do want to save state but don't want to save it on the server, is to append query parameters to the URL.

A lot of users of dc.js (one of many reusable charts built on d3) do this.

This ticket has a bunch of links to examples:

Brandon Hamilton

unread,
Mar 25, 2015, 8:03:13 PM3/25/15
to d3...@googlegroups.com
Ahh. What you were saying is much clearer to me now, and I think more incrementally helpful to me (I can implement a controller without necessarily adopting a framework.) Thanks for your help!

Brandon Hamilton

unread,
Mar 25, 2015, 8:05:01 PM3/25/15
to d3...@googlegroups.com
This gets very clearly at one of the motivations for not wanting state to live in the charts themselves -- being able to create the entire vis in a particular state from a URL.  Thanks much for the lead.

Curran

unread,
Mar 31, 2015, 5:15:58 PM3/31/15
to d3...@googlegroups.com
Hi Brandon,

This sounds like a case where the Model View Controller idea fits well. You can organize the application by having all the state (including the list of selected elements) live in models. Various frameworks provide model functionality, such as Backbone Models or Model.js models. Each visualization can have its own model, and can be set up to re-render when certain parts of its model change. With this setup, you can then listen for changes on visualization model properties, and in response to changes in a certain model property (like "selectedBars", which could be an array of ids from the data), make a request to the server. When the result comes back, you can update the models of the other charts with the new data, which would cause them to re-render the new data. Perhaps the data fetching component can also be encapsulated as a model, where it has one property for input (query specification) and one property for output (results).

Also, I'm working on a project called Chiasm for managing visualization state using JSON. One of the things I'd like to be able to do with this is exactly what you're describing - represent selected data elements or brushed regions as part of a kind of "visualization session" for serialization and later use. Here's a simple configurable dashboard example, where each visualizations and the data loaders are encapsulated as configurable models. Here's a more complex example with linked views using Crossfilter.

Good luck solving your issue, hope this helps.

Best regards,
Curran
Reply all
Reply to author
Forward
0 new messages