Simplest way of integrating Angular-created elements with D3 - javascript

I'm playing around with different ways of integrating D3 and Angular, in the context of learning both of these frameworks, and am hoping for some input:
My client application receives a JSON array of nodes and an array of edges from my server.
The central component of the client is a graph visualization implemented as a D3 force-directed layout: For each node in the nodes array, a (SVG) circle element is added to this visualization, and lines between these circles are added for each edge in the edges array.
Of course, D3's selection.data( ) makes it trivial to add these elements and bind each to the data it represents, but the graph visualization is only part of a much larger application: I need to create different types of elements representing these same nodes and edges in different parts of the application (which D3 has nothing to do with), and I'd like to keep all of these elements bound to a single dataset.
My primary goal is minimizing code complexity, and - although I've fallen in love with the simplicity and elegance of D3's data-binding functionality - I've arrived at the tentative conclusion that using it in one part of the application while using Angular to do the same in other parts is creating unnecessary complexity, and that the simplest approach would be to use Angular to handle the data-binding/element-creation
In other words, instead of using selection.data( ).enter( ).append( ) to create SVG elements, I'm thinking I should do so using a ng-repeat="node in nodes", perhaps creating each as a custom directive to allow for custom functionality. Having done so, I would then need to get these elements "into" D3, i.e. managed by its force-directed layout.
Is my reasoning here sound? In particular, I'm worried that I'm overlooking complications this will create with regard to object constancy, which is an important requirement as nodes will be entering, exiting and moving about the screen constantly and I need these transitions to be smooth. How would you recommend I go about integrating my angular-created elements into D3 (more precisely, getting them into my force.nodes{ } and force.links( ) arrays) to avoid any such complications?
Finally, I'm also considering a strategy that I'm hoping might give me the best of both worlds: I could use D3 to create my SVG elements and bind them to their respective datum, but rather than executing that code in the link function of visualization directive (as I've been doing, and as all the Angular/D3 tutorials I've found do), I could run it in the compile function, and do something like this:
d3.select('SVG')
.selectAll('.node')
.data('nodeDataArray')
.enter( )
.append('circle')
.attr("class", "node-icon"); //...other attributes/styles etc
where node-icon is the normalized name of a directive with a restrict property that includes C. If this runs in the compile method, angular should then compile all of these directives as normal, adding any additional functionality / interfaces with other directives (etc.), the same way it does with any other type of element. Right?
This is the option I'm most attracted to intuitively - are there any pitfalls of it I might be overlooking, which might make the former strategy preferable?

I have been pondering pretty much the same problem for a while and come to the following conclusion.
The simplest way to integrate Angular created elements with d3 is to add the directives with .attr and then .call the compile service on the d3 generated elements. Like this:
mySvg.selectAll("circle")
.data(scope.nodes)
.enter()
.append("circle")
.attr("tooltip-append-to-body", true)
.attr("tooltip", function(d){
return d.name;
})
.call(function(){
$compile(this[0].parentNode)(scope);
});
Here is a Plunker.
I think the idea of generating elements with Angular ngRepeat rather than d3 is working against the frameworks. D3 does not expect to be handed a bunch of elements. It wants to be handed data - almost always an array. It then has a stack of excellent functions to convert that data into various SVG or HTML elements. Let it do that.
It seems from this quote...
D3 makes it trivial to add elements and bind each to the data it represents, but the graph visualization is only part of a much larger application: I need to create different types of elements representing these same nodes and edges in different parts of the application (which D3 has nothing to do with), and I'd like to keep all of these elements bound to a single dataset.
... you are implying that generating elements with d3 somehow prevents you from binding the same data to different parts of the application. I can't see why. Just have d3 generate elements from a scope array (as is done in the linked Plunker). Then use the same dataset wherever you want in the usual Angular way. Other parts of the application can update the dataset and a $watch callback can re-render the d3 graphic.

Related

D3.js updating data in packed hierarchy changes wrong elements

