1
votes

I have rather simple application (below) where I try to output grouped table using DataTable with the ability to collapse the groups. I found solution in that is implemented in jQuery here but I have no idea how such complex implementation could be moved into R.

Currently, I am able to collapse within a group but not the whole group itself. Any hints how this could be implemented in Shiny?

My application:

library(shiny)
library(DT)
library(shinyjs)

ui <- fluidPage(

   # Application title
   titlePanel("Collapse/Expand table"),

            mainPanel(
          DTOutput("my_table")

      )
   )


server <- function(input, output) {

    output$my_table<-DT::renderDataTable({

        datatable(mtcars[1:15,1:5],
                  extensions = 'RowGroup', 
                  options = list(rowGroup = list(dataSrc=c(3)),
                                 pageLength = 20),
                  callback = JS("
                                table.on('click', 'tr', function () {
                                    var rowsCollapse = $(this).nextUntil('.group');
                                    $(rowsCollapse).toggleClass('hidden');
                                 });"))
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

EDIT

Given AEF comment one can adjust the code to specify that even has to take place once table body is clicked. This indeed collapses any rows until next group. The remaining part is to restrict the clicks only onto group rows. The callback should be now:

callback = JS("$('#DataTables_Table_0 tbody').on('click', 'tr', function () {
 $(this).nextUntil('.group').toggleClass('hidden');});"))
4
I don't know much about javascript so I haven't been able to figure it out, so I will tell you where I think is the problem: when you click on the grouping rows of the datatable, if you look at the browser's console there will be an error message: Uncaught TypeError: Cannot read property 'row' of undefined. I recommend to add the javascript tag to your question to see if you can get extra help, maybe a question on github.com/rstudio/DT?David Jorquera
It seems the problem is that the event handler is attached to the table itself and not to the rows. So its seems teh second argument you give to the on function is not respected.AEF
Thank you for the comment, indeed you were right. Adjusting the callback allows to collapse any row. Now I just have to figure out how to limit the event to only group rows.An economist
Can someone please help with the issue posted here. stackoverflow.com/questions/60401871/…Ankita Chowdhury

4 Answers

2
votes

It turns out to be a bug of DT's javascript code. There's a click event listener that will record all the info of the clicked cells. However, the RowGroup extension creates a new row that doesn't belong to the original datasets and leads to an error. This error stops the further javascript executions.

In your cases, the tr.group event doesn't work because of the error thrown from the previous cell-click event.

We've fixed this bug and the dev version of DT should work with the below code:

library(shiny)
library(DT)
ui <- fluidPage(# Application title
  titlePanel("Collapse/Expand table"),
  mainPanel(DTOutput("my_table")))

callback_js <- JS(
  "table.on('click', 'tr.dtrg-group', function () {",
  "  var rowsCollapse = $(this).nextUntil('.dtrg-group');",
  "  $(rowsCollapse).toggleClass('hidden');",
  "});"
)

server <- function(input, output) {
  output$my_table <- DT::renderDT({
    datatable(
      mtcars[1:15, 1:5],
      extensions = 'RowGroup',
      options = list(rowGroup = list(dataSrc = 3), pageLength = 20),
      callback = callback_js,
      selection = 'none'
    )
  })
}

# Run the application
shinyApp(ui = ui, server = server)

Thanks again for you reports!

Ticker to the Github issue of DT: https://github.com/rstudio/DT/issues/759

1
votes

Just one other note on this. I noticed that in a standard fluidPage etc, this worked as expected. However, it stopped working when I started using it in an htmlTemplate.

Somewhere along the way, it loses the .hidden class and I had to manually add it.

.hidden {
  display: none !important;
}

Then it worked as expected.

0
votes

Thanks to the AEF comment I was able to boil down the problem. The event has to occur one user clicks on the body $('#DataTables_Table_0 tbody') and only on rows with group identifier 'tr.group'.

The final callback has to be adjusted to take both of those conditions into account.

Therefore application with collapsable rows looks as follows:

library(shiny)
library(DT)
library(shinyjs)

ui <- fluidPage(

   # Application title
   titlePanel("Collapse/Expand table"),

            mainPanel(
          DTOutput("my_table")

      )
   )


server <- function(input, output) {

    output$my_table<-DT::renderDataTable({

        datatable(mtcars[1:15,1:5],
                  extensions = 'RowGroup', 
                  options = list(rowGroup = list(dataSrc=c(3)),
                                 pageLength = 20),
                  callback = JS("
                                $('#DataTables_Table_0 tbody').on('click', 'tr.group', function () {
                                    var rowsCollapse = $(this).nextUntil('.group');
                                    $(rowsCollapse).toggleClass('hidden');
                                 });"))
    })
}

# Run the application 
shinyApp(ui = ui, server = server)
0
votes

As mentioned by @David Joequera in the comments this is a JavaScript error, where one of the default event Handlers of Datatable throws an Error because the row porperty doesn't exist in the Group row.

As a work around we can remove this event handler so that the one for hiding will work.

Also i would recommend you to only target the group rows with the event handler so that you can only fully close and open the groups and can not semi hide groups. You can achieve this by simply adding a ".group" to your event Listener Target. Resulting in this code:

table.on('click', 'tr.group', function () {
   var rowsCollapse = $(this).nextUntil('.group');
   $(rowsCollapse).toggleClass('hidden');
})

In order to remove the event Handler we will need to wait until the table is properly loaded and the problematic event handler is atached so I would recommend working with a small timeout. 1000 ms worked fine for me and should not bring any usability issues. so adding this code to the Callbacks should fix the issue:

setTimeout(function(){$('#DataTables_Table_0').off('click.dt','tbody td')},1000);

Be aware that the the ID of the DataTable to delete might change in your final/real solution

Resulting in this code for the demo:

library(shiny)
library(DT)
library(shinyjs)

ui <- fluidPage(

  # Application title
  titlePanel("Collapse/Expand table"),

  mainPanel(
    DTOutput("my_table")

  ),
)


server <- function(input, output) {

output$my_table<-DT::renderDataTable({

datatable(mtcars[1:15,1:5],
          extensions = 'RowGroup', 
          options = list(rowGroup = list(dataSrc=c(3)),
                         pageLength = 20),
          callback = JS("
                           setTimeout(function(){$('#DataTables_Table_0').off('click.dt','tbody td')},1000);
                           table.on('click', 'tr.group', function () {
                                var rowsCollapse = $(this).nextUntil('.group');
                                $(rowsCollapse).toggleClass('hidden');
                             });"))
})



}

# Run the application 
shinyApp(ui = ui, server = server)