Yanofsky
Yanofsky

Reputation: 1856

How to update nested child nodes using D3 v4's update pattern

I'm having trouble getting the certain descendants to update their data when using the D3 v4 update pattern.

What is the proper way to get changes in a data-join to propagate to children?

Here is an example https://bl.ocks.org/yanofsky/5b30b12582b6b66bc262b165806a6dc5

When you click the buttons, only the text changes rather than both the text and the colors of the rects.

Upvotes: 0

Views: 2109

Answers (1)

Marcelo
Marcelo

Reputation: 4282

You should follow the pattern of adding data for every element you add. In this way you always have a update, enter and exit selections. After removing the exit and adding new elements for enter, you can merge update and enter and update the attributes:

Here is the updated block

  //define some data
var data = [
        {"location": 1, "month": "Jan", "year": 2017, "value": "#ccc"},
        {"location": 2, "month": "Jan", "year": 2017, "value": "#999"},
        {"location": 3, "month": "Jan", "year": 2017, "value": "#666"},
        {"location": 4, "month": "Jan", "year": 2017, "value": "#333"},
        {"location": 1, "month": "Jan", "year": 2018, "value": "#fcc"},
        {"location": 2, "month": "Jan", "year": 2018, "value": "#f99"},
        {"location": 3, "month": "Jan", "year": 2018, "value": "#f66"},
    {"location": 4, "month": "Jan", "year": 2018, "value": "#f33"},
        {"location": 1, "month": "Feb", "year": 2017, "value": "#cfc"},
        {"location": 2, "month": "Feb", "year": 2017, "value": "#9f9"},
        {"location": 3, "month": "Feb", "year": 2017, "value": "#6f6"},
        {"location": 4, "month": "Feb", "year": 2017, "value": "#3f3"},
        {"location": 1, "month": "Feb", "year": 2018, "value": "#ccf"},
        {"location": 2, "month": "Feb", "year": 2018, "value": "#99f"},
        {"location": 3, "month": "Feb", "year": 2018, "value": "#66f"},
        {"location": 4, "month": "Feb", "year": 2018, "value": "#33f"},
    {"location": 5, "month": "Feb", "year": 2018, "value": "#0505ff"},
    ]

 // nest the data by month then year
 var by_month = d3.nest()
        .key(function(d){return d.month})
        .key(function(d){return d.year})
        .entries(data)


function render(ident, key_month) {

    var w = 500
    var h = 100

    var container = d3.select(ident)

  // select and add a container div for each year in the data
  // using only the data for the target month
    var year = container
        .selectAll("div.year")

  var data = by_month.filter(function(d){return d.key == key_month})[0].values;
    var yearAll= year.data(data);


  yearAll.exit().remove();


  var yearEnter= yearAll
        .enter()
    .append("div")
    .classed("year",true);

  //Add h2 and svg
  yearEnter.append("h2");
  yearEnter.append("svg")
                    .append("g")
                    .classed("gwrapper",true);

  yearEnter = yearEnter.merge(yearAll);

  yearEnter.select("h2")
    .text(function(d) {return  d.values[0].month + " " + d.values[0].year ;});

  // update the svg dimensions
  yearEnter.select("svg")
    .attr("width", w)
        .attr("height", h);


  // select and add element g location
    var gloc = yearEnter.select(".gwrapper").selectAll("g")
                                             .data(function(d){return d.values;});

  gloc.exit().remove();


  var glocEnter = gloc.enter()
                                        .append("g")
                       .classed("loc",true);

  glocEnter.append("rect");
           ;

  glocEnter = glocEnter.merge(gloc);


  // merge and position element wrappers
  glocEnter
        .attr("transform", function(d,i){return "translate("+[w/5*i]+")"});

  var t = d3.transition().duration(1000);

  glocEnter.select("rect")
            .attr("x", (w/5 - 5)/2)
                                .attr("y", (w/5 - 5)/2)
                                .attr("width", 0)
                                    .attr("height", 0)
                                .attr("fill", "black")
                                .transition(t)
                                .attr("x", 5)
                                .attr("y", 0)
                                    .attr("width", w/5 - 5)
                                    .attr("height", w/5 - 5)
                                .attr("fill", function(d){return d.value});

}

// on button click change the subset of data being used
d3.selectAll(".month").on("click", function(){
    render("#toggle",this.getAttribute("data-month"))
}) 


d3.selectAll("#clearb").on("click", function(){
   d3.selectAll("g.loc").remove();
}) 

render("#toggle","Jan")

Upvotes: 3

Related Questions