Search code examples
rselectshinyshinywidgets

When using selectizeGroupUI from shinyWidgets, how to limit default selection to a specified subset of data?


The below example code for selectizeGroupUI() works great for my needs. However by default when first invoking it selects and shows the entire dataset, before the user applies any filters.

My problem is the dataset I'm using this for is very large and takes some time to load. Is there a way to limit the initial dataset view to a subset of the data frame (in this example, manufacturer = Audi), and the user clicks another to-be-added button in order to show the complete dataset?

Example code:

library(shiny)
library(shinyWidgets)

data("mpg", package = "ggplot2")

ui <- fluidPage(
  fluidRow(
    column(
      width = 10, offset = 1,
      tags$h3("Filter data with selectize group"),
      panel(
        checkboxGroupInput(
          inputId = "vars",
          label = "Variables to use:",
          choices = c("manufacturer", "model", "trans", "class"),
          selected = c("manufacturer", "model", "trans", "class"),
          inline = TRUE
        ),
        selectizeGroupUI(
          id = "my-filters",
          params = list(
            manufacturer = list(inputId = "manufacturer", title = "Manufacturer:"),
            model = list(inputId = "model", title = "Model:"),
            trans = list(inputId = "trans", title = "Trans:"),
            class = list(inputId = "class", title = "Class:")
          )
        ),
        status = "primary"
      ),
      DT::dataTableOutput(outputId = "table")
    )
  )
)

server <- function(input, output, session) {
  
  vars_r <- reactive({
    input$vars
  })
  
  res_mod <- callModule(
    module = selectizeGroupServer,
    id = "my-filters",
    data = mpg,
    vars = vars_r
  )
  
  output$table <- DT::renderDataTable({
    req(res_mod())
    res_mod()
  })
}

shinyApp(ui, server)

