hydradon
hydradon

Reputation: 1436

Data loading issue in Table using D3 version 4

I have a table that will be populated with data when I click a button. Below is the Snippet:

function myFunction() {
	myTable = d3.select("#mytable");
	myTable.selectAll("td").remove();
	var rows = add_temp_rows(myTable);
}

function add_temp_rows(table) {
 	data = [{"a" : get_rand_temp(),
           "b" : get_rand_temp()
          }, 
          {"a" : get_rand_temp(),
           "b" : get_rand_temp()
          },
          {"a" : get_rand_temp(),
           "b" : get_rand_temp()
          }];
	var rows = table
		.select('tbody')
		.selectAll('tr')
		.data(data);
		
	rows.enter()
		.append("tr")
		.merge(rows);

	rows.exit().remove();
  
  rows.selectAll("td")
      .data(function (d) {
        return [d.a, d.b];
      })
      .enter()
      .append('td')
      .text(function (d) {
        return d;
      });
  return rows;
}

function get_rand_temp() {
	var precision = 100;
	return Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision);
}
<!DOCTYPE html>

<head>
    <title>Test</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- bootstrap -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
        integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
</head>

<body>

    <div class="container">
      
        <div class="row">
            <button onclick="myFunction()">Try it</button>
            <div id="FilterableTable2" class="col-8">
                <div class="table-responsive">
                    <table class="table table-striped table-dark" id="mytable">
                        <thead class='thead-dark'>
                            <tr>
                                <th>Col1</th>
                                <th>Col2</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td>test</td>
                                <td>test2</td>
                            </tr>
                            <tr>
                                <td>test3</td>
                                <td>test4</td>
                            </tr>
                            <!--<tr>
                                <td>test</td>
                                <td>test2</td>
                            </tr>
                            <tr>
                                <td>test3</td>
                                <td>test4</td>
                            </tr>
                            -->
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>


    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"
        integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
  
    <!-- D3 -->
  
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <!-- <script src="https://d3js.org/d3.v5.min.js"></script> -->

    <script src="js/main.js"></script>
</body>

Now I have several issues:

  1. If the table is empty at the beginning, the first click won't populate the table, but the second click onwards will populate correctly. Although I checked in Chrome and saw that the first click still got processed and some data were printed.
  2. If I hard code some initial values in the HTML and the number of hard-coded rows is equal or greater than the number of new data, then it will work fine from the first click.
  3. If there are fewer initial hard-coded rows (but not empty), the first click will change the data of those rows but will not add more rows to match the data. But the second click onwards will append the correct number of rows.

May I know how to fix this? I'm migrating from D3 v3 to v4, there are many breaking changes. Ideally what I want is that, every click will change the table to display the correct rows of data.

Upvotes: 2

Views: 60

Answers (1)

jacouh
jacouh

Reputation: 8769

The mistake is that during the first call, the object rows does not contain new tr's created with .enter(), even though .merge(row) command d3js.org. So rows.selectAll("td") does not affect new tr's. Newly created tr's are selected during the second call.

So to be effective as soon as the first call, the solution is to call .selectAll("td") just after .merge(row).

    //...
    rows.enter()
        .append("tr")
        .merge(rows)
        .selectAll("td")
        .data(function (d) {
            return [d.a, d.b];
        })
        .enter()
        .append('td')
        .text(function (d) {
            return d;
        });
    //...

<!DOCTYPE html>

<head>
    <title>Test</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- bootstrap -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
        integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
</head>

<body>

    <div class="container">
      
        <div class="row">
            <button onclick="myFunction()">Try it</button>
            <div id="FilterableTable2" class="col-8">
                <div class="table-responsive">
                    <table class="table table-striped table-dark" id="mytable">
                        <thead class='thead-dark'>
                            <tr>
                                <th>Col1</th>
                                <th>Col2</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td>test</td>
                                <td>test2</td>
                            </tr>
                            <tr>
                                <td>test3</td>
                                <td>test4</td>
                            </tr>
                            <!--<tr>
                                <td>test</td>
                                <td>test2</td>
                            </tr>
                            <tr>
                                <td>test3</td>
                                <td>test4</td>
                            </tr>
                            -->
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>


    <!-- jQuery -->
    <!-- script src="https://code.jquery.com/jquery-3.5.1.min.js"
        integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script -->
  
    <!-- D3 -->
  
    <!-- script src="https://d3js.org/d3.v4.min.js"></script -->
    <script src="https://d3js.org/d3.v5.min.js"></script>

    <!-- script src="js/main.js"></script -->

<script>
function myFunction() {
	myTable = d3.select("#mytable");
	myTable.selectAll("td").remove();
	var rows = add_temp_rows(myTable);
}

function add_temp_rows(table) {
 	data = [{"a" : get_rand_temp(),
           "b" : get_rand_temp()
          }, 
          {"a" : get_rand_temp(),
           "b" : get_rand_temp()
          },
          {"a" : get_rand_temp(),
           "b" : get_rand_temp()
          }];
	var rows = table
		.select('tbody')
		.selectAll('tr')
		.data(data);
		
	rows.enter()
		.append("tr")
		.merge(rows)
    .selectAll("td")
      .data(function (d) {
        return [d.a, d.b];
      })
      .enter()
      .append('td')
      .text(function (d) {
        return d;
      });

	rows.exit().remove();
  
  return rows;
}

function get_rand_temp() {
	var precision = 100;
	return Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision);
}
</script>
</body>
</html>

Upvotes: 2

Related Questions