MorrisseyJ
MorrisseyJ

Reputation: 1271

Dynamically update selectInput, based on dynamically updated selectInput

I am trying to dynamically update a selectInput() value based on a dynamically updated selectInput() value. I can get the first value to update dynamically, but when I try and use those to update further values, I get an error:

Warning: Error in [.data.frame: undefined columns selected
  [No stack trace available]

The behavior I am looking for is:

  1. Select a variable from a dataframe
  2. Filter the dataframe for a selection of values from that variable
  3. Select another variable from the dataframe
  4. Filter the dataframe for a selection of values from that variable

Once a variable has been selected, I don't want that choice to appear as an option in subsequent selections. I want the filter options to populate based on the variable selected.

Here is a reproducible example:

library('shiny')

#create sample data
df = data.frame('first.name' = c('peter', 'paul', 'patricia'), 
                  'family.name' = c('smith', 'jones', 'gibbon'), 
                  'language' = c('english', 'french', 'spanish'))
#create the UI
ui <- fluidPage(
  
  #Create first selector for variable 1, based on column names
  selectInput(inputId = 'var1', 
              label = 'Variable 1', 
              choices = colnames(df)),
  
  #create selector for filtering, populated based on the selection for variable 1
  #leave values for label and choice blank - to be populated by updateSelectInput()
  selectInput(inputId = 'var1_subselect', 
              label = '', 
              choices = ''),
  
  #create selector for variable 2, should not include variable one
  selectInput(inputId = 'var2', 
              label = 'Variable 2', 
              choices = ''),
  
  #create selector for filtering, populated based on the selection for variable 2
  selectInput(inputId = 'var2_subselect', 
              label = '', 
              choices = '')
  
)

#Create the server
server <- function(session, input, output) {
 observe({
   updateSelectInput(session, 
                     inputId = 'var1_subselect', #for var1_subselect
                     label = paste(input$var1, 'selection:'), #Get the label from the value for var1
                     choices = unique(df[input$var1])) #Get the choices based on unique values far var1
 })
  
  observe({
    updateSelectInput(session, 
                      inputId = 'var2', #for var2
                      label = 'Variable 2', #Label is standard 
                      choices = setdiff(colnames(df), input$var1)) #Get choices from the colnames for the df, excluding the choice for var1
  })
  
  #Everything works up to this point, however when i add the following effort to filter the values for var 2, i get an error
  
  observe({
    updateSelectInput(session, 
                      inputId = 'var2_subselect', #for var2_subselect
                      label = paste(input$var2, 'selection:'), #Get the label from the value for var2
                      choices = unique(df[input$var2])) #Get the choices based on the unique values of var2
  })
  
}

#call the app
shinyApp(ui, server)

As soon as I call that third observe({}) function, I get an error. I presume this is because I am trying to call an input that is reactive based on a reactive input.

I tried to solve this using uiOutput() in my UI and renderUI({}) in my server, but ran into the same problem once i got to trying to dynamically update content, based on dynamically updated content.

Upvotes: 0

Views: 499

Answers (1)

Ronak Shah
Ronak Shah

Reputation: 389275

A small and simple fix would be to filter the data with [[ instead of [. The higher level difference between the two is explained in this post The difference between bracket [ ] and double bracket [[ ]] for accessing the elements of a list or dataframe .

Here, [[ always returns a vector whereas [ returns a dataframe which works for most of the cases as you expect but fails when there is no selection made for var2 yet meaning when var2 is empty.

[ returns

df['']

Error in [.data.frame(df, "") : undefined columns selected

this is the error message that you receive.

whereas with [[ it returns NULL.

df[['']]
#NULL

which is accepted by choices argument in selectInput.


Complete code -

library(shiny)

#create sample data
df = data.frame('first.name' = c('peter', 'paul', 'patricia'), 
                'family.name' = c('smith', 'jones', 'gibbon'), 
                'language' = c('english', 'french', 'spanish'))
#create the UI
ui <- fluidPage(
  
  #Create first selector for variable 1, based on column names
  selectInput(inputId = 'var1', 
              label = 'Variable 1', 
              choices = colnames(df)),
  
  #create selector for filtering, populated based on the selection for variable 1
  #leave values for label and choice blank - to be populated by updateSelectInput()
  selectInput(inputId = 'var1_subselect', 
              label = '', 
              choices = ''),
  
  #create selector for variable 2, should not include variable one
  selectInput(inputId = 'var2', 
              label = 'Variable 2', 
              choices = ''),
  
  #create selector for filtering, populated based on the selection for variable 2
  selectInput(inputId = 'var2_subselect', 
              label = '', 
              choices = '')
  
)

#Create the server
server <- function(session, input, output) {
  observe({
    updateSelectInput(session, 
                      inputId = 'var1_subselect', #for var1_subselect
                      label = paste(input$var1, 'selection:'), #Get the label from the value for var1
                      choices = unique(df[[input$var1]])) #Get the choices based on unique values far var1
  })
  
  observe({
    updateSelectInput(session, 
                      inputId = 'var2', #for var2
                      label = 'Variable 2', #Label is standard 
                      choices = setdiff(colnames(df), input$var1)) #Get choices from the colnames for the df, excluding the choice for var1
  })
  
  observe({
     updateSelectInput(session, 
                       inputId = 'var2_subselect', #for var2_subselect
                       label = paste(input$var2, 'selection:'), #Get the label from the value for var2
                       choices = unique(df[[input$var2]])) #Get the choices based on the unique values of var2
   })
  
}

#call the app
shinyApp(ui, server)

enter image description here

Upvotes: 2

Related Questions