Having trouble with expanding rows in a data table on a shiny application when clicking? Here are some solutions to help you fix it!

I have developed a code in shiny R to generate a data table that expands to display child rows when a row is clicked. However, the expansion feature is only working for the last row of the table. For all previous rows, only the headers of the child table are being displayed without any data.

Here is my data table for reference: Data Table

And here is the output I am currently getting: My result

callback = JS("table.column(1).nodes().to$().css({cursor: 'pointer'});

// Formatting the cars object into another table
var format = function (d) {
    if (d != null) {
        var result = ('<table id=\"child_' + d[2] + '_' + d[3] +
            '\">').replace('.', '_') + '<thead><tr>'
        for (var col in d[4][0]) {
            result += '<th>' + col + '</th>'
        }
        result += '</tr></thead></table>'
        return result
    } else {
        return '';
    }
}

var format_datatable = function (d) {
    var dataset = [];
    for (i = 0; i <= d[4].length - 1; i++) {
        var datarow = $.map(d[4][i], function (value, index) {
            return [value];
        });
        dataset.push(datarow)
    }
    var subtable = $(('table#child_' + d[2] + '_' +
        d[3]).replace('.', '_')).DataTable({
        'data': dataset,
        'autoWidth': true,
        'deferRender': true,
        'info': false,
        'lengthChange': false,
        'ordering': true,
        'paging': false,
        'scrollX': false,
        'scrollY': false,
        'searching': false
    });
};

table.on('click', 'td.details-control', function () {
    var td = $(this),
        row = table.row(td.closest('tr'));
    if (row.child.isShown()) {
        row.child.hide();
        td.html('&oplus;');
    } else {
        row.child(format(row.data())).show();
        td.html('&CircleMinus;');
        format_datatable(row.data())
    }
});
")

Answer №1

A challenge arises due to the presence of white spaces in the Description column. If you expand row 3, you'll notice that it works fine since there are no white spaces. However, the JavaScript code creates IDs for child tables with white spaces, making these IDs invalid. To rectify this issue, the white spaces are replaced with underscores (_). Furthermore, simply replacing dots with underscores using replace('.', '_') may not suffice, as this only targets the first occurrence of a dot. The correct approach involves using global replacements: replace(/\./g, '_') for dots and replace(/\s/g, '_') for white spaces.

dat <- data.frame(
  ' ' = rep('&oplus;',2),
  Sr = c(1, 2),
  Description = c("A - B", "X - Y"),
  Details = I(list(list(list(Chromosome = "chr18", SNP = "rs2")), 
                   list(list(Chromosome = "chr19", SNP = "rs3"),
                        list(Chromosome = "chr20", SNP = "rs4")))), 
  check.names = FALSE
)

callback = JS(
  "table.column(1).nodes().to$().css({cursor: 'pointer'});",
  "// Format the nested table into another table",
  "var format = function (d) {",
  "  if (d != null) {",
  "    var result = ('<table class=\"display compact\" id=\"child_' + ",
  "                 ((d[2] + '_' + d[3]).replace(/\\s/g, '_')) +",
  "                 '\">').replace(/\\./g, '_') + '<thead><tr>';", 
  "    for (var col in d[4][0]) {",
  "      result += '<th>' + col + '</th>';",
  "    }",
  "    result += '</tr></thead></table>';",
  "    return result;",
  "  } else {",
  "    return '';",
  "  }",
  "}",
  "var format_datatable = function (d) {",
  "  var dataset = [];",
  "  for (i = 0; i < d[4].length; i++) {",
  "    var datarow = $.map(d[4][i], function (value, index) {",
  "      return [value];",
  "    });",
  "    dataset.push(datarow);",
  "  }",
  "  var subtable = $(('table#child_' + d[2] + '_' + d[3])",
  "                   .replace(/\\./g, '_').replace(/\\s/g, '_')).DataTable({",
  "                     'data': dataset,",
  "                     'autoWidth': true,",
  "                     'deferRender': true,",
  "                     'info': false,",
  "                     'lengthChange': false,",
  "                     'ordering': true,",
  "                     'paging': false,",
  "                     'scrollX': false,",
  "                     'scrollY': false,",
  "                     'searching': false,",
  "                     'sortClasses': false,",
  "                     'columnDefs': [{targets: '_all', className: 'dt-center'}]",
  "                   });",
  "};",
  "table.on('click', 'td.details-control', function () {",
  "  var td = $(this),",
  "      row = table.row(td.closest('tr'));",
  "  if (row.child.isShown()) {",
  "    row.child.hide();",
  "    td.html('&oplus;');",
  "  } else {",
  "    row.child(format(row.data())).show();",
  "    td.html('&CircleMinus;');",
  "    format_datatable(row.data())",
  "  }",
  "});")

datatable(dat, callback = callback, escape = FALSE,
          options = list(
            columnDefs = list(
              list(visible = FALSE, targets = 4),
              list(orderable = FALSE, className = 'details-control', targets = 1)
            )
          ))

https://i.sstatic.net/6enhM.gif

Answer №2

Try using a jQuery selector for the table instead of directly referencing the table variable in the event.

$('.table').on('click', 'td.details-control', function () {
    var td = $(this),
        row = table.row(td.closest('tr'));
    if (row.child.isShown()) {
        row.child.hide();
        td.html('&oplus;');
    } else {
        row.child(format(row.data())).show();
        td.html('&CircleMinus;');
        format_datatable(row.data())
    }
});

This modification should do the trick

For more information, check out the documentation: https://datatables.net/examples/api/row_details.html

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Modifying Element Values with JavaScript in Selenium using C#

As a newcomer to automation, I am facing a challenge with automating a web page that has a text field. I initially attempted using driver.FindElement(By.XPath("Xpath of elemnt").SendKeys("Value"); but unfortunately, this method did not work. I then resor ...

The evaluation of jQuery did not function properly on the less.css file

I have a jQuery function that I am using as shown below @winheight:`$(window).height()` within my less file. However, when I compile it to CSS, I encounter a compiler error I am currently using a software called Crunch Compiler Errors You are usin ...

Unleashing the power of RollupJs: A guide to dynamically bundling modules and objects

Is there a way to dynamically bundle a module/object into my RollupJs output file? I have experimented with various options without success in achieving the desired result. Below is a brief sample project that demonstrates what I am trying to achieve. The ...

Can SailsJS be used exclusively for API processes?

Can SailsJS be used solely as an API? After downloading the Sails project, is it possible to exclude the views and focus only on utilizing Sails as an API? ...

What is the best approach to concurrently update a single array from multiple functions?

In my React app, I have a form with various input fields and checkboxes. Before making an API call to submit the data, I have functions set up to check if any fields are left blank or unchecked. These check functions are triggered when the form button is ...

JavaScript is unable to modify the value of an input in HTML

function modifyComment(id) { var pageID = "<?= $ID ?>"; var commentID = "p" + id; console.log(id); console.log(commentID); console.log(pageID); var commentContent = document.getElementById(commentID).innerHTML; < ...

In Javascript, the color can be changed by clicking on an object, and the color

Looking for help with my current HTML code: <li><a href="index.php" id="1" onclick="document.getElementById('1').style.background = '#8B4513';">Weblog</a></li> The color changes while clicking, but when the lin ...

Consolidate list: Make sure to leave only the currently active item open

For some reason, I am encountering an issue with this code on this platform. The problem is that every time I click on a list title, all items open up instead of just the one I clicked on. Is there a way to modify this code so that only the clicked item ex ...

Ways to constrain checkbox choices to only one within an HTML file as the checklist with the checkboxes is being created by JavaScript

I am working on developing an HTML dialogue box to serve as a settings page for my program. Within this settings page, users can create a list of salespeople that can be later selected from a drop-down menu. My current objective is to incorporate a checkbo ...

Add Firebase Data to Dropdown

Utilizing Vuetify to build a dropdown has been an interesting challenge for me. While I can successfully select a value and store it in the firebase database, I'm facing difficulties when it comes to repopulating the dropdown after page refresh. Belo ...

Tips for Avoiding Inheritance of a Specific Method

Let's say we have two classes, A and B. Class B extends class A, inheriting all of its methods. It is also possible to override these inherited methods. The question at hand is whether it is possible to prevent class B from inheriting a specific metho ...

Is there a way to stop the page from scrolling once I reach the bottom of a specific div within it?

My webpage has multiple divs that share a similar structure: <div style="height:200px; overflow:auto;"> <div style="height:300px;"> <!--There is a lot of content here--> </div> </div> When the user hovers ove ...

Analyzing similarities between objects and arrays to find and return the matches

Items {670: true, 671: true} List 0: {id: 669, item_id: 35} 1: {id: 670, item_id: 35} Desired outcome 0: {id: 670, item_id: 35} Is there a way to compare two datasets and return the matching entries based on their ids? ...

What is the best way to customize multiselection in jqgrid?

jQuery("#grid").jqGrid({ datatype: "local", width:'auto', height: 'auto', multiselect:true, colNames:[ 'no' ], colModel:[ {name:'no', align:'r ...

Having trouble finding the correct output when searching in the table using regex

I am currently experiencing an issue with my code for searching. When I search the full text, it works fine but when I search for a middle character, it does not work as expected. For instance, searching for "apple" or "ap" works correctly, however, if I ...

Sending data to HTML using parameters in a .cshtml document

I encountered a situation where I had to deal with the following code snippet within a .cshtml file: var peopleList = $('#PeopleListTable').dataTable({ // not relevant "fnRender": function (oObj) { ...

Struggling to integrate CKEditor into my Angular project, as I keep encountering the error message "CKEDITOR is not

These are the steps I followed: 1) To begin, I added the ng-ckeditor.min.js file to my project. 2) Next, I included it in the page using the following code: <script type="text/javascript" src="Scripts/ng-ckeditor.min.js"></script> 3) I then ...

The @output decorator in Angular5 enables communication between child and

Hello fellow learners, I am currently diving into the world of Angular and recently stumbled upon the @output decorators in angular. Despite my best efforts to research the topic, I find myself struggling to fully grasp this concept. Here's a snippet ...

Unable to display the value of my array in JSON encoded PHP due to it being undefined

In my cart.php file, I have encoded an array to JSON and returned it to an AJAX function: $data = array(); $data['total'] = '10000'; $data['quantity'] = '10'; echo json_encode($data); In my index.php f ...

Bring in content using transclusion, then swap it out using AngularJS

I am looking to develop a custom directive that will transform : <my-overlay class="someOverlay"> <h4>Coucouc</h4> </my-map-overlay> Into : <div class="someOverlay default-overlay"> <h4>Coucouc</h4&g ...