0

I have been reading and watching videos on Shiny Modules. I am trying to implement it one of my apps and am running into some trouble. I have extensively look at several questions here and examples but I am still unable to fix the error.

What I am trying to achieve is to dynamically add filters. I read in a dataframe when I load the app. I then want to add/remove filters dynamically using an "Add-Filter" button, which brings up a selectInput box that allows the user to choose any variable in the dataframe. Once this is chosen, I want the app to open a checkbox UI where the checkboxes are factor levels of that variable. I am able to add the selectInput box but am having trouble with the dynamic checkboxes. I have borrowed a lot of code from one of Jonas Hagenberg's examples.

Below is a reprex:

library(shiny)
library(ggplot2)

`%nin%` <- Negate(`%in%`)

cell_type <- sample( 
  c("BCell", "TCell", "Marcophage", "Monocyte"),
  100, replace = TRUE) %>% as.factor
sex <- sample( 
  c("Male", "Female"),
  100, replace = TRUE) %>% as.factor
disease <- sample( 
  c("adenocarcinoma", "copd", "nsclc", "sclc"),
  100, replace = TRUE) %>% as.factor
tumor <- sample( 
  c("tumor", "normal", "early"),
  100, replace = TRUE) %>% as.factor
exp = sample(
  c(1:2000), replace = FALSE
)

df <- data.frame( cell_type, sex, disease, tumor, exp )

#var_choices <- setdiff(names(df), "exp") %>% as.list
#names(var_choices) = var_choices
varChoices <- setdiff(names(df), "exp")

var_ui <- function(id) {
  ns <- NS(id)
  # Update var_choices by removing existing selections
  var_choices <- varChoices
  div(
    id = id,
    selectInput(
      inputId = ns("var_choice"),
      label = "variable to subset",
      choices = c(var_choices)
    ),
    uiOutput(
      outputId = ns("selected_var")
    )
  )
}

var_server <- function(id, df) {
  moduleServer(
    id,
    function(input, output, session) {
      #browser()
      vals <- reactive({ levels( df[[ input$var_choice ]] ) })
      output$selected_var <- renderUI({
        ns <- session$ns
        # update based on selected_var
        # vals = c("BCell", "TCell", "Monocyte", "Macrophage")
        checkboxGroupInput(
          inputId = ns("val_choice"),
          label = "Select which cells to show", 
          inline = TRUE,
          choices = vals, 
          selected = vals
        )
        #return(reactive({input$var_choice}))
      })
    }
  )
}


ui <- fluidPage(
  h5(""),
  actionButton(
    inputId = "add_module",
    label = "Add a module"
  ),
  actionButton(
    inputId = "remove_module",
    label = "Remove a module"
  )
)


server <- function(input, output, session) {
  
  active_modules <- reactiveVal(value = NULL)
  
  observeEvent(input$add_module, {
    # update the list of currently shown modules
    current_id <- paste0("id_", input$add_module)
    active_modules(c(current_id, active_modules()))
    
    var_server(
      id = current_id,
      df = df
    )
    
    insertUI(
      selector = "h5",
      where = "beforeEnd",
      ui = var_ui(id = current_id)
    )
  })
  
  observeEvent(input$remove_module, {
    
    # only remove a module if there is at least one module shown
    if (length(active_modules()) > 0) {
      current_id <- active_modules()[1]
      removeUI(
        selector = paste0("#", current_id)
      )
      
      # update the list of currently shown modules
      active_modules(active_modules()[-1])
    }
  })
}


shinyApp(ui, server)

What am I missing ?

Thanks -JJ

I get the selectInputUI added dynamically, but the checkboxUI is not created dynamically.

JaJ
  • 25
  • 3
  • You need a `ns` in `checkboxGroupInput(inputId = ns("val_choice"),...)` and `df` in `var_server(id = current_id, df)` – YBS Apr 27 '23 at 01:29
  • Lastly, remove the line `return(reactive({input$var_choice}))` – YBS Apr 27 '23 at 01:41
  • Thanks YBS. I added your suggestions and sill get the same error: cannot coerce type 'closure' to vector of type 'character' – JaJ Apr 27 '23 at 02:06

1 Answers1

0

Try this

library(shiny)
library(ggplot2)
library(tidyverse)

`%nin%` <- Negate(`%in%`)

cell_type <- sample( 
  c("BCell", "TCell", "Marcophage", "Monocyte"),
  100, replace = TRUE) %>% as.factor
sex <- sample( 
  c("Male", "Female"),
  100, replace = TRUE) %>% as.factor
disease <- sample( 
  c("adenocarcinoma", "copd", "nsclc", "sclc"),
  100, replace = TRUE) %>% as.factor
tumor <- sample( 
  c("tumor", "normal", "early"),
  100, replace = TRUE) %>% as.factor
exp = sample(
  c(1:2000), replace = FALSE
)

df <- data.frame( cell_type, sex, disease, tumor, exp )

#var_choices <- setdiff(names(df), "exp") %>% as.list
#names(var_choices) = var_choices
varChoices <- setdiff(names(df), "exp")

var_ui <- function(id) {
  ns <- NS(id)
  # Update var_choices by removing existing selections
  #var_choices <- varChoices
  div(
    id = id,
    selectInput(
      inputId = ns("var_choice"),
      label = "variable to subset",
      choices = varChoices
    ),
    uiOutput(
      outputId = ns("selected_var")
    )
  )
}

var_server <- function(id, df) {
  moduleServer(
    id,
    function(input, output, session) {
      ns <- session$ns
      vals <- reactive({ df[[ input$var_choice ]] %>% levels() })
      
      output$selected_var <- renderUI({
        # print(input$var_choice)
        # update based on selected_var
        
        checkboxGroupInput(
          inputId = ns("val_choice"),
          label = "Select which cells to show", 
          inline = TRUE,
          choices = vals(), 
          selected = vals()
        )
        #return(reactive({input$var_choice}))
      })
    }
  )
}


ui <- fluidPage(
  actionButton(
    inputId = "add_module",
    label = "Add a module"
  ),
  actionButton(
    inputId = "remove_module",
    label = "Remove a module"
  ),
  div(
    id = "add_here"
  )
)


server <- function(input, output, session) {
  
  active_modules <- reactiveVal(value = NULL)
  
  observeEvent(input$add_module, {
    # update the list of currently shown modules
    current_id <- paste0("id_", input$add_module)
    active_modules(c(current_id, active_modules()))
    
    var_server(id = current_id, df)
    
    insertUI(
      selector = "#add_here",
      where = "beforeEnd",
      ui = var_ui(id = current_id)
    )
  })
  
  observeEvent(input$remove_module, {
    
    # only remove a module if there is at least one module shown
    if (length(active_modules()) > 0) {
      current_id <- active_modules()[1]
      removeUI(
        selector = paste0("#", current_id)
      )
      
      # update the list of currently shown modules
      active_modules(active_modules()[-1])
    }
  })
}


shinyApp(ui, server)
YBS
  • 19,324
  • 2
  • 9
  • 27