Search code examples
rshinydtselectinput

How to preserve the filters of a datatable while updating the data?


When I apply or adjust the filters from the options given in filter = 'top' in renderDT. I would like those filters to remain after I adjust the (selected_column) using the selectInput in the sidebarPanel.

Currently the problem I have been dealing with is that every time I select a new choice from the selectInput, example (Monthly", "Two Month"...), the filters refresh, and I have to then reapply them. I do not want to have to do that.

I've tried several methods I would think stateSave = TRUE would fix my problem, but alas, it does not.

Specifically, I believe it has to do with the fact that the "average_type" is coming from the ui portion of the code, but I don't know how to resolve this in any other way.

ui <- fluidPage(
  titlePanel("Average Data Viewer"),
  sidebarLayout(
    sidebarPanel(
      selectInput("average_type", "Select Average Type:",
                  choices = c("Monthly" = "monthly_average",
                              "Two Month" = "two_month_average",
                              "Three Month" = "three_month_average",
                              "Four Month" = "four_month_average",
                              "Six Month" = "six_month_average",
                              "Year" = "year_average"),
                  selected = "monthly_average")
    ),
    mainPanel(
      DTOutput("catalogTable")
    )
  )
)

This is a demo code (shorter) that shows the problem I am facing.

library(shiny)
library(DT)
library(shinyjs)
library(shinyWidgets)
library(ggplot2)
library(dplyr)

# UI
ui <- fluidPage(
  titlePanel("Average Data Viewer"),
  sidebarLayout(
    sidebarPanel(
      selectInput("average_type", "Select Average Type:",
                  choices = c("Monthly" = "monthly_average",
                              "Two Month" = "two_month_average",
                              "Three Month" = "three_month_average",
                              "Four Month" = "four_month_average",
                              "Six Month" = "six_month_average",
                              "Year" = "year_average"),
                  selected = "monthly_average")
    ),
    mainPanel(
      DTOutput("catalogTable")
    )
  )
)

# Server
server <- function(input, output, session) {
  # Sample data frame
  cat2 <- data.frame(
    part_number = c("A", "B", "C"),
    quantity = c(500, 6, 34),
    monthly_average = c(100, 150, 200),
    two_month_average = c(120, 160, 220),
    three_month_average = c(130, 170, 230),
    four_month_average = c(140, 180, 240),
    six_month_average = c(160, 200, 260),
    year_average = c(180, 220, 280)
  )
  
  cat2 <- cat2 %>%
    mutate(MOH = quantity/monthly_average)
  
  
  # Initialize reactive values to store the previous selected column and table state
  prevSelectedColumn <- reactiveVal(NULL)
  prevTableState <- reactiveVal(NULL)
  
  output$catalogTable <- renderDT({
    # Select the appropriate column based on user input
    selected_column <- input$average_type
    catalogTable <- datatable(cat2[, c("part_number", "quantity", "MOH", selected_column)],
                              filter = 'top',
                              options = list(
                                stateSave = TRUE))
    
    return(catalogTable)
  })
 
}

# Run the application
shinyApp(ui = ui, server = server)

To reiterate, I am trying to preserve the filters from filter = 'top' even when the displayed column, which is chosen from the selectInput, changes.


Solution

  • stateSave is not applicable for preserving the filters. However, you could use a combination of dataTableProxy() and updateSearch() as shown below.

    enter image description here

    library(shiny)
    library(DT)
    library(shinyWidgets)
    library(dplyr)
    
    if (!exists("default_search"))
      default_search <- ""
    if (!exists("default_search_columns"))
      default_search_columns <- NULL
    
    # UI
    ui <- fluidPage(titlePanel("Average Data Viewer"),
                    sidebarLayout(sidebarPanel(
                      selectInput(
                        "average_type",
                        "Select Average Type:",
                        choices = c(
                          "Monthly" = "monthly_average",
                          "Two Month" = "two_month_average",
                          "Three Month" = "three_month_average",
                          "Four Month" = "four_month_average",
                          "Six Month" = "six_month_average",
                          "Year" = "year_average"
                        ),
                        selected = "monthly_average"
                      )
                    ),
                    mainPanel(DTOutput("catalogTable"))))
    
    # Server
    server <- function(input, output, session) {
      # Sample data frame
      cat2 <- data.frame(
        part_number = c("A", "B", "C"),
        quantity = c(500, 6, 34),
        monthly_average = c(100, 150, 200),
        two_month_average = c(120, 160, 220),
        three_month_average = c(130, 170, 230),
        four_month_average = c(140, 180, 240),
        six_month_average = c(160, 200, 260),
        year_average = c(180, 220, 280)
      )
      
      cat2 <- cat2 %>%
        mutate(MOH = quantity / monthly_average)
      
      
      # Initialize reactive values to store the previous selected column and table state
      prevSelectedColumn <- reactiveVal(NULL)
      prevTableState <- reactiveVal(NULL)
      
      output$catalogTable <- renderDT({
        # Select the appropriate column based on user input
        selected_column <- input$average_type
        catalogTable <-
          datatable(
            cat2[, c("part_number", "quantity", "MOH", selected_column)],
            filter = 'top',
            options = list(searchCols = default_search_columns,
                           stateSave = FALSE)
          )
        
        return(catalogTable)
      })
      
      observeEvent(input$average_type, {
        isolate({
          default_search <- input$catalogTable_search
          default_search_columns <-
            c("", input$catalogTable_search_columns)
          
          proxy %>% updateSearch(keywords =
                                   list(global = default_search, columns = default_search_columns))
        })
      })
      
      proxy <- dataTableProxy('catalogTable')
      
    }
    
    # Run the application
    shinyApp(ui = ui, server = server)