GSP
GSP

Reputation: 3789

Conditionally build group in d3

I am trying to build a graph using the force layout in D3. I would like to build different looking nodes depending on the data. Currently all nodes have a category and a name. So I draw an svg:g consisting of two rect and two text elements.

My code currently looks something like this:

// nodes are in the form: { group: 'string', name: 'string2' }
this.node = this.node.data(this.node, function(d) { return d.id; });

var g = this.node.enter().
    append('svg:g').
    attr('transform', function(d) { return 'translate('+ d.x +','+ d.y +')'; });
g.append('svg:rect').attr('h', 20).attr('w', 100);
g.append('svg:rect').attr('y', 20).attr('h', 20).attr('w', 100);
g.append('svg:text').text(function(d) { d.group; });
g.append('svg:text').attr('y', 20).text(function(d) { d.name; });

If the node doesn't have a name, however, I'd like to supress the creation of the second rect and text. Logically, if it wasn't for the implicit iterator in d3 I'd be doing something like:

var g = this.node.enter().
    append('svg:g').
    attr('transform', function(d) { return 'translate('+ d.x +','+ d.y +')'; });
g.append('svg:rect').attr('h', 20).attr('w', 100);
g.append('svg:text').text(function(d) { d.group; });


// unfortunately 'd' isn't defined out here.
// EDIT: based on comment from the answer below; the conditional should
// be for the text and the related rectangle.
if(d.name) {
  g.append('svg:rect').attr('y', 20).attr('h', 20).attr('w', 100);
  g.append('svg:text').attr('y', 20).text(function(d) { d.name; });
}

Upvotes: 2

Views: 1084

Answers (1)

AmeliaBR
AmeliaBR

Reputation: 27544

You could use an each call on your g selection to decide whether or not to add the label.

g.each(function(d) {
    if (d.name){
       var thisGroup = d3.select(this);

       thisGroup.append("text")
                 .text(d.group);
       thisGroup.append("text")
                .attr("y", 20)
                .text(d.name);
});

However, be aware that this structure could get confusing if you're going to be updating the data.

If you want to be able to update neatly, I would recommend doing a nested selection:

var labels = g.selectAll("text")
     .data(function(d){ d.name? [d.group, d.name]:[]; });

labels.enter().append("text");
labels.exit().remove();

labels.text(function(d){return d;})
          .attr("y", function(d,i){return i*20;});

The data-join function tests the parent's data object, and based on it either passes an array containing the two values you want to use for the label text, or an empty array. If it passes the empty array, then no labels are created; otherwise, each label has it's text set by the value in the array and it's vertical position set by the index.

Upvotes: 6

Related Questions