在d3可重复使用的图表

时间:2016-02-25 19:47:53

标签: d3.js

我正在按照本教程构建可重用的图表:https://bost.ocks.org/mike/chart/。完整的代码就在问题的最后。我有以下问题:

正如您所看到的,特定组件上的“click”事件会触发一个查询,该查询会更新检索新数据的整个图表。我指的是这一行:

selection.datum(relatedConcepts).call(chart);       // Update this vis

现在这个更新很有效,但是当然在函数“chart”中我也有

color.domain(data.map(function(d){ return d[0]}));

色标的域名也会更新,我不希望这样。 所以问题是:如何在第一次创建图表时设置色阶域?

d3.custom = d3.custom || {};
d3.custom.conceptsVis = function () {

    var color = d3.scale.category20();

    // To get events out of the module we use d3.dispatch, declaring a "conceptClicked" event
    var dispatch = d3.dispatch('conceptClicked');

    function chart(selection) {

        selection.each(function (data) {

            //TODO: This should be executed only the first time
            color.domain(data.map(function(d){ return d[0]}));

            // Data binding
            var concepts = selection.selectAll(".progress").data(data, function (d) {return d[0]});

            // Enter
            concepts.enter()
                .append("div")
                .classed("progress", true)
                .append("div")
                .classed("progress-bar", true)
                .classed("progress-bar-success", true)
                .style("background-color", function (d) {
                    return color(d[0])
                })
                .attr("role", "progressbar")
                .attr("aria-valuenow", "40")
                .attr("aria-valuemin", "0")
                .attr("aria-valuemax", "100")
                .append("span") // (http://stackoverflow.com/questions/12937470/twitter-bootstrap-center-text-on-progress-bar)
                .text(function (d) {
                    return d[0]
                })
                .on("click", function (d) {

                    // Update the concepts vis
                    d3.json("api/concepts" + "?concept=" + d[0], function (error, relatedConcepts) {
                        if (error) throw error;
                        selection.datum(relatedConcepts).call(chart);       // Update this vis
                        dispatch.conceptClicked(relatedConcepts, color);    // Push the event outside
                    });
                });

            // Enter + Update
            concepts.select(".progress-bar").transition().duration(500)
                .style("width", function (d) {
                    return (d[1] * 100) + "%"
                });

            // Exit
            concepts.exit().select(".progress-bar").transition().duration(500)
                .style("width", "0%");

        });
    }

    d3.rebind(chart, dispatch, "on");
    return chart;
};

ANSWER 我最终做了一下meetamit建议,我补充说:

// Getter/setter
chart.colorDomain = function(_) {
    if (!arguments.length) return color.domain();
    color.domain(_);
    return chart;
};

到我的conceptsVis函数,所以从外面我可以做到:

.... = d3.custom.conceptsVis().colorDomain(concepts);

当然我删除了这一行:

color.domain(data.map(function(d){ return d[0]}));

1 个答案:

答案 0 :(得分:1)

您可以检查域是否为空数组,并且仅在以下情况下填充它:

if(color.domain().length == 0) {
  color.domain(data.map(function(d){ return d[0]}));
}

话虽这么说,这种行为似乎从根本上说是错误的,或者至少容易出错。这意味着填充域是第一个渲染的副作用。但是,第一次渲染使它与后续调用不同,因此值得设置域是什么?如果稍后,随着您的应用程序的发展,您决定首先渲染不同的数据集,然后渲染当前第一个数据集,会发生什么?那么你最终可能会得到一个不同的域名。在图表代码之外显式计算域似乎更合理,然后通过setter将域传递到图表中。类似的东西:

chart.colorDomain(someArrayOfValuesThatYouPreComputeOrHardCode)