d3:可选择根据强制布局中的节点内容添加子元素 - 当过滤器不是解决方案时

时间:2014-08-27 18:04:40

标签: javascript svg d3.js force-layout

我正在努力为这个问题提供一个可能的解决方案:d3 force layout with node groups

我有一个像这样的JSON:

{
    nodes: [
        {
            label: "x"
            inputs: [],
            outputs: ["a"]
        },
        {
            label: "y"
            inputs: ["b"],
            outputs: []
        },
        ...
    ],
    links: [
        {
            source: 0, //x
            sourceIndex: 0, //x.outputs.a
            target: 1, //y
            targetIndex: 0 //y.inputs.b
        },
        ...
    ]
}

我想为每个节点构建一个SVG:

    <g class="component worker" transform="translate(0,0)">
        <use class="container" xlink:href="#container"/>
        <use class="icon" xlink:href="#worker"/>
        <text class="label" text-anchor="middle" alignment-baseline="middle" dy="40">Worker</text>
        <g class="input" transform="translate(-31,-16)">
            <use class="port" xlink:href="#port">
                <title>stdin</title>
            </use>
            <use y="8" class="port" xlink:href="#port"/>
            <use y="16" class="port" xlink:href="#port"/>
            <use y="24" class="port" xlink:href="#port"/>
            <use y="32" class="port" xlink:href="#port"/>
        </g>
        <g class="output" transform="translate(31,16)">
            <use class="port" xlink:href="#port">
                <title>stdout</title>
            </use>
            <use y="-8" class="port" xlink:href="#port"/>
            <use y="-16" class="port" xlink:href="#port"/>
            <use y="-24" class="port" xlink:href="#port"/>
            <use y="-32" class="port" xlink:href="#port"/>
        </g>
    </g>

我有以下js代码来构建SVG:

    var node = svg.selectAll(".node").data(this.data.nodes).enter().append("g").attr({
        "class": function (d) {
            return d.groups.join(" ");
        }
    }).call(force.drag);
    node.append("use").attr({
        "class": "container",
        "xlink:href": "#container"
    });
    node.append("use").attr({
        "class": "icon",
        "xlink:href": function (d) {
            return d.icon;
        }
    });
    node.append("text").attr({
        "class": "label",
        "text-anchor": "middle",
        "alignment-baseline": "middle",
        dy: 40
    }).text(function (d) {
        return d.label
    });

    node.append("g").attr({
        "class": "input",
        transform: "translate(-31,-16)"
    });

    node.append("g").attr({
        "class": "output",
        transform: "translate(31,16)"
    });

现在我缺少的是输入和输出。我不知道如何迭代节点的那些属性以及如何使用d3.js的流畅接口逐个追加它们。我检查了d3: Optionally add child elements based on node content in force layout但过滤在这里不起作用,因为我们讨论的是迭代集合,而不是检查属性的存在。有什么建议吗?

2 个答案:

答案 0 :(得分:0)

您可以使用嵌套选择。

我假设输入要为节点数据中的inputs数组的每个成员附加一个项目,同样也为输出附加。

例如,输入:

node.append("g").attr({
    "class": "input",
    transform: "translate(-31,-16)"
})
.selectAll('SOME_THING')
  .data(function(d) { return d.inputs; })
  .enter().append('SOME_THING')

其中'SOME_THING'是您要追加的元素(可能是<use>,尽管从您的示例输出中可以清楚地看到这些元素会被映射到)。

答案 1 :(得分:0)

我用each()解决了问题:

    node.each(function (d) {
        var node = d3.select(this);
        var inputs = node.append("g").attr({
            "class": "input",
            transform: "translate(-31,-16)"
        });
        d.inputs.forEach(function (name, index) {
            inputs.append("use").attr({
                y: -8 * index,
                "xlink:href": "#port"}).append("title").text(name);
        });
    });

但是我会使用jshanley的解决方案,我想那是d3方式......: - )

    var inputs = node.append("g").attr({
        "class": "input",
        transform: "translate(-31,-16)"
    });
    inputs.selectAll("use").data(function (d) {
        return d.inputs;
    }).enter().append("use").attr({
        y: function (d, index) {
            return -8 * index;
        },
        "xlink:href": "#port"
    }).append("title").text(String);