I have some hierarchical data which is being packed to form a bubble chart, this works fine however when I update the data and apply a new pack the wrong elements are updated so they transition to the wrong location within the circle. It seems to be updating the data based on its index but I can’t find any way to tell it which data applies to which existing element. Is there a mechanism to link new data with existing elements, for example an ID or anything?
This is noticeable if my hierarchy is one level and then later split into groups. Some of the existing circles become the parent group circles rather than creating new ones for it. I do have enter/exit functions but it just does a dumb update as there seems to be no way of linking the existing elements to the new data.
I’m sure there must be a way to bind the two together but for the life of me I can’t find it. Thanks!
Mehdi answered this in the comments but adding here for anybody else with the same issue.
I was missing a key in the data binding reference.
Instead of applying the data using .data(data) I needed to add an additional key so that it could be bound properly like .data(data, function(d) { return d.data.id; })

Code reuse (DRY) in procedural drawing?

I am building a workflow tool that renders with d3js. I initially created the tool in an "editor" mode, where users can drag and manipulate nodes and dependencies within a workflow. Now I am having a hard time rationalizing the proper way to make my current draw procedure support "view" and "edit" modes and stay DRY.
My initial code is basically one long draw procedure:
Define JS prototypes
Create initial d3js grid (zoom/pan grid)
Function(s): Define menu system
Function: Draw dependencies (svg groups in d3js)
Function: Draw nodes (svg groups in d3js)
Get data, iterate & instantiate, iterate and draw dependencies, iterate and draw nodes
Adding switch logic within each of the functions where mode-behavior or SVG structure differs seems like the road to spaghetti-code.
It feels equally wrong to have 3 independent procedures for each mode - but I'm starting to think that having a distinct "draw" procedure for each mode is more flexible way to go about having 3 modes.
Maybe I'm thinkin' about this all wrong. Maybe I should render everything in read-only mode first and then apply any "edit" or "monitor" mode behaviors or visual additions afterwards.
What's the best way to support multiple similar, but different draw procedures in a DRY fashion?

Is it possible to nest one (or more) layout(s) within another, different layout(s) in d3?

I'm trying to build a force-directed layout, wherein the connected nodes have their own internal layout that is not simply a recursive force-directed layout (which, I believe, would be possible with the hierarchy layout). The "inner" layout would be custom, but, for illustration let's say I wanted the nodes, internally, to have a partition layout. Is this possible?
My question was really twofold:
can you pull off having more than one style of layout (for instance, a bubble graph inside a force-directed graph) in a sensible way with D3, or is D3 the wrong tool for such a thing, and
Can you use D3 layouts for each of these layouts, or do you have to do everything custom.
In the end, the design changed, and no longer called for this odd scenario. Being much more familiar with D3, though, I think I can answer.
Yes. It can be done. Each layout is its own discrete object, with its own data on which to work, and can be given its own DOM elements to populate. Creating two layouts that even shared the same data and DOM outputs would probably work, if you could manage the interaction between the two (making sure one only overrode changes from the other when desired).
What I know you can do for sure is manually manipulate anything that D3 is doing. At one point during development, actually, I did have two layouts on the same page, come to think of it. I was laying out half the graph with pre-determined x/y coordinates, and allowing the rest to be laid out by the force directed layout. That first set, the manually placed nodes, could have been placed by some other logic than pre-determined coordinates, and the functionality would have been roughly the same.

What is the difference between D3 and jQuery?

