Updating row data after updating data source

Updating row data after updating data source

eponymeponym Posts: 16Questions: 5Answers: 0

**Scenario: **

A DataTable uses ajax to load data. Each row has a child that displays a custom element using data from a unique URL when a disclosure button is clicked. The unique URL is loaded into the row data object when the DataTable is populated.

There are three methods that the DataTable gets updated. 1) The initial load, 2) Through a SVG map interface, and 3) Through a keyword search. In practice there is the initial load of data when the page first loads, and then the user could choose either the map interface or keyword search to refine their query. Each method is successful in loading a newly populated DataTable.

Issue:

While each method successfully populates the DataTable, it is only the first one that is used and the first time it is used that successfully has the unique URL that populates the custom element when clicking on the disclosure button. After that the DataTable will correctly populate all the parent rows but the data that contains the the unique URL for the child element is undefined.

Sorry I can't provide a live demo. I hope that I have given you enough info to go on. Below is the code that I am using...

Thanks!

D

Data object that populates each row on the first load, from console:

{ detail: "https://cce-datasharing-xxx/profile/projectprofile/4497/15", DT_RowData: {…}, status: "Completed", name: "<b>Accuracy (ARSET 2018) </b><br>Accuracy Assessment of a Land Cover Classification", DT_RowId: "4497" }

Table:

const table = $('#data_table').DataTable({
      language: {
        loadingRecords: "<div id='progHold' class='loader'></div>",

        lengthMenu: ' _MENU_ ',
        search:
          '<div  style=" height:42px; padding: 0.375rem 0.52rem; display:inline-block; border:thin solid #6c757d; margin-right:-6px; border-top-left-radius:6px;border-bottom-left-radius:6px; color:#6c757d; " ><i class="fa-solid fa-filter"></i></div><div class="btn btn-outline-secondary" style="float:right; margin-left:1rem"><i class="fa-solid fa-download fa-lg"></i></div> ',
        searchPlaceholder: 'Filter Records',
        infoFiltered: ' - filtered from _MAX_ records',
      },

      lengthMenu: [
        [10, 25, 50, -1],
        ['10 Rows', '25 Rows', '50 Rows', 'All Rows'],
      ],
      destroy: true,
      oLanguage: {
        sSearch: '',
      },
      ajax: {
        url: theURL,
        contentType: 'application/json',
        type: 'POST',
        destroy: true,
        data: function (d) {
          switch (searchtype) {
            case 'do_search':
              d.programid = postdat.programid;
              d.FT = postdat.FT;
              d.stID = postdat.stID;
              break;
            case 'map_search':
              d.programid = postdat.programid;
              d.mapCodes = postdat.mapCodes;
              break;
          }
          // console.log(JSON.stringify(d));
          return JSON.stringify(d);
        },
        dataSrc: function (json) {
          dataLength = json.data.length;
          $('#resultMessage').html(
            // 'The search term <b>"' +
            //   searchTerm +
            //   '"</b> returned <b>' +
            dataLength + ' items'
          );
          console.log(json.data);
          return json.data;
        },
      },
      error: function (xhr, error, code) {
        console.log(xhr, error, code);
      },
      dom: 'fBtpi',
      buttons: ['pageLength'],
      info: true,
      pageLength: 10,
      order: order,
      columnDefs: [{className: 'text-center', targets: [centeredCol]}],
      columns: cols,
    });

Table functions:

function format(rowdata) {
      console.log(rowdata);
      const detail_link = JSON.stringify(rowdata.detail);
      return `<div class="slider"><detail-element detail=${detail_link}></detail-element></div>`;
    }

    $('#data_table tbody').on('click', 'td.details-control', function () {
      var tr = $(this).closest('tr');
      var row = table.row(tr);

      if (row.child.isShown()) {
        row.child.hide();
        tr.removeClass('shown');
      } else {
        row.child(format(row.data())).show();
        tr.addClass('shown');
        0;
      }
    });

    $('#data_table > tbody').on('mouseover', '> tr', function (e) {
      var data = table.row(this).data();
      if (data === undefined) {
        data = table.row($(this).prev('tr.dt-hasChild')).data();
      }

      // var map_codes = data.DT_RowData.codes;
      // for (var t = 0; t < map_codes.length; t++) {
      //   selColor = Map_Handlers.codes.colors[map_codes[t]];
      //   $('#' + map_codes[t]).css({fill: selColor});
      // }
    });

    $('#data_table > tbody').on('mouseout', '> tr', function () {
      var data = table.row(this).data();
      if (data === undefined) {
        data = table.row($(this).prev('tr.dt-hasChild')).data();
      }

      // var map_codes = data.DT_RowData.codes;
      // for (var t = 0; t < map_codes.length; t++) {
      //   selColor = Map_Handlers.codes.colors[map_codes[t]];

      //   if (map_codes[t].charAt(0) == 'O') {
      //     $('#' + map_codes[t]).css({fill: '#e9ecf1'});
      //   } else {
      //     $('#' + map_codes[t]).css({fill: '#999999'});
      //   }
      // }
    });

