Shiny: Error when dynamically generating numeric inputs using lists

160 Views Asked by At

I am trying to dynamically generate user inputs based on multiple predefined lists, my goal is to ease code maintainability by allowing it to conform to predefined data structures. Below is the code and the explanation for a simplified version of the problem I am having:

library(shiny)

distributions <- list(
  "Distribution 1" = list("Distribution 1 Parameter 1" = list("Lower" = 0, "Upper" = Inf, "Default" = 1))
)

models <- list(
  "General Model 1" = list("Specific Model 1")
)

parameters <- list(
  "Specific Model 1" = list("Specific Model 1 Parameter 1")
)

ui <- fluidPage(
  # Model selection 
  fluidRow(
    selectInput("generalModel", "General Model", names(models)),
    selectInput("specificModel", "Specific Model", models[[1]]),
    tabsetPanel(
      # Tab for inputs
      tabPanel(
        title = "Inputs",
        uiOutput("inputsTabs")
      )
    )
  )
)  

server <- function(input, output, session) {
  # Handling model selection
  observe({
    updateSelectInput(session, "specificModel", choices = models[[input$generalModel]])
  })

  # Displaying inputs for a specific model in tabs
  output$inputsTabs <- renderUI({
    nTabs = length(parameters[[input$specificModel]])
    tabs = lapply(seq_len(nTabs), function(i) {
      tabPanel(
        paste0(parameters[[input$specificModel]][[i]]),
        uiOutput(paste0(input$specificModel, "Input", parameters[[input$specificModel]][[i]]))
      )
    })
    do.call(tabsetPanel, tabs)
  })

  # Creating inputs for each specific parameter
  observe(
    lapply(seq_len(length(parameters[[input$specificModel]])), function(i) {
      output[[paste0(input$specificModel, "Input", parameters[[input$specificModel]][[i]])]] <- renderUI({
        distribution <- paste0(input$specificModel, "Input", parameters[[input$specificModel]][[i]], "Distribution")
        selectInput(inputId = distribution, label = "Distribution",  choices = names(distributions))
        uiOutput(paste0(distribution, "Parameters"))
      })
    }), 
    priority = 100
  )

  # Creating inputs for each specific parameter
  observe(
    lapply(seq_len(length(parameters[[input$specificModel]])), function(i) {
      distribution <- paste0(input$specificModel, "Input", parameters[[input$specificModel]][[i]], "Distribution")
      output[[paste0(distribution, "Parameters")]] <- renderUI({
        nNumericInputs = length(distributions[[input[[distribution]]]])
        numericInputs = lapply(seq_len(nNumericInputs), function(i) {
          numericInput(
            inputId = paste0(distribution, input[[input[[distribution]]]], distributions[[input[[distribution]]]][[i]]),
            label = paste0(distributions[[input[[distribution]]]][[i]]),
            value = distributions[[input[[distribution]]]][[i]][[3]],
            max = distributions[[input[[distribution]]]][[i]][[2]],
            min = distributions[[input[[distribution]]]][[i]][[1]]
          )
        })
        do.call(wellPanel, numericInputs)
      })
    }),
    priority = 50
  )

}

shinyApp(ui = ui, server = server)

Image of the app

All the user inputs described below are generated based on the parameters, models, and distributions lists defined before the UI in the shiny app above.

So the user starts by selecting a general model followed by a specific model from the two drop down menus respectively. Then based on whatever the specific model is, the Inputs tab is filled with tabs corresponding to the parameters of the chosen model. The user can then choose the distribution for each parameter from a drop down menu inside the tab.

After that the user should be able to populate a series of numeric inputs corresponding to the distribution parameters. I tried to dynamically generate those numeric inputs but I keep getting:

Error: attempt to select less than one element in get1index

The code of my attempt is at the end of the app and is commented out, uncomment it to reproduce the error.

0

There are 0 best solutions below