Referring to this example:
http://vallandingham.me/stepper_steps.html
it seems that the D3 and jQuery libraries are very similar in the sense that they both do DOM manipulation in an object-chaining way.
I'm curious as to know what functions D3 makes easier than jQuery and vice versa. There are plenty of graphing and visualization libraries that use jQuery as a basis (e.g., highcharts, flot, wijmo).
Please give specific examples of how they are different.
D3 is data-driven but jQuery is not: with jQuery you directly manipulate elements, but with D3 you provide data and callbacks through D3's unique data(), enter() and exit() methods and D3 manipulates elements.
D3 is usually used for data visualization but jQuery is used for creating web apps. D3 has many data visualization extensions and jQuery has many web app plugins.
Both are JavaScript DOM manipulation libraries, have CSS selectors and fluent API and are based on web standards which makes them look similar.
Following code is an example of D3 usage which is not possible with jQuery (try it in jsfiddle):
// create selection
var selection = d3.select('body').selectAll('div');
// create binding between selection and data
var binding = selection.data([50, 100, 150]);
// update existing nodes
binding
.style('width', function(d) { return d + 'px'; });
// create nodes for new data
binding.enter()
.append('div')
.style('width', function(d) { return d + 'px'; });
// remove nodes for discarded data
binding.exit()
.remove();
d3 has a silly description. jQuery and d3 are not at all similar, you just don't use them for the same things.
jQuery's purpose is to do general dom manipulation. It's a general purpose javascript toolkit for anything you might want to do.
d3 was primarily designed to make it easy to make shiny graphs with data. You should definitely use it (or something similar, or something built on top of it) if you want to make graphical visualizations of data.
If you want a general purpose JS library for all your interactive form needs, consider jQuery or proto or mootools. If you want something tiny, consider underscore.js. If you want something with dependency injection and testability, consider AngularJS.
A General comparison guide from wikipedia.
I can see why someone would think they are similar. They use a similar selector syntax -- $('SELECTOR'), and d3 is an extremely powerful tool for selecting, filtering, and operating on html elements, especially while chaining these operations together. d3 tries to explain this to you on its home page by claiming to be a general purpose library, but the fact is that most people use it when they want to make graphs. It is pretty unusual to use it for your average dom manipulation because the d3 learning curve is so steep. It is, however, a far more general tool than jQuery, and generally people build other more specific libraries (such as nvd3) on top of d3 rather than using it directly.
#JohnS's answer is also very good. Fluent API = method chaining. I also agree about where the plugins and extension lead you with the libraries.
I've been using a little of both lately. Since d3 uses Sizzle's selectors you can pretty much mix up selectors.
Just keep in mind d3.select('#mydiv') doesn't return quite the same as jQuery('#mydiv'). It's the same DOM element, but it's being instantiated with different constructors. For example, let's say you have the following element:
<div id="mydiv" rel="awesome div" data-hash="654687867asaj"/>
And let's grab some common methods:
> d3.select('#mydiv').attr('rel') ;
"awesome div"
> jQuery('#mydiv').attr('rel');
"awesome div"
Seems legit. But if you go a little further:
> d3.select('#mydiv').data();
[undefined]
> jQuery('#mydiv').data();
Object {hash: "654687867asaj"}
D3 is not just about visual graphs. Data Driven Documents. When you use d3, you bind data to dom nodes. Because of SVG we are able to make graphs, but D3 is so much more. You can replace frameworks like Backbone, Angular, and Ember with using D3.
Not sure who down voted, but let me add some more clarity.
Many websites request data from the server, which usually comes from a database. When the website receives this data, we have to do a page update of the new content. Many frameworks do this, and d3 does this as well. So instead of using a svg element, you can use html element instead. When you call the redraw, it'll quickly update the page with the new content. It's real nice to not have all the extra overhead like jquery, backbone + its plugins, angular, etc. You only need to know d3. Now d3 doesn't have a routing system baked into it. But you can always find one.
Jquery on the other hand, it's sole purpose is to write less code. It's just a short hand version of javascript that has been tested on many browsers. If you don't have a lot of jquery on your webpage. It's a great library to use. It's simple and takes a lotta pains out of javascript development for multiple browsers.
If you tried to implement jquery in a d3 like fashion, it'll be quite slow, as it wasn't designed for that task, likewise, d3 isn't design to post data to servers, it's designed just to consume and render data.
d3 is made for data visualization, it does this by filtering through DOM objects and applying transformations.
jQuery is made for DOM manipulation and making life easier for many basic JS tasks.
If you're looking to turn data into pretty, interactive pictures, D3 is awesome.
If you're looking to move, manipulate or otherwise modify your webpage, jQuery is your tool.
Great question!
While both libraries share many of the same features, it seems to me that the greatest difference between jQuery and D3 is the focus.
jQuery is a general purpose library with a focus on being cross-browser and being easy to use.
D3 is focused on data (manipulation and visualisation) and supports only modern browsers. And while it does look like jQuery, it's a lot more difficult to use.
Both can solve the same purpose of creating and manipulating a DOM (whether it be HTML or SVG). D3 surfaces an API that simplifies common tasks you would take when generating/manipulating a DOM based on data. It does this through it's native support for data binding via the data() function. In jQuery you would have to manually process the data and define how to bind to the data in order to generate a DOM. Because of this, your code becomes more procedural and harder to reason and follow. With D3, it's less steps/code and more declarative. D3 also provides some higher level functions that aid in generating data visualization in SVG. Functions like range(),domain(), and scale() make it easier to take data and plot them on a graph. Functions like axis() also make it easier to draw common UI elements you would expect in a chart/graph. There are many other higher-level api libraries that sit on top of D3 to aid in more complex visualizations of data including interactive behavior and animation.

