Home > Blockchain >  Accessing and comparing data in jQuery loop
Accessing and comparing data in jQuery loop

Time:06-28

I have a table on my page which is dynamically built and contains editable data fields. The table looks something like this:

<tr>
    <td >1</td>
    <td ><input type="number" data-id="123" value="123" /></td>
    <td ><input type="number" data-id="456" value="456" /></td>
</tr>
<tr>
    <td >2</td>
    <td ><input type="number" data-id="321" value="321" /></td>
    <td ><input type="number" data-id="654" value="654" /></td>
</tr>

I am using a jQuery .each() loop over all the tr elements, and trying to compare the values of each input to the data=id (the data-id is set at the server as equal to the initial value of the box) so I can save changed values when the user clicks a button.

My function looks something like this:

$('tr').each(function (index, element) {
    var idToSave = $(element).children('.identifier').first().text();
    var toSave = false;
    var $cone = $(element).children('td.pup input[type=number]').first();
    var $ctwo = $(element).children('td.van input[type=number]').first();

    var x = $cone.text();
    alert('Text: '   x);
    var y = $cone.val();
    alert('Val: '   y);
    var z = $cone.data("id");
    alert('Data: '   z);
    
    if ($cone.text() != $cone.data("id")) {
        toSave = true;
    }

    if (toSave) {
        //Do an ajax call to the save method, passing in values
    }
});

When I run my jQuery each() function, I see the identifier correctly (verified by alerts I've since removed) but the variables x, y, and z all come back as undefined. I have confirmed that the variable names are unique within my page (they are NOT x,y,z in my actual page, just this simplified version) and I have experimented with many versions of the code, including using .attr("data-id") and .dataset.id to pull the data off of my input. I feel like I'm missing something simple and obvious.

Cany anyone give any advice?

CodePudding user response:

The main problems here seems:

  • .children() only travels a single level down the DOM tree (docs)
    this was causing the undefined values, since starting from tr you can only search for direct children (I.E. some td) but not td children as input.
  • Using .data('id') returns type number the first time
    (because it parse as a number from data-id="123")
  • missing .pup and .van classes in td (td.pup, td.van selectors are missing; there are no td with this classes, so no inputs will be found)

Solution

  • Update your .children(selector) method with .find(selector) to find deep nested elements
  • Concat '' to your .data('id') to make it a string and to check against your input value.
  • Update your toBeSaved value by checking all the conditions you need

In the following example i used a listener for input.keyup() to run the .each() function on each input change

// I'm setting up a keyup listener to run our .each() every time some input get a keyup event
$('input').keyup(function() {
  console.clear()
  // I'm using 'tbody tr' as selector to exlude the first 'tr' (the header tr aka 'thead tr') 
  $('tbody tr').each(function(index, element) {

    // Use .find() to search for children element

    // this is because .children() method differs from .find() 
    // in that .children() only travels a single level down the DOM tree 
    var idToSave = $(element).find('.identifier').first().text();
    var c1 = $(element).find('td.c1 input[type=number]').first();
    var c2 = $(element).find('td.c2 input[type=number]').first();

    // Check if some value needs to be saved (using .attr() to get 'data-id' value)
    var toSave = c1.val() !== c1.data("id")   '' || c2.val() !== c2.data('id')   '';

    if (toSave) {
      // Optionally build queryString params for your fetch
      /// Note that to do this, i added 'name' attribute in our input
      const rowSerialized = $(element).find('input').filter(function(el) {

        // Including only fields with updated value

        const res = ($(this).data('id')   '') !== $(this).val();
        // Note that .data() will return number the first time

        return res;
      }).serialize();

      // Optionally build a diff object from the serialized string
      const rowDiffsObject = parseSerializedRow(rowSerialized)

      console.log("Saving Row With Id: ", idToSave, "\nDiff Object: ", rowDiffsObject);

      // Just for this example i'm faking save by updating the data-id
      c1.data('id', c1.val());
      c2.data('id', c2.val());
    }
  });
})

// just convert from serialized string
// to diffs object
function parseSerializedRow(rowSerialized) {
  let rowDiffs = {}
  rowSerialized.split('&').forEach((field) => {
    const split = field.split('=');
    const fieldName = split[0];
    const fieldValue = split[1];
    rowDiffs[fieldName] = fieldValue;
  });
  return rowDiffs;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
  <thead>
    <tr>
      <th>Id</th>
      <th>Data 1</th>
      <th>Data 2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td >1</td>
      <td ><input type="number" name="data1" data-id="123" value="123" /></td>
      <td ><input type="number" name="data2" data-id="456" value="456" /></td>
    </tr>
    <tr>
      <td >2</td>
      <td ><input type="number" name="data1" data-id="321" value="321" /></td>
      <td ><input type="number" name="data2" data-id="654" value="654" /></td>
    </tr>
  </tbody>
</table>
<p>Update the values to see when some row needs to update</p>

This way you will be able to perform a save each time one of your lines is updated.

Please note that using .data() to get the initial value (data-id="123") will return a number instead of string. This is because i concat '' every time i check the .data('id')

  • Related