8

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.

awwsmm
  • 1,353
  • 1
  • 18
  • 28

2 Answers2

8

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.

awwsmm
  • 1,353
  • 1
  • 18
  • 28
  • 2
    Better than my answer! – DeanAttali Oct 18 '18 at 19:19
  • 1
    Thanks for pointing to `input$table_serach_columns`. Very useful! Any idea how to parse the output (removing brackets and quotes), especially when multiple features are selected per column e.g `"[\"AAK1\",\"AARS\"]"`. Desired output would be `AAK1` and `AARS` – MrNetherlands Jun 07 '19 at 14:00
  • @cholland, I just used some regular expressions – awwsmm Jun 07 '19 at 15:23
  • @awwsmm can you give an example of a reactive table that updates to reflect the filtered state (i.e. how to apply `input$table_state` to `input$table`) – isthisthat Jun 07 '23 at 15:46
  • Also to contribute with the bonus answer, you can provide a clear string result ( instead of a array of empty and no empty values) by using he following lines: `allVals <- sapply(input$table_state$columns, function(x) x$search$search)` and `currentFilter <- paste0(allVals[!is.na(allVals) & allVals !=""],sep=";") ` – Corina Roca Jun 08 '23 at 15:38
1

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

DeanAttali
  • 25,268
  • 10
  • 92
  • 118