7
votes

I have a DT data table in R Shiny and I have enabled column filtering by setting filter="top" within renderDT(). I now want to extract the user-applied filters so I can save them in variables server-side and reapply them when -- for instance -- a database is updated, requiring an update of the table.

Here's a MWE using Shiny Dashboard:

library(shiny)           #  Shiny web app
library(shinydashboard)  #  Dashboard framework for Shiny
library(plotly)          #  Plotly interactive plots
library(DT)

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(),
  dashboardBody(
    fluidRow(column(12, DTOutput("table")))
  )
)

server <- function(input, output, session) {
  fileData <- reactiveFileReader(1000, session, 'test.csv', read.csv)
  output$table <- renderDT(fileData(), filter = "top")
}

shinyApp(ui, server)

To reiterate, I'd like to save the filters (for instance, a user might select a range of numeric values or a specific factor from one of the filter boxes) as input$ variables so I can use them on the server side.

2

2 Answers

6
votes

I think the simplest way to do this is to just add

options = list(stateSave = TRUE)

inside the renderDT() function. Then, within the server, the state of the table can be accessed at any time with input$<tableID>_state (my table is just called "table" so this becomes input$table_state:

observeEvent(input$table_state, {
  str(input$table_state)
})

The whole solution is then:

library(shiny)
library(shinydashboard)
library(plotly)
library(DT)

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(),
  dashboardBody(
    box(DTOutput("table"))
  )
)

server <- function(input, output, session) {
  fileData <- reactiveFileReader(1000, session, 'www/test.csv', read.csv)
  output$table <- renderDT(fileData(), filter = "top",
    options = list(stateSave = TRUE))

  observeEvent(input$table_state, {
    str(input$table_state)
  })

}

shinyApp(ui, server)

Sample output within the RStudio console:

List of 6
 $ time   : num 1.54e+12
 $ start  : int 0
 $ length : int 10
 $ order  : list()
 $ search :List of 4
  ..$ search         : chr ""
  ..$ smart          : logi TRUE
  ..$ regex          : logi FALSE
  ..$ caseInsensitive: logi TRUE
 $ columns:List of 5
  ..$ :List of 2
  .. ..$ visible: logi TRUE
  .. ..$ search :List of 4
  .. .. ..$ search         : chr ""
  .. .. ..$ smart          : logi TRUE
  .. .. ..$ regex          : logi FALSE
  .. .. ..$ caseInsensitive: logi TRUE
  ..$ :List of 2
  .. ..$ visible: logi TRUE
  .. ..$ search :List of 4
  .. .. ..$ search         : chr "[\"0\"]"
  .. .. ..$ smart          : logi TRUE
  .. .. ..$ regex          : logi FALSE
  .. .. ..$ caseInsensitive: logi TRUE
  ..$ :List of 2
  .. ..$ visible: logi TRUE
  .. ..$ search :List of 4
  .. .. ..$ search         : chr "[\"8\"]"
  .. .. ..$ smart          : logi TRUE
  .. .. ..$ regex          : logi FALSE
  .. .. ..$ caseInsensitive: logi TRUE
  ..$ :List of 2
  .. ..$ visible: logi TRUE
  .. ..$ search :List of 4
  .. .. ..$ search         : chr ""
  .. .. ..$ smart          : logi TRUE
  .. .. ..$ regex          : logi FALSE
  .. .. ..$ caseInsensitive: logi TRUE
  ..$ :List of 2
  .. ..$ visible: logi TRUE
  .. ..$ search :List of 4
  .. .. ..$ search         : chr ""
  .. .. ..$ smart          : logi TRUE
  .. .. ..$ regex          : logi FALSE
  .. .. ..$ caseInsensitive: logi TRUE

Note the search lists which show the filters applied to each column.

Bonus

For super-easy filter extraction, use input$table_search_columns. This gives the same result as using sapply:

sapply(input$table_state$columns, function(x) x$search$search)

This will give something like

[1] ""        "[\"0\"]" "[\"8\"]" ""        ""      

for the above example.

1
votes

There may be a simpler way to do this, but here's my quick 5 minute solution:

Every time the table redraws (which will happen when you update a filter, but will also happen when you sort, paginate, or do anything else that causes a redraw), inject some javascript that looks for the value of the filter you're interested in. We can use then use this method to send the value as an input to the server side of the shiny app in R.

I will be using the mtcars dataset rather than a csv file, I'm using a slightly simpler template than what you provided, and I'll be specifically looking for filtered values of the third column which is disp. This should be enough to help you solve your specific case.

library(shiny)

ui <- fluidPage(
  "disp filter:",
  textOutput("dispOut"),
  DT::dataTableOutput("table")
)

server <- function(input, output, session) {
  output$table <- DT::renderDataTable(
    DT::datatable(
      mtcars,
      filter = "top",
      options = list(
        drawCallback = JS('function(){ Shiny.onInputChange("dispFilter", this.api().table().columns(3).search()[0]); }')
      )
    )
  )

  output$dispOut <- renderText(input$dispFilter)
}

shinyApp(ui,server)

Edit: the other answer by @awwsmm is better, this one is more javascript based, the other is more shiny