Solution

  • Since we are dealing with a module (and the inputs are not directly accessible), I modified the function selectizeGroupServer To include an updater for manufacturer input. The new function is called selectizeGroupServer_custom

        observe({
        updateSelectInput(inputId = 'manufacturer', choices = unique(rv$data$manufacturer), selected = 'audi')
        })
    

    new module:

    selectizeGroupServer_modified <- 
    function(input, output, session, data, vars) 
    {
      
      `%inT%` <- function(x, table) {
        if (!is.null(table) && ! "" %in% table) {
          x %in% table
        } else {
          rep_len(TRUE, length(x))
        }
      }
      
      ns <- session$ns
      shinyWidgets:::toggleDisplayServer(session = session, id = ns("reset_all"), 
                          display = "none")
      rv <- reactiveValues(data = NULL, vars = NULL)
      observe({
        if (is.reactive(data)) {
          rv$data <- data()
        }
        else {#this will be the first data
          rv$data <- as.data.frame(data)
        }
        if (is.reactive(vars)) { #this will be the data type for vars
          rv$vars <- vars()
        }
        else {
          rv$vars <- vars
        }
        for (var in names(rv$data)) {
          if (var %in% rv$vars) {
            shinyWidgets:::toggleDisplayServer(session = session, id = ns(paste0("container-", 
                                                                  var)), display = "table-cell")
          }
          else {
            shinyWidgets:::toggleDisplayServer(session = session, id = ns(paste0("container-", 
                                                                  var)), display = "none")
          }
        }
      })
      observe({
        lapply(X = rv$vars, FUN = function(x) {
          vals <- sort(unique(rv$data[[x]]))
          updateSelectizeInput(session = session, inputId = x, 
                               choices = vals, server = TRUE)
          
          #CODE INSERTED HERE
          if (x == 'manufacturer') {
            updateSelectizeInput(session = session, inputId = x, 
                                 choices = vals, server = TRUE, selected = 'manufacturer')
          }
          
          
        })
      })
      observeEvent(input$reset_all, {
        lapply(X = rv$vars, FUN = function(x) {
          vals <- sort(unique(rv$data[[x]]))
          updateSelectizeInput(session = session, inputId = x, 
                               choices = vals, server = TRUE)
        })
      })
      observe({
        vars <- rv$vars
        lapply(X = vars, FUN = function(x) {
          ovars <- vars[vars != x]
          observeEvent(input[[x]], {
            data <- rv$data
            indicator <- lapply(X = vars, FUN = function(x) {
              data[[x]] %inT% input[[x]]
            })
            indicator <- Reduce(f = `&`, x = indicator)
            data <- data[indicator, ]
            if (all(indicator)) {
              shinyWidgets:::toggleDisplayServer(session = session, id = ns("reset_all"), 
                                  display = "none")
            }
            else {
              shinyWidgets:::toggleDisplayServer(session = session, id = ns("reset_all"), 
                                  display = "block")
            }
            for (i in ovars) {
              if (is.null(input[[i]])) {
                updateSelectizeInput(session = session, inputId = i, 
                                     choices = sort(unique(data[[i]])), server = TRUE)
              }
            }
            if (is.null(input[[x]])) {
              updateSelectizeInput(session = session, inputId = x, 
                                   choices = sort(unique(data[[x]])), server = TRUE)
            }
          }, ignoreNULL = FALSE, ignoreInit = TRUE)
        })
      })
      
        observe({
        updateSelectInput(inputId = 'manufacturer', choices = unique(rv$data$manufacturer), selected = 'audi')
        })
       
      
      return(reactive({
        data <- rv$data
        vars <- rv$vars
        indicator <- lapply(X = vars, FUN = function(x) {
           `%inT%`(data[[x]], input[[x]]) 
        })
        indicator <- Reduce(f = `&`, x = indicator)
        data <- data[indicator, ]
        return(data)
      }))
    }
    

    app:

    library(shiny)
    library(shinyWidgets)
    
    data("mpg", package = "ggplot2")
    
    ui <- fluidPage(
      fluidRow(
        column(
          width = 10, offset = 1,
          tags$h3("Filter data with selectize group"),
          panel(
            checkboxGroupInput(
              inputId = "vars",
              label = "Variables to use:",
              choices = c("manufacturer", "model", "trans", "class"),
              selected = c("manufacturer", "model", "trans", "class"),
              inline = TRUE
            ),
            selectizeGroupUI(
              id = "my-filters",
              params = list(
                manufacturer = list(inputId = "manufacturer", title = "Manufacturer:"),
                model = list(inputId = "model", title = "Model:"),
                trans = list(inputId = "trans", title = "Trans:"),
                class = list(inputId = "class", title = "Class:")
              )
            ),
            status = "primary"
          ),
          DT::dataTableOutput(outputId = "table")
        )
      )
    )
    
    server <- function(input, output, session) {
      
      
      
      vars_r <- reactive({
        input$vars
      })
      
      res_mod <- callModule(
        module = selectizeGroupServer_modified,
        id = "my-filters",
        data = mpg,
        vars = vars_r
      )
      
      
      
      output$table <- DT::renderDataTable({
        res_mod()
      })
    }
    
    
    shinyApp(ui, server)
    

    EDIT:

    If we want to have a button that says "show all data", we can modify selectizeGroupUI. The new name will be selectizeGroupUI_custom

    Modules and App code:

    library(shiny)
    library(shinyWidgets)
    
    # SERVER MODULE -----------------------------------------------------------
    
    
    selectizeGroupServer_modified <-
      function(input, output, session, data, vars) {
        `%inT%` <- function(x, table) {
          if (!is.null(table) && !"" %in% table) {
            x %in% table
          } else {
            rep_len(TRUE, length(x))
          }
        }
    
        ns <- session$ns
        shinyWidgets:::toggleDisplayServer(
          session = session, id = ns("reset_all"),
          display = "none"
        )
        rv <- reactiveValues(data = NULL, vars = NULL)
        observe({
          if (is.reactive(data)) {
            rv$data <- data()
          } else { # this will be the first data
            rv$data <- as.data.frame(data)
          }
          if (is.reactive(vars)) { # this will be the data type for vars
            rv$vars <- vars()
          } else {
            rv$vars <- vars
          }
          for (var in names(rv$data)) {
            if (var %in% rv$vars) {
              shinyWidgets:::toggleDisplayServer(session = session, id = ns(paste0(
                "container-",
                var
              )), display = "table-cell")
            } else {
              shinyWidgets:::toggleDisplayServer(session = session, id = ns(paste0(
                "container-",
                var
              )), display = "none")
            }
          }
        })
        observe({
          lapply(X = rv$vars, FUN = function(x) {
            vals <- sort(unique(rv$data[[x]]))
            updateSelectizeInput(
              session = session, inputId = x,
              choices = vals, server = TRUE
            )
          })
        })
        observeEvent(input$reset_all, {
          lapply(X = rv$vars, FUN = function(x) {
            vals <- sort(unique(rv$data[[x]]))
            updateSelectizeInput(
              session = session, inputId = x,
              choices = vals, server = TRUE
            )
          })
        })
        observe({
          vars <- rv$vars
          lapply(X = vars, FUN = function(x) {
            ovars <- vars[vars != x]
            observeEvent(input[[x]],
              {
                data <- rv$data
                indicator <- lapply(X = vars, FUN = function(x) {
                  data[[x]] %inT% input[[x]]
                })
                indicator <- Reduce(f = `&`, x = indicator)
                data <- data[indicator, ]
                if (all(indicator)) {
                  shinyWidgets:::toggleDisplayServer(
                    session = session, id = ns("reset_all"),
                    display = "none"
                  )
                } else {
                  shinyWidgets:::toggleDisplayServer(
                    session = session, id = ns("reset_all"),
                    display = "block"
                  )
                }
                for (i in ovars) {
                  if (is.null(input[[i]])) {
                    updateSelectizeInput(
                      session = session, inputId = i,
                      choices = sort(unique(data[[i]])), server = TRUE
                    )
                  }
                }
                if (is.null(input[[x]])) {
                  updateSelectizeInput(
                    session = session, inputId = x,
                    choices = sort(unique(data[[x]])), server = TRUE
                  )
                }
              },
              ignoreNULL = FALSE,
              ignoreInit = TRUE
            )
          })
        })
    
        observe({
          updateSelectInput(inputId = "manufacturer", choices = unique(rv$data$manufacturer), selected = "audi")
        })
    
    
        return(reactive({
          data <- rv$data
          vars <- rv$vars
          indicator <- lapply(X = vars, FUN = function(x) {
            `%inT%`(data[[x]], input[[x]])
          })
          indicator <- Reduce(f = `&`, x = indicator)
          data <- data[indicator, ]
          return(data)
        }))
      }
    
    # UI MODULE ---------------------------------------------------------------
    
    
    selectizeGroupUI_custom <-
      function(id, params, label = NULL, btn_label = "Reset filters", inline = TRUE) {
        ns <- NS(id)
        if (inline) {
          selectizeGroupTag <- tagList(
            ##### NEW LOCATION FOR THE BUTTON #####
            actionButton(
              inputId = ns("reset_all"), label = btn_label,
              style = "float: left;"
              ##### NEW LOCATION FOR THE BUTTON #####
            ),
            tags$b(label), tags$div(
              class = "btn-group-justified selectize-group",
              role = "group", `data-toggle` = "buttons", lapply(
                X = seq_along(params),
                FUN = function(x) {
                  input <- params[[x]]
                  tagSelect <- tags$div(
                    class = "btn-group",
                    id = ns(paste0("container-", input$inputId)),
                    selectizeInput(
                      inputId = ns(input$inputId),
                      label = input$title, choices = input$choices,
                      selected = input$selected, multiple = ifelse(is.null(input$multiple),
                        TRUE, input$multiple
                      ), width = "100%",
                      options = list(
                        placeholder = input$placeholder,
                        plugins = list("remove_button"), onInitialize = I("function() { this.setValue(\"\"); }")
                      )
                    )
                  )
                  return(tagSelect)
                }
              )
            )
          )
        } else {
          selectizeGroupTag <- tagList(tags$b(label), lapply(
            X = seq_along(params),
            FUN = function(x) {
              input <- params[[x]]
              tagSelect <- selectizeInput(
                inputId = ns(input$inputId),
                label = input$title, choices = input$choices,
                selected = input$selected, multiple = ifelse(is.null(input$multiple),
                  TRUE, input$multiple
                ), width = "100%", options = list(
                  placeholder = input$placeholder,
                  plugins = list("remove_button"), onInitialize = I("function() { this.setValue(\"\"); }")
                )
              )
              return(tagSelect)
            }
          ), actionLink(
            inputId = ns("reset_all"), label = btn_label,
            icon = icon("remove"), style = "float: right;"
          ))
        }
        tagList(
          singleton(tagList(tags$link(
            rel = "stylesheet", type = "text/css",
            href = "shinyWidgets/modules/styles-modules.css"
          ), shinyWidgets:::toggleDisplayUi())),
          selectizeGroupTag
        )
      }
    
    
    # APP ---------------------------------------------------------------------
    
    
    
    data("mpg", package = "ggplot2")
    
    ui <- fluidPage(
      fluidRow(
        column(
          width = 10, offset = 1,
          tags$h3("Filter data with selectize group"),
          panel(
            checkboxGroupInput(
              inputId = "vars",
              label = "Variables to use:",
              choices = c("manufacturer", "model", "trans", "class"),
              selected = c("manufacturer", "model", "trans", "class"),
              inline = TRUE
            ),
            selectizeGroupUI_custom(
              id = "my-filters",
              params = list(
                manufacturer = list(inputId = "manufacturer", title = "Manufacturer:"),
                model = list(inputId = "model", title = "Model:"),
                trans = list(inputId = "trans", title = "Trans:"),
                class = list(inputId = "class", title = "Class:")
              ), btn_label = "Show all data"
            ),
            status = "primary"
          ),
          DT::dataTableOutput(outputId = "table")
        )
      )
    )
    
    ########### SERVER###########
    
    server <- function(input, output, session) {
      vars_r <- reactive({
        input$vars
      })
    
      res_mod <- callModule(
        module = selectizeGroupServer_modified,
        id = "my-filters",
        data = mpg,
        vars = vars_r
      )
    
    
    
      output$table <- DT::renderDataTable({
        res_mod()
      })
    }
    
    
    shinyApp(ui, server)
    

    enter image description here