I have a datatable with a calculated column. I would like the RShiny user to be able to change the order of rows in this datatable and have the column recalculate based on the new table order.
Using the 'RowReorder' extension within datatable looks like it should be able to achieve this. However, when I set server = T
, the row order immediately reverts back to the original when I try to drag and drop, and when I set server = F
, the rows reorder but the column does not recalculate.
The current code I have is below. I thought it might be due to the order = list(list(0, 'asc'))
forcing the same order each time, but the problem remains when this is simply order = list()
.
suppressPackageStartupMessages({
library(tidyverse)
library(shiny)
library(shinyhelper)
library(sortable)
library(shinyWidgets)
library(DT)
})
data <- data.frame(Type = c("A", "B", "C", "D"),
Value = c(100, 90, 80, 70)) %>%
mutate(calculate = Value - lag(Value))
ui <- function(){
fluidPage(
dataTableOutput("table")
)
}
server <- function(input, output){
updated_data <- reactiveVal(data)
observeEvent(input$table_row_reorder, {
updated_data(updated_data()[input$table_row_reorder$order, ])
updated_data$calculate <- updated_data()$Value - lag(updated_data()$Value)
})
output$table <- renderDT(server = T,
data,
colnames = c("ID" = 1),
extensions = 'RowReorder',
options = list(rowReorder = TRUE, order = list(list(0, 'asc'))))
}
shinyApp(ui, server)
There's no Shiny value input$table_row_reorder
. So, in order to get the new order after a row reordering, one has to listen to the JavaScript event row-reorder
which provides the details about the row reordering, and one has to send this new order to the Shiny server with Shiny.setInputValue
. Then one can use replaceData
to update the table.
library(shiny)
library(DT)
library(dplyr)
dat <- data.frame(Type = c("A", "B", "C", "D"),
Value = c(100, 90, 80, 70)) %>%
mutate(calculate = Value - lag(Value))
js <- c(
"table.on('row-reorder', function(e, details, edit) {",
" var order = [0, 1, 2, 3];",
" for(entry of details) {",
" order[entry.newPosition] = entry.oldPosition;",
" }",
" Shiny.setInputValue('newOrder', order);",
"});"
)
ui <- fluidPage(
br(),
DTOutput("table")
)
server <- function(input, output, session){
output$table <- renderDT({
datatable(
dat,
extensions = 'RowReorder',
callback = JS(js),
options = list(rowReorder = TRUE, order = list(list(0, 'asc')))
)
}, server = TRUE)
proxy <- dataTableProxy("table")
Dat <- reactiveVal(dat)
observeEvent(input$newOrder, {
dat0 <- Dat()
dat1 <- dat0[input$newOrder + 1, ]
dat1$calculate <- dat1$Value - lag(dat1$Value)
replaceData(proxy, dat1, resetPaging = FALSE, rownames = TRUE)
Dat(dat1)
})
}
shinyApp(ui, server)