This question has an accepted answers - jump to answer

Answers

  • allanallan Posts: 63,691Questions: 1Answers: 10,500 Site admin

    Are you able to PM me a link, or recreate the issue on a public facing page? I think I'll need to be able to trace the code through and see the data being used as well.

    Allan

  • kthorngrenkthorngren Posts: 21,450Questions: 26Answers: 4,975

    If I understand correctly the child row works correctly from the initial data but if the data is reloaded using either 2) Through a SVG map interface, or 3) Through a keyword search then the child row isn't working. If this is correct how are you reloading the table data?

    the data that contains the the unique URL for the child element is undefined.

    Are you saying the format() function shows undefined with console.log(rowdata);?

    There isn't anything obvious from your code snippets to indicate an issue. Are you able to provide an example, with simulated data, that shows the issue?

    The problem could be how you are reloading the data.

    The problem could be the use of destroy: true, if you are reinitializing the Datatable for each data load. There might be left over objects that aren't cleaned up causing the undefined.

    Please provide more details and hopefully a test case that shows the issue. We can help with the test case if needed.
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • eponymeponym Posts: 16Questions: 5Answers: 0

    Thanks for the quick response Allan and Kevin!

    Unfortunately, I can't provide a link to a public facing page as I am developing the interface on my local server, and can only access the database using a secured VPN.

    Creating a sample with simulated data is doable, but would require several hours, and I would prefer to hold off on this unless it becomes absolutely necessary. Apologies.

    "The problem could be how you are reloading data"

    Details- I have a function called do_search(searchfor, prog, srv, searchtype, postdat)

    1) searchfor- is a string that is either 'people', 'projects' or 'publications'

    2) prog- is a string that contains a number that identifies a business unit. It's concatenated on to a URL for accessing the correct database

    3) srv- is a string that identifies the domain root URL that accesses either the development or operational server.

    4) searchtype- is a string that helps choose which URL to use for the search ( depending on whether the we're using the map or keyword search/initial load)

    5) postdat- is an object that contains key/value pairs. This is POST data that is sent through the AJAX call to process the query.

    The function can be called from three separate places:
    1) When the page loads,
    2) When a new search term is added to the keyword search,
    3) When the SVG map is clicked/unclicked

    The function call looks like this:

    DT_Handlers.do_search(
            'projects',
            thisProgID,
            which_serv,
            'do_search',
            postdata
          );
    

    The function looks like this:
    (Note- The function contains the code that I sent previously, plus the housekeeping stuff. This means that every time that I call the do_search method, I am re-initializing the DataTable)

    DT_Handlers = {
      do_search: function (searchfor, prog, srv, searchtype, postdat, ) {
        
        switch (searchfor) {
          case 'people':
            var theURL = srv + '/participants/dtsearch/';
    
            var cols = [
              {
                className: 'details-control',
                orderable: false,
                targets: 0,
                data: null,
                defaultContent: '',
              },
              {title: 'Name', data: 'name'},
              {title: 'Organization', data: 'organization'},
              {title: 'Email', data: 'email'},
            ];
            var order = [[1, 'asc']];
            var centeredCol = 3;
            var srch_pop_text =
              'Free-text searches name, contact information and associated groups for all participants.';
            break;
    
          case 'projects':
            var theURL = '';
    
            searchtype === 'do_search'
              ? (theURL = MUL.mapper_search.do_search + prog)
              : (theURL = MUL.mapper_search.do_map_search + prog);
    
            var cols = [
              {
                className: 'details-control',
                orderable: false,
                targets: 0,
                data: null,
                defaultContent: '',
              },
              {title: 'Project Title', data: 'name'},
              {title: 'Status', data: 'status'},
            ];
            var order = [[1, 'asc']];
            var centeredCol = 2;
            var srch_pop_text =
              'Free-text searches name, contact information and associated groups for all participants.';
            break;
    
          case 'publications':
            var theURL = srv + '/pubsearch/dtsearch/';
            var cols = [
              {title: 'Year', data: 'pubYear'},
              {title: 'Citation', data: 'citation'},
              {title: 'Month', data: 'pubMonth', visible: false},
            ];
            var order = [
              [0, 'desc'],
              [2, 'desc'],
              [1, 'asc'],
            ];
            var centeredCol = 0;
            var srch_pop_text =
              'Free-text searches publication citations. Example: entering terms Carbon Flux will search publication citations "Carbon" AND "Flux"; entering "Carbon Flux" (note double quotes around phrase) will search the exact phrase "Carbon Flux".';
    
            break;
        }
    
        $('#resultMessage').html('');
    
        // $('[data-toggle="popover"]').popover({html: true});
        // $('#srch_info').attr('data-content', srch_pop_text);
    
        const table = $('#data_table').DataTable({
          language: {
            loadingRecords: "<div id='progHold' class='loader'></div>",
    
            lengthMenu: ' _MENU_ ',
            search:
              '<div  style=" height:42px; padding: 0.375rem 0.52rem; display:inline-block; border:thin solid #6c757d; margin-right:-6px; border-top-left-radius:6px;border-bottom-left-radius:6px; color:#6c757d; " ><i class="fa-solid fa-filter"></i></div><div class="btn btn-outline-secondary" style="float:right; margin-left:1rem"><i class="fa-solid fa-download fa-lg"></i></div> ',
            searchPlaceholder: 'Filter Records',
            infoFiltered: ' - filtered from _MAX_ records',
          },
    
          lengthMenu: [
            [10, 25, 50, -1],
            ['10 Rows', '25 Rows', '50 Rows', 'All Rows'],
          ],
          destroy: true,
          oLanguage: {
            sSearch: '',
          },
          ajax: {
            url: theURL,
            contentType: 'application/json',
            type: 'POST',
            data: function (d) {
              switch (searchtype) {
                case 'do_search':
                  d.programid = postdat.programid;
                  d.FT = postdat.FT;
                  d.stID = postdat.stID;
                  break;
                case 'map_search':
                  d.programid = postdat.programid;
                  d.mapCodes = postdat.mapCodes;
                  break;
              }
              // console.log(JSON.stringify(d));
              return JSON.stringify(d);
            },
            dataSrc: function (json) {
              dataLength = json.data.length;
              $('#resultMessage').html(
                // 'The search term <b>"' +
                //   searchTerm +
                //   '"</b> returned <b>' +
                dataLength + ' items'
              );
              console.log(json.data);
              return json.data;
            },
          },
          error: function (xhr, error, code) {
            console.log(xhr, error, code);
          },
          dom: 'fBtpi',
          buttons: ['pageLength'],
          info: true,
          pageLength: 10,
          order: order,
          columnDefs: [{className: 'text-center', targets: [centeredCol]}],
          columns: cols,
        });
    
        function format(rowdata) {
          console.log(rowdata);
          const detail_link = JSON.stringify(rowdata.detail);
          return `<div class="slider"><detail-element detail=${detail_link}></detail-element></div>`;
        }
    
        $('#data_table tbody').on('click', 'td.details-control', function () {
          var tr = $(this).closest('tr');
          var row = table.row(tr);
    
          if (row.child.isShown()) {
            row.child.hide();
            tr.removeClass('shown');
          } else {
            row.child(format(row.data())).show();
            tr.addClass('shown');
            0;
          }
        });
    
        $('#data_table > tbody').on('mouseover', '> tr', function (e) {
          var data = table.row(this).data();
          if (data === undefined) {
            data = table.row($(this).prev('tr.dt-hasChild')).data();
          }
    
          // var map_codes = data.DT_RowData.codes;
          // for (var t = 0; t < map_codes.length; t++) {
          //   selColor = Map_Handlers.codes.colors[map_codes[t]];
          //   $('#' + map_codes[t]).css({fill: selColor});
          // }
        });
    
        $('#data_table > tbody').on('mouseout', '> tr', function () {
          var data = table.row(this).data();
          if (data === undefined) {
            data = table.row($(this).prev('tr.dt-hasChild')).data();
          }
    
          // var map_codes = data.DT_RowData.codes;
          // for (var t = 0; t < map_codes.length; t++) {
          //   selColor = Map_Handlers.codes.colors[map_codes[t]];
    
          //   if (map_codes[t].charAt(0) == 'O') {
          //     $('#' + map_codes[t]).css({fill: '#e9ecf1'});
          //   } else {
          //     $('#' + map_codes[t]).css({fill: '#999999'});
          //   }
          // }
        });
      },
    };
    

    Hope this gives you the info you need to take the next step!

    Thanks!

    David

  • kthorngrenkthorngren Posts: 21,450Questions: 26Answers: 4,975

    Here is a simple example of reloading the data using destroy which is working.
    https://live.datatables.net/gohefoki/795/edit

    In your do_search() function it looks like you are recreating the format function and creating new event handlers for the `details-control click and the mouse over, etc. You probably only want to do this once outside of the function. Similar to the example I posted.

    If this doesn't help then maybe you can adapt my example to show the issue you are having.

    Kevin

  • eponymeponym Posts: 16Questions: 5Answers: 0

    Thanks Kevin! I'll take a look, and will let you know.

  • eponymeponym Posts: 16Questions: 5Answers: 0

    Hi Kevin-

    I have moved in a different direction with reloading the data, and have been only partially successful. Instead of reloading the whole datatable each time, I create it once and then use the following to reload new data.

    ///For map
    const table = $('#data_table').DataTable();
            table.ajax
              .url(
                'https://cce-dev.gsfc.nasa.gov/cgi-bin/mapper/projects_dash.pl?do_map_search=1&programid=15'
              )
              .load();
    
    ///For keyword & initial load
    const table = $('#data_table').DataTable();
            table.ajax
              .url(
                'https://cce-dev.gsfc.nasa.gov/cgi-bin/mapper/projects_dash.pl?do_search=1&programid=15'
              )
              .load();
    

    It successfully loads both the parent and child data as planned.

    However, I need to send unique post data along with the new url.

    Is there a way to send the data object along when using table.ajax.url().load()?

    Thanks!

    David

  • allanallan Posts: 63,691Questions: 1Answers: 10,500 Site admin

    Hi David,

    You need to use ajax.data (as a function in this case) to tell DataTables what data to send to the server. There isn't an option to specify data to send through the ajax.url().load() method (although I can see that it might be useful!).

    Allan

  • eponymeponym Posts: 16Questions: 5Answers: 0

    Hi Allan,

    I'd like to zoom out a little on the issue that we've been discussing, and ask about my approach to see if what I set up is possible or the best way of handling the parameters of the project.

    1) A single selector that gets rendered as a DataTable.

    2) The DataTable will need to be populated by multiple data sources via AJAX w/ different POST data schema – depending on the type of search.

    Example:
    URL- https://xxxxx/projects_dash.pl?do_search=1&programid=15 | POST- {programid:15, FT: 'string', searchterms:{x:foo}}
    
    URL- https://xxxxx/projects_dash.pl?do_map_search=1&programid=15 | POST- {programid:15, mapCodes: ['CSA', 'CNA']
    

    3) Each row of the DT has a details-control that populates a child of that row. The child is populated by data received via AJAX from a URL that is stored in the row data, provided during table initialization and population.

    row data
    { detail: "https://xxx/profile/projectprofile/4633/15", status: "Active", name: "<b>Artisanal Mining (SERVIR) </b><br>Monitoring of Artisanal Mining (Galamsey) in Ghana", DT_RowId: "4633"}, DT_RowData: Object { codes:  "CAF", "GH" ] }
    

    Because I need to provide a different URL and POST data for the different types of searches, I set up the DataTable to have the destroy option to remove all info in the table, and then call the table re-initialize function with he new URL and POST data.

    --Is this the correct approach? | Should I be doing this differently?--

    This approach works perfectly fine initializing and then re-initializing the DataTable which displays the new data using the new URL and POST data. The problem that I am running into is that the details-control only shows the URL it needs to populate the child row on the original initialization, after each successive re-initialization it console.log's as undefined.

    After spending a bit of time trying to solve this problem I discovered something that I'm a bit confused by – but may be a clue to solving this:

    When initializing the the DataTable the first time, I also define the table handlers for mouseover, mouseout for the rows and and the click handler for the detail-control for each row through a function called tableHandlers_init() . If I only define the handlers once on the first initialization of the table, I get the undefined on the successive re-initializations as stated above. If, on the other hand I redefine the the handlers at the same time as I reinitialize the table, I get the undefined, then I get the correct data. If I do this a second time I get undefined twice, then the correct data. And then again, I get undefined three times, then the correct data.

    --Thoughts on this?--

    I was hoping to define the table once and reinitialize with the new URL and POST data. Is this possible?

    Thanks!

    David

  • kthorngrenkthorngren Posts: 21,450Questions: 26Answers: 4,975
    Answer ✓

    I set up the DataTable to have the destroy option to remove all info in the table, and then call the table re-initialize function with he new URL and POST data.

    Other than the ajax option does any other configuration options change, like the columns? If not possibly you can use jQuery ajax() to fetch the new data. Then in the success function use clear() followed by rows.add() to update the rows.

    If I only define the handlers once on the first initialization of the table, I get the undefined on the successive re-initializations as stated above.

    What is undefined?

    Possibly a reference to the original table is not properly cleaned up. If you reinitialize the event handlers and the first doesn't but the second does then possibly you will need to use jQuery off() to turn off the current handler before creating the new one. Possibly if you don't destroy and use the method above you might only need to create the event handlers once.

    Kevin

  • eponymeponym Posts: 16Questions: 5Answers: 0

    You are the man Kevin!

    Using $.ajax() and table.clear().rows.add(results.data).draw(); in the success function did the trick! I need to do further testing - but it appears your answer has ended days of frustration! Very much much appreciated!

    David

Sign In or Register to comment.