glin / reactable

Interactive data tables for R
https://glin.github.io/reactable
Other
613 stars 79 forks source link

Get sorting state in Shiny #265

Closed pvictor closed 1 year ago

pvictor commented 1 year ago

Hello,

Is it possible to get sorting options in a shiny application ? On which columns is the table sorted and in which order. Something like :

getReactableState("table", "sort")

will be great !

glin commented 1 year ago

Hi, that's not possible with getReactableState() today, but would be fairly straightforward to add - added as a feature request. I just didn't know if anyone would find it useful to get the sorted state, so I'm curious what you want to use it for, if you have time to share.

There is a a way to get sorted state in the JavaScript API though, using Reactable.getState(). This could be used as a partial workaround (send it to Shiny via Shiny.setInputValue()), although there's also not currently a way to listen on state changes and automatically send the sorting state up.

pvictor commented 1 year ago

I want to export the table data in a special format (an Excel file with added metadata), and I would like to keep the sorting done by the user in the export. Reactable.getState().sorted is perfect for my use-case I think, I can get the value when export button is clicked and reproduce the sorting in R (I just tested with simple sorting for the moment, have to check how to manage NA or more complicated sorting). Otherwise getting the row indice sorted (and maybe with filters applied) could be nice, but less straightforward.

Here's my code using the JavaScript API :

library(shiny)
library(reactable)

# custom handler otherwise JSON is over simplified
registerInputHandler("reactable_sort", function(x, ...) {
  x
}, force = TRUE)

ui <- fluidPage(
  downloadButton(
    "downloadData", "Download",
    onClick = "Shiny.setInputValue('table_state:reactable_sort', Reactable.getState('cars_table').sorted)"
  ),
  reactableOutput("cars_table"),
  verbatimTextOutput("test")
)

server <- function(input, output) {
  output$cars_table <- renderReactable({
    reactable(MASS::Cars93)
  })
  output$test <- renderPrint(input$table_state)
  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      data <- if (is.null(input$table_state)) {
        MASS::Cars93
      } else {
        data <- MASS::Cars93
        table_state <- input$table_state
        for (i in rev(seq_along(input$table_state))) {
          col <- table_state[[i]]$id
          desc <- table_state[[i]]$desc
          data <- data[order(data[[col]], decreasing = isTRUE(desc)), ]
        }
        data
      }
      write.csv(data, file)
    }
  )
}

shinyApp(ui, server)
glin commented 1 year ago

getReactableState() now includes the sorted state in the development version. If columns are sorted, getReactableState(outputId, "sorted") will returned a named list of sorted columns like list(Model = "asc", Type = "desc").

And then I also added a way to listen on table state changes in the JavaScript API, so you can send table state to Shiny even if getReactableState() doesn't support it.

  • New Reactable.onStateChange() method in the JavaScript API that sets up a function to be called whenever the table state changes (#265).
  • getReactableState() now includes the current sorted columns (#265).