Create dynamic number of input elements with R/Shiny
Solution 1:
You could handle generation of the UI element in server.R
, so you have something like:
ui.R
----
shinyUI( pageWithSideBar(
...
selectInput("numIndividuals", ...)
uiOutput("sliders"),
...
))
and
server.R
--------
shinyServer( function(input, output, session) {
output$sliders <- renderUI({
numIndividuals <- as.integer(input$numIndividuals)
lapply(1:numIndividuals, function(i) {
sliderInput(...)
})
})
})
When I have UI elements that depend on values from other UI elements, I find it easiest to generate them in server.R
.
It's useful to understand that all of the _Input
functions just generate HTML. When you want to generate that HTML dynamically it makes sense to move it to server.R
. And perhaps the other thing worth emphasizing is that it's okay to return a list
of HTML 'elements' in a renderUI
call.
Solution 2:
You can access dynamically named variables from shiny using this syntax:
input[["dynamically_named_element"]]
So in your example above, if you initialise your slider elements as so
# server.R
output$sliders <- renderUI({
members <- as.integer(input$members) # default 2
max_pred <- as.integer(input$max_pred) # default 5000
lapply(1:members, function(i) {
sliderInput(inputId = paste0("ind", i), label = paste("Individual", i),
min = 0, max = max_pred, value = c(0, 500), step = 100)
})
})
# ui.R
selectInput("num", "select number of inputs", choices = seq(1,10,1))
uiOutput("input_ui")
You can print the values to a table using the following
# server.R
output$table <- renderTable({
num <- as.integer(input$num)
data.frame(lapply(1:num, function(i) {
input[[paste0("ind", i)]]
}))
})
# ui.R
tableOutput("table")
See here for a working Shiny example. Working gist here.
Source: Joe Cheng's first answer, about half way down this thread
Solution 3:
You could generate the sidebar with do.call
and lapply
, something like:
# create the first input, which isn't dynamic
sel.input = selectInput(inputId = "class", label = "Choose plan type:",
list("Employee only" = "emp", "Employee and spouse" = "emp_spouse",
"Employee and child" = "emp_child", "Employee and family" = "emp_fam"))
num.individuals = 5 # determine the number of individuals here
# concatenate the select input and the other inputs
inputs = c(list(sel.input), lapply(1:num.individuals, function(i) {
sliderInput(inputId = paste0("ind", i), label = paste("Individual", i), min = 0, max = 20000, value = c(0, 2500), step = 250)
}))
sidebar.panel = do.call(sidebarPanel, inputs)