Javascript directed acyclic graph library? (Graph visualization is NOT necessary)

I have a dataset which is best represented by a graph. It consists of nodes of 6 or 7 different "types" with directed edges (dependencies on one another, guaranteed not to have cyclic dependencies). The dataset is essentially a template of a layered configuration, and the user needs to be able to select bits and pieces of the configuration from different layers which are desired, and have the dependent bits be brought in automatically.
The general UI need is for a user to select or un-select items from multi-select boxes (one such box for each node type), and have "depended-on" items in the other boxes become selected or unselected as needed. I need to be able to pull down the dataset from the server, let the user select the desired bits (with the dependency processing being done in javascript on the client side for responsiveness), and then submit the result back when they are finished.
The dataset is large and complex enough that actually showing it as a graph would be overwhelming and confusing to the user. Only basic graph traversal operations are needed, since all that is required is to cascade selections out the dependencies. (For example, a user un-selecting a node would result in that nodes dependencies becoming unselected if there were no other selected node which still depended on them. A user selecting a node would result in all of that node's dependencies becoming selected.) A simple depth or breadth first search following directed edges from the start node will suffice to visit all affected nodes. If I can follow edges either direction, bonus. (If not I can easily generate an edge-reversed graph and use that when needed.)
I have dug around on here and found references to a number of javascript graph visualization libraries, but most of these discussions seem to interpret "graph" as "chart" and I have no charting needs here. My digging has led me to this list: Raphael, protovis, flare, D3, jsVis, Dracula, and prefuse. From this list it looks like jsVis or Dracula might have the underlying graph constructs I need if I just ignore the visualization side, but it isn't clear to me from the documentation if that is the case. I have to rule out a few others because I cannot bring in any flash dependencies. Unfortunately I don't have time to prototype things with this many libraries. (I will be digging into jsVis and dracula more though, barring some handy input here.)
If anyone has experience with something from that list and believes that the graph portion of it can be used independently of the visualization portion, that will certainly meet my needs. If there is some other library I could use that meets my needs, that would be great too. One final requirement regarding licensing: the library needs to be "free" in a non-copyleft way - So ideally Apache v2.0, BSD, MIT, or something like that.
I haven't used it, but you might want to check out data.js. It's an MIT-licensed library with a range of data-structure utilities. In particular, it includes Data.Node and Data.Graph:
A Data.Graph can be used for representing arbitrary complex object graphs. Relations between objects are expressed through links that point to referred objects. Data.Graphs can be traversed in various ways.

Categories