Sortable Tables

The datatable widget is a powerful table-building tool used to display data in shiny apps. It can use many of the datatable plugins and extensions but a small javascript hack is needed for the rowReorder extension which allows rows to be reordered using drag and drop.

In the example below, the rowReorder options are set to selector = 'tr' (select by clicking anywhere on a row) and update = FALSE (do not fire an update to rowReorder). The removal of the update is due to the fact that the rows are not reordered properly in a shiny app. We can now use the row-order event which is fired irrespective of the setting of the update option (row-reordered, however, is only fired when update = true). The javascript function called on row-reorder simply grabs the new order of the table rows and exports it to a shiny variable. This can then be used to update the table.

 1library(shiny)
 2library(DT)
 3
 4## Sortable table
 5
 6server <- function(input, output) {
 7  
 8  df <- reactiveValues(data = mtcars[c(1:20), c('mpg', 'cyl', 'disp')],
 9                       data_reordered = mtcars[c(1:20), c('mpg', 'cyl', 'disp')])
10
11  output$tab1 <- DT::renderDataTable({
12    DT::datatable(df$data, 
13                  selection = 'none', 
14                  extensions = c('Scroller', 'RowReorder'), 
15                  options = list(rowReorder = list(selector = 'tr', update = FALSE), 
16                                 columnDefs = list(list(targets = 0, visible = TRUE)),
17                                 deferRender = TRUE,
18                                 scrollY = 400,
19                                 scroller = TRUE,
20                                 dom = 't'),
21                  callback = JS("table.on('row-reorder', function(e, diff, edit) { 
22                                  var arr = [];
23                                  $(this).parent().find('.dataTable tbody tr').each(function() { arr.push($(this).find('td').eq(0).text()); })
24                                  Shiny.onInputChange('tableReordered', arr);
25                                });")
26                  )
27  })
28
29  observeEvent(input$tableReordered, {
30    df$data_reordered <- df$data[order(match(row.names(df$data), input$tableReordered)), ]
31    df$order <- input$tableReordered
32  })
33
34  output$df_original <- renderTable(df$data, rownames = TRUE)  
35  output$df_reordered <- renderTable(df$data_reordered, rownames = TRUE) 
36}
37
38ui <- fluidPage(
39  column(4, 
40           h4('Sortable Table'),
41           DT::dataTableOutput('tab1')
42         ),
43  column(4, 
44           h4('Original Table'),
45           tableOutput('df_original')
46         ),
47  column(4, 
48           h4('Sorted Table'),
49           tableOutput('df_reordered')
50         )
51)
52
53shinyApp(ui = ui, server = server)
54