Row Reordering in DataTables/Shiny
Row reordering is a cool extension to the DataTables library, which is packaged to R/Shiny through the DT Package. Unlike other table actions like row selecting, reordering doesn’t have functionality built in so it takes a little more effort to get it working.
First, we have to enable the extension by passing a few flags to the renderDataTable
call. Since Shiny doesn’t have a JS –> R hook built-in for the row-reorder event, we have to make it manually, with some custom javascript code.
## server.R
# example data
get_table <- reactive({
data.frame(
A=LETTERS[1:10]
B=rnorm(10)
)
})
output$table <- DT::renderDataTable({
get_table()
}, extensions='RowReorder', options=list(rowReorder=T), callback=JS(
"// pass on data to R
table.on('row-reorder', function(e, details, changes) {
Shiny.onInputChange('table_row_reorder', JSON.stringify(details));
});"
))
## ui.R
DT::dataTableOutput('table')
Now, when we reorder the rows in our table, we will catch this event in our javascript and pass back the details of the reorder event via. the shiny event table_row_reorder
. To change the underlying order of the table from this event, we first need to listen to the table_row_reorder
event in the server.
# store the order of the table as a reactive value that we
# can set and also listen for changes
table_order <- reactiveVal(value=seq(1,10))
# observe row reordering event - sent from javascript function
observeEvent(input$table_row_reorder, {
info <- input$table_row_reorder
# error checking
if(is.null(info) | class(info) != 'character') { return() }
# I'm using the "yaml" package to read JSON, but you can use "jsonlite"
# if you want. just be aware that jsonlite also uses the "validate"
# function which conflicts with the same "validate" function in shiny.
info <- read_yaml(text=info)
# info will be empty if a reorder event fired but no row orders changed
if(length(info) == 0) { return() }
# load our order vectors
.order <- table_order()
.new_order <- .order
# for each updated row in the info object, update the order vector
for(i in 1:length(info)) {
j <- info[[i]]
.new_order[(j$newPosition + 1)] <- .order[(j$oldPosition + 1)]
}
# update our order vector's reactive value
table_order(.new_order)
})
Here’s a slightly more complex version in action, inside DO-MS:
Bugs, Glitches
- The left row-number column doesn’t change when reordering, but you could create your own “original order” column to let the user keep track of the original order
- There are some visual glitches/flashes when the table is updating. I understand that this can be fixed by modifying the underlying datatables instance directly with a
dataTableProxy()
object (as described here), but I’ve had some issues with it and can’t get it to work
Related Posts
← Back to home