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.

library(shiny)
library(DT)

## Sortable table

server <- function(input, output) {
  
  df <- reactiveValues(data = mtcars[c(1:20), c('mpg', 'cyl', 'disp')],
                       data_reordered = mtcars[c(1:20), c('mpg', 'cyl', 'disp')])

  output$tab1 <- DT::renderDataTable({
    DT::datatable(df$data, 
                  selection = 'none', 
                  extensions = c('Scroller', 'RowReorder'), 
                  options = list(rowReorder = list(selector = 'tr', update = FALSE), 
                                 columnDefs = list(list(targets = 0, visible = TRUE)),
                                 deferRender = TRUE,
                                 scrollY = 400,
                                 scroller = TRUE,
                                 dom = 't'),
                  callback = JS("table.on('row-reorder', function(e, diff, edit) { 
                                  var arr = [];
                                  $(this).parent().find('.dataTable tbody tr').each(function() { arr.push($(this).find('td').eq(0).text()); })
                                  Shiny.onInputChange('tableReordered', arr);
                                });")
                  )
  })

  observeEvent(input$tableReordered, {
    df$data_reordered <- df$data[order(match(row.names(df$data), input$tableReordered)), ]
    df$order <- input$tableReordered
  })

  output$df_original <- renderTable(df$data, rownames = TRUE)  
  output$df_reordered <- renderTable(df$data_reordered, rownames = TRUE) 
}

ui <- fluidPage(
  column(4, 
           h4('Sortable Table'),
           DT::dataTableOutput('tab1')
         ),
  column(4, 
           h4('Original Table'),
           tableOutput('df_original')
         ),
  column(4, 
           h4('Sorted Table'),
           tableOutput('df_reordered')
         )
)

shinyApp(ui = ui, server = server)


Harvey Lieberman

Written by

Updated