I have data objects that I want to add to an SVG. Consider the following pseudo-snippet:
var data = [], counter = 0;
for (var col=1; col<=5; col++)
for (var row=1; row<=3; row++)
data.push({
id: "obj-" + ++counter
,x: col * 120
,y: row * 120
,width: 40
,height: 40
,shape: counter % 2 ? "circle" : "rect"
});
d3.select(".container").selectAll(".obj")
.data(data)
.enter()
.append("g")
.attr("id", function(d){ return d.id; }
/***
now I want to draw here a circle or rect based on the shape key
so if (d.shape == "rect") -- we will use width and height
if (d.shape == "rect" && d.width == d.height) we will set "r" to "width", etc.
***/
Ideally, I would create an object of type Shape, e.g.
function Shape(id, shape, x, y, w, h) {
this.id = id;
this.shape = shape;
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.render = function(parent) {
var g = parent.append("g")
.attr("id", this.id);
switch (this.shape) {
case "circle":
g.append("circle")
.attr( /* more code here */ )
break;
case "rect":
g.append("rect")
.attr( /* more code here */ )
break;
case "triangle":
g.append("polygon")
.attr( /* more code here */ )
break;
}
}
}
Then I'd be able to do something like:
var data = [], counter = 0;
for (var col=1; col<=5; col++)
for (var row=1; row<=3; row++)
data.push(new Shape({
id: "obj-" + ++counter
,x: col * 120
,y: row * 120
,width: 40
,height: 40
,shape: counter % 2 ? "circle" : "rect"
)});
But how can I call the Shape's render() method from d3? i.e.
d3.select(".container").selectAll(".obj")
.data(data)
.enter()
/* given a datum named d, call d.render(parent) ? */
I'm rather new to d3, so maybe data joins is the wrong way to go? Is there a different way to render data items that would be better for this scenario?
To have the data objects render themselves in an object-oriented manner you can resort to a less known use of
selection.append(name)
. According to the docs, you can provide a callback to.append()
which needs to return a DOM element to append:For the purpose of this question this may be modified to not create the element in place, but to delegate the creation to the
.render()
method of your data objects.Your
.render()
method might look something like this:Have a look at this snippet for a working example:
I've been interested in this topic as well. Here's what I'm currently doing. In your case, instead of setting text to d.text, you would choose the shape you want. Or maybe someone might enlighten us both on a better way.