Search code examples
rshiny

Generating new Pickers without clearing input in Shiny


I have a Shiny app where n pickerInputs are generated by the user. If the user first generates the Inputs and then makes the Choices, it works fine. However, if the user generates an Input, makes a Choice and then decides to generate another Input, the Choices are restored.

One workaround for this is to extend the function generate_pickers_fun with selected. When there is a change in Picker selection the function gets the Selections when creating the picker. I have posted this as an solution.

The issue is though that everytime one new Pickeris selected to be generated, all the previous Pickers are reran. Is there a solution to only update instead of rerunning?

library(shiny)
library(shinyWidgets)
library(tidyverse)

generate_pickers_fun <- function(picker_name, id){
  
  picker <-   pickerInput(
    inputId = NS(id, picker_name),
    label = picker_name,
    multiple = TRUE,
    choices = LETTERS[1:5]
  )
  
  return(picker)
  
}

ui_mod <- function(id) {
  ns <- NS(id)
  tagList(
    
    pickerInput(NS(id, "generate_pickers"), choices = c("Picker_1", "Picker_2", "Picker_3", "Picker_4"), multiple = TRUE),
    uiOutput(NS(id, "new_pickers"))
  )
}

server_mod <- function(id) {
  moduleServer(id, function(input, output, session) {
    
    reactive_values <- reactiveValues()
    
    observeEvent(input$generate_pickers, {
      
      req(input$generate_pickers)
        
      
      new_pickers <- map(input$generate_pickers, generate_pickers_fun, id = id)
      output$new_pickers <- renderUI(tagList(new_pickers))
      
    }, ignoreNULL = FALSE)
    
  })
}

app <- function() {
  ui <- fluidPage(
    ui_mod("test")
  )
  server <- function(input, output, session) {
    server_mod("test")
  }
  shinyApp(ui, server)  
}

app()

Solution

  • You could rely on conditionalPanels instead of renderUI and only show / hide the pickerInputs when needed - so each input is rendered only once:

    library(shiny)
    library(shinyWidgets)
    library(tidyverse)
    
    generate_pickers_fun <- function(picker_name, id){
      ns <- NS(id)
      conditionalPanel(sprintf("input['generate_pickers'].includes('%s')", picker_name),
                       pickerInput(
                         inputId = ns(picker_name),
                         label = picker_name,
                         multiple = TRUE,
                         choices = LETTERS[1:5]
                       ), ns = ns)
    }
    
    ui_mod <- function(id) {
      ns <- NS(id)
      picker_ids <- c("Picker_1", "Picker_2", "Picker_3", "Picker_4")
      tagList(
        pickerInput(NS(id, "generate_pickers"), choices = picker_ids, multiple = TRUE),
        map(picker_ids, generate_pickers_fun, id = id)
      )
    }
    
    server_mod <- function(id) {
      moduleServer(id, function(input, output, session) {})
    }
    
    app <- function() {
      ui <- fluidPage(
        ui_mod("test")
      )
      server <- function(input, output, session) {
        server_mod("test")
      }
      shinyApp(ui, server)  
    }
    
    app()