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)
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.