How can I use the same inputId in multiple uiOutputs in R Shiny?

555 views Asked by At

I am writing an app that will be used to enter data from paper forms into an SQL database. The data are from wildlife capture records, and will go into a single table, so the different forms share a lot of the same fields (e.g. weight, age, species, etc), but some of the forms have different fields, or the same fields in a different order. I don't want to have unused fields visible, and I want the Shiny app to match the field order on the data sheet so the user can just tab through. So, I have a uiOutput for each species that renders based on the species initially selected from a menu. Then, within those uiOutputs, other inputs, including selectInputs where the choices are dependent on the species selected.

Since many of the fields are the same, I want to just have one dataframe at the end that uses all the inputs, regardless of what the selected species is, rather than a bear dataframe, an elk dataframe, etc. So, input$Age will be rendered multiple times rather than input$bearAge, input$elkAge each rendered once. Then, eventually, I would have a dataframe that is entrydata <- data.frame(Age = input$Age), rather than bearentrydata <- data.frame(Age = input$bearAge), elkentrydata <- data.frame(Age = input$elkAge), etc.

The problem is that shiny seems to really not like having the same input value used over and over again, and it will randomly stop recognizing new input$Age for species 1, then start recognizing it again for species 2, then work again for species 1. I put a print statement inside observeEvent(input$Age, {}) to test. I've tried to fix it with choices = someAgeVector, where someAgeVector updates inside an observeEvent(input$GameID,{}) and the same thing happens, leading me to believe it is a problem with reusing the same inputId.

Is there a way to have the same input rendered multiple times?

Here is a simplified version of the app:

library(shiny)

game <- c('', 'Black_Bear', 'Elk', 'Mule_Deer')

## age class data frame
ages <- read.table(header = T, text = "
    Age_Class     Game
    Cub           Black_Bear
    Yearling      Black_Bear
    Subadult      Black_Bear
    Adult         Black_Bear
    6mo           Elk
    Yearling      Elk
    2-9yr_old     Elk
    10-14yr_old   Elk
    >15yr_old     Elk
    Neonate       Mule_Deer
    Fawn          Mule_Deer
    Yearling      Mule_Deer
    Adult         Mule_Deer
    Unknown       Mule_Deer
")
ui <- fluidPage(
  
  selectInput(inputId = 'GameID', label = 'Game', choices = game),
  
  uiOutput(outputId = 'bearUI'),
  uiOutput(outputId = 'elkUI'),
  uiOutput(outputId = 'deerUI'),
)

server <- function(input, output, session) {
  
  observeEvent(input$Age, {
    print(input$Age)
  })
  
  output$bearUI <- renderUI({
    req(input$GameID == 'Black_Bear')
    selectInput(inputId = 'Age', label = 'Age', choices = c(ages[ages$Game == input$GameID,]$Age_Class))
  })
  
  output$elkUI <- renderUI({
    req(input$GameID == 'Elk')
    selectInput(inputId = 'Age', label = 'Age', choices = c(ages[ages$Game == input$GameID,]$Age_Class))
  })
  
  output$deerUI <- renderUI({
    req(input$GameID == 'Mule_Deer')
    selectInput(inputId = 'Age', label = 'Age', choices = c(ages[ages$Game == input$GameID,]$Age_Class))
  })
  
}

shinyApp(ui = ui, server = server)
1

There are 1 answers

0
tamarack On

Per @MrFlick : ID's must be unique on the page. This is because they are implemented as actually HTML id attributes on the nodes. Web browsers do not do well with duplicate IDs. For a better strategy, try something like shiny modules where you can "namespace" elements so you don't have conflicting IDs.