I'm working on a medium sized application that is going to include a lot of D3 charts / interactions in it. I'm wondering if anyone has tried to use Backbone, Angular, or Ember with D3, and if so, which one seems like the best fit for a front end MV* framework. The application won't be doing a whole lot of CRUD operations, mainly interactive charts and widgets to manipulate them.
Any comments appreciated!
We used d3 with Backbone pretty extensively on a project that consisted of multiple "scenes". Each scene contained a set of different charts, and a user has the ability navigate from one scene to another. These scenes and their content all needed to be highly configurable (e.g. label colors and formatting, or indicating which data params should be plotted on a given axis).
D3 (rightfully) doesn't provide a view management system, which is where Backbone took over. Backbone Views served as wrappers for d3 charts. Predictably, Backbone Models served as the carriers of the d3-plotted data. But more interestingly, we also found that they served well as a means of controlling the appearance and behavior of the d3 code contained within the Backbone Views; essentially they served as view models. Since d3 promotes passing functions as arguments into other functions, these Backbone Models-as-view-models ended up holding many functions in them.
The following is a simplistic example, but picture doing this with dozens of properties. Using coffeescript here, because it's shorter (and better).
First, there's the model, which we instantiate inside (for example) a router's event handler. We populate this model with functions that will be applied to d3 selectors.
barChartModel = new Backbone.Model
barColor: (d, i) -> if d.profits < 0 then "red" else "green"
barLengthVal: (d, i) -> return bar.profits #// profits will be the prop we graph
onClick: (d, i) ->
console.log "We are", if d.profits <= 0 then "losing" else "making", "money"
data: someJsonWeLoaded
We pass this model into a new view:
barChartView = new BarChartView
el: "#the_bar_chart"
model: barChartModel
A view might be implemented like this:
class BarChartView extends Backbone.View
render: ->
bars = d3.select(@el)
.selectAll('.bar')
.data(@model.get 'data') # <---- THIS
bars.enter()
.attr('class', 'bar')
.attr('fill', @model.get 'barColor') # <---- THIS
.attr('height', (d, i) ->
@barLengthScale @model.get('barLengthVal')(d, i) # <---- AND THIS
)
.on('click', @model.get 'onClick') # <---- INTERACTIVITY TOO
initialize: ->
@barLengthScale = d3.scale.linear()
.domain([-100, 100]) # <---- THIS COULD ALSO COME FROM MODEL
.range([0, @$el.height()])
@render()
I have used D3 with Angular on a few dashboards, and it worked very well. I have never really used Backbone, and not with D3, so I cannot compare the two. I chose Angular to complement D3 because it appeared to me that lately the D3 community has been using D3 with Angular the most of the three options you mentioned, so there were great resources available. There has recently been an entire book dedicated to using D3 and Angular together. I had also used Angular a bit before, and was aware of directives. Directives (in Angular it is a way to extend html tags) are great for meshing with D3. Each chart can become a directive, and then makes it extremely easy to reuse charts, changing only the $scope data. These are some resources I found helpful when combining the two:
https://www.youtube.com/watch?v=aqHBLS_6gF8
https://leanpub.com/d3angularjs
http://plnkr.co/edit/WnoCtNPV9azj0oPwv9kM?p=preview
http://vicapow.github.io/angular-d3-talk/slides/demos/a-donut-chart-editor/index.html#/
I recently gave a talk on this very topic, here are some links: Video · Code · Slides
I've done some smaller projects using similar methods to meetamit, but have recently started exploring Ember + D3. I haven't done much yet, but I think Ember has a lot to offer that could simplify building these types of app. Some things that come to mind:
Computed properties: you'll often be displaying aggregates, so slicing your data using computed properties means you just have to call your chart's update function whenever the data changes, and you're good to go. No more worrying about sending off an event to every view that will change when one specific part of your data changes. Plus, these will probably be properties on your controllers, instead of being calculated within a specific chart or view, which will make reuse much easier.
Storing state: I had a tough time figuring out the best way to store state in Backbone. I started out trying to coordinate everything via events, but in the end landed on having a separate State model which acted as the brains of the whole system.
I didn't get around to using Backbone's router much, but Ember's router + focus on state has made this design challenge easier for me so far. If you build within the system, you can click around your filters and controls, and everything just works. It may be possible to do exactly the same thing in Backbone, but there's something to be said for seriously reducing your cognitive load. You can also explicitly use a StateManager object - there may be some really interesting solutions here, though I haven't explored them yet.
Again, my experience with this combo is shallow, but if my instinct is right there are going to be many gains from building visualizations within Ember's conventions.
If you haven't already come across this, Square put up an article briefly covering their experience building an interactive dashboard with Ember + D3.
Keep us up to date on your progress + any insights you come across, and good luck!
My group has used both angular and backbone with d3, and we like both for different reasons.
Backbone is a bit less opinionated about the way you construct your application, which is nice if you need to customize the way data is getting handled for performance. You generally integrate d3 with a backbone view.
One challenge of working with Backbone is memory management for complex views, but using marionette helps with that. Also Marionette's event aggregator (and specifically using request-response) is a good fit for centralized data sources for coordinated views if you want to use something like crossfilter or lunr.
Angular is more structured, and it is allows you to build up cool features very quickly. It has a steep learning curve, but I've found now that I'm understanding angular (having using it to develop an application for the past ~4 weeks), I've found that I can accomplish many of the same things I can in backbone without resorting to anything too hackish.
Like the request-response object in backbone marionette, using angular services allows you to build up complex views quickly. You'll need to avoid using angular's dirty checking on $scope data for complex data visualizations to keep your application from bogging down, so the code you write for working with your data in angular is going to end up looking a lot like the code you would write in backbone.
I had resisted angular's "magic" for a while, but I'm starting to get won over by the speed of development you can achieve because of all the built in directives, scope checking, and other goodies. Angular still allows you to poke around in its internals to speed up your code when that's required. This "digging" may take more time than in backbone (because the code base is more complex), but I've found that time lost in this phase is usually recouped by time saved avoiding common bugs like memory leaks in view code and writing boilerplate code like view rendering and data binding.
I use D3 on Angular since one year and I love the combination of both. Angular uses directives to create new and reusable HTML elements. When you have this knowlegde, you can encapsule a D3 visualization in an Angular directive without having D3 in your controllers or somewhere else. After that you can reuse the directive everywhere in your application to visualize your data. When working in a team of multiple Angular developers, the rest of your team doesn't need to know anything about D3, because your D3 code exists only in the directive.
Directives in Angular give you a flexible way of dealing with data. You can choose whether your data persists in your directive (then you will have a static reusable visualizaiton) or you make a data binding to a controller in your Angular application (then you will have a dynamic reusable visualization). The latter can be achieved by setting a scope in your directive
scope: { data: '=' /* bi-directional binding */ }
,
setting the data in your controller
$scope.data = [23,44,22];
and join both in your HTML element instance
<div your-new-d3-element data="data"></div>
.
Thats it!
Furthermore you can use watcher in your directive to watch changing data in your controller. A nice example of reusable d3 directives in Angular gives this example: http://bl.ocks.org/biovisualize/5372077
Moreover you can find an easy D3 on Angular setup in this article: http://goo.gl/hNS8k1 On this site you find further introductions how to use d3-plugins like the fisheye plugin in your Angular application or how to implement more complex d3 visualizations in Angular.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With