如果数据中存在某个属性,则有条件地呈现文本元素

时间:2016-04-04 12:19:54

标签: d3.js svg

我在我的应用程序中使用d3作为条形图,并且需要使用一段文本为条形图所代表的数据值注释每个条形。

到目前为止我的工作原理如下:

layers = svg.selectAll('g.layer')
    .data(stacked, function(d) {
        return d.dataPointLegend;
    })
    .enter()
    .append('g')
    .attr('class', function(d) {
        return d.dataPointLegend;
    });

layers.selectAll('rect')
    .data(function(d) {
        return d.dataPointValues;
    })
    .enter()
    .append('rect')
    .attr('x', function(d) {
        return x(d.pointKey);
    })
    .attr('width', x.rangeBand())
    .attr('y', function(d) {
        return y(d.y0 + d.pointValue);
    })
    .attr('height', function(d) {
        return height - margin.bottom - margin.top - y(d.pointValue)
    });

layers.selectAll('text')
    .data(function(d) {
        return d.dataPointValues;
    })
    .enter()
    .append('text')
    .text(function() {
        return 'bla';
    })
    .attr('x', function(d) {
        return x(d.pointKey) + x.rangeBand() / 2;
    })
    .attr('y', function(d) {
        return y(d.y0 + d.pointValue) - 5;
    })

如果数据中存在某个属性,我实际上只想附加一个文本元素。

我在文档中看到了datum选择方法,并想知道这是否是我需要的,如果我正在寻找的属性不存在,我不确定如何取消追加调用。

由于

尝试2

好的所以我又进行了另一次尝试,这次使用each函数,如下所示:

layers.selectAll('text')
    .data(function(d) {
        return d.dataPointValues;
    })
    .enter()
    //This line needs to be my each function I think?
    .append('text')
    .each(function(d){
        if(d.pointLabel) {
            d3.select(this)
                .text(function(d) {
                    return d.pointLabel;
                })
                .attr('x', function(d) {
                    return x(d.pointKey) + x.rangeBand() / 2;
                })
                .attr('y', function(d) {
                    return y(d.y0 + d.pointValue) - 5;
                })
                .attr('class', 'data-value')
        }
    });
}

我现在遇到的问题是,无论是否存在pointLabel属性,我都会添加一个文本元素。

感觉我已经接近了,我确实想知道我是否应该将append('text')向下移动到每个中,但是当我尝试时我得到了一个错误,因为d3并不期待那个特定的电话链。 / p>

1 个答案:

答案 0 :(得分:1)

如何使用d3的数据绑定...而不是将文本附加到layers.enter()选项,请执行以下操作整个 layers选择,即包括更新节点:

labels = layers.selectAll('text')
    .data(function(d) {
        // d is the datum of the parent, and for this example
        // let's assume that the presence of `pointLabel`
        // indicates whether the label should be displayed or not.
        // You could work in more refined logic for it if needed:
        return d.pointLabel ? [d] : [];
    })

// The result of the above is that if a label is needed, a binding will
// occur to a single element array containing `d`. Otherwise, it'll bind
// to an empty array. After that binding, using enter, update and exit,
// you get to add, update or even remove text (you might need removal if
// you're updating an existing view whose existing label needs to go away)

labels.enter()
  .append("text")

labels
  .text(function(d) { d.pointLabel })
  .attr('x', function(d) {
    return x(d.pointKey) + x.rangeBand() / 2;
  })
  .attr('y', ...)

labels.exit()
  .remove()

这里的诀窍(它不是一个技巧,但它不是一个非常常见的d3用例)是它要么绑定到单个元素数组[d]或者是空白[],这就是您使用enter()选项仅在需要时创建标签的方式。此方法优于非数据绑定应用程序的好处是,可以多次调用此代码 - 每当d.pointLabel更改或应用程序的状态更改时 - 以及标签' presense(或缺乏存在,即删除)将相应更新。