otto
otto

Reputation: 2033

d3 update paths and circles in Graph

i want to update the graph but it does not work. I want to update the line and the circles. I tried to add

.exit().remove()

to update the circle and

  g.selectAll("path").attr("d", line);

to update the paths.

However it does not work.

Updating the outside group with exit().remove() works fine. (Checkbox in this example).

Updating only the path and circle does not work. (Update Button in this example)

I dont want to remove all lines in the graph and append it again, because i want to add transition when data changes.

Here is a JS Fiddle: LINK Here is my code:

var data = [
  [{
    point: {
      x: 10,
      y: 10
    }
  }, {
    point: {
      x: 100,
      y: 30
    }
  }],
  [{
      point: {
        x: 30,
        y: 100
      }
    }, {
      point: {
        x: 230,
        y: 30
      }
    },
    {
      point: {
        x: 50,
        y: 200
      }
    },
    {
      point: {
        x: 50,
        y: 300
      }
    },
  ]
];

var svg = d3.select("svg");

var line = d3.line()
  .x((d) => d.point.x)
  .y((d) => d.point.y);

function updateGraph() {
  console.log(data)
  var allGroup = svg.selectAll(".pathGroup").data(data);
  var g = allGroup.enter()
    .append("g")
    .attr("class", "pathGroup")
  allGroup.exit().remove()

  g.append("path")
    .attr("class", "line")
    .attr("stroke", "red")
    .attr("stroke-width", "1px")
    .attr("d", line);

  g.selectAll("path").attr("d", line);

  g.selectAll(null)
    .data(d => d)
    .enter()
    .append("circle")
    .attr("r", 4)
    .attr("fill", "teal")
    .attr("cx", d => d.point.x)
    .attr("cy", d => d.point.y)
    .exit().remove()

}
updateGraph()

document.getElementById('update').onclick = function(e) {

  data = [
    [{
      point: {
        x: 10,
        y: 10
      }
    }, {
      point: {
        x: 100,
        y: 30
      }
    }],
    [{
        point: {
          x: 30,
          y: 100
        }
      }, {
        point: {
          x: 230,
          y: 30
        }
      },
      {
        point: {
          x: 50,
          y: 300
        }
      },
    ]
  ];
  updateGraph()
}


$('#cb1').click(function() {
  if ($(this).is(':checked')) {
    data = [
      [{
        point: {
          x: 10,
          y: 10
        }
      }, {
        point: {
          x: 100,
          y: 30
        }
      }],
      [{
          point: {
            x: 30,
            y: 100
          }
        }, {
          point: {
            x: 230,
            y: 30
          }
        },
        {
          point: {
            x: 50,
            y: 200
          }
        },
        {
          point: {
            x: 50,
            y: 300
          }
        },
      ]
    ];

  } else {
    data = [
      [{
        point: {
          x: 10,
          y: 10
        }
      }, {
        point: {
          x: 100,
          y: 30
        }
      }]
    ];
  }
  updateGraph()
});

Upvotes: 1

Views: 478

Answers (1)

Mehdi
Mehdi

Reputation: 7403

Problem

The reason why allGroup.exit().remove() does nothing is that the updated dataset still has the same number of items as the original one. The exit selection is therefore empty.

The variable data contains lines, not points. The one defined at page load, and the one inside update listener contain two lines, only the number of points in them differs.

You can check this by putting a console.log(data.length) inside function updateGraph.

Solution 1

Change your data structure. You can assign an id property to each line, and use .data's, key function. cf. d3-selection documentation.

Updated jsFiddle implementing solution 1: see here.

This solution requires less changes.

Solution 2

In case you have no control over the data structure, you can transition the line drawing inside the update selection, rather than the exit one.

Upvotes: 1

Related Questions