I have a module in which I create a list of inputs based on a reactive value: if this value is 3, then 3 textInput
s are created, etc. Until now, when I needed to return inputs/values from a module, I listed all of the inputs/values in return()
at the end of the server
part of my module (see this article for more details).
But in this situation, since the number of inputs is not constant, I can't list all of the inputs in return()
. Therefore, I would like to create a reactive list that I could return.
Consider the example below:
library(shiny)
some_things <- c("drat", "mpg", "cyl")
numtextinputs <- length(some_things)
some_UI <- function(id) {
ns <- NS(id)
tagList(
fluidRow(
column(
12,
lapply(1:numtextinputs, function(x) {
textInput(ns(some_things[x]),
label = some_things[x],
value = some_things[x])
}),
actionButton(ns("change"), "change inputs")
)
)
)
}
some_server <- function(input, output, session) {
observeEvent(input$change, {
test <- list()
char_numtextinputs <- as.character(numtextinputs)
for (x in char_numtextinputs) {
local({
test[[x]] <- input[[some_things[x]]]
})
}
return(reactive({ test }) )
})
}
ui <- fluidPage(
actionButton("create_ui", "create ui"),
uiOutput("ui"),
verbatimTextOutput("display_changes")
)
server <- function(input, output, session) {
observeEvent(input$create_ui, {
output$ui <- renderUI({
some_UI("1")
})
list_of_variables <- callModule(some_server, "1")
})
output$display_changes <- renderText({
if (exists("list_of_variables")){
list_of_variables$test()
}
else {
"list_of_variables$test does not exist"
}
})
}
shinyApp(ui, server)
In this example, clicking on "create ui" generates a list of textInput
s. Note that here, the number of textInput
s created is constant (I wanted to have a simple example, adding some reactivity in the number of inputs to be created is not very useful).
Expected result: after having generated the list of textInput
s, the user can modify the values in those and click on "change inputs" when he/she has finished. The module should then return the values in the textInput
s as a vector. The verbatimTextOutput
is just here to verify that the values are well returned. Note that the answer should work with any number of textInput
s (this is why I tried lists and loop).
Also asked on Rstudio Community
UPDATE: This app will create 3 textInputs in a UI module and return a vector of the textInput values extracted using a server module and display in the UI. Some of this original example has been slightly modified or removed to highlight the answer of returning a vector of values from inputs created in a module.
library(shiny)
library(tidyverse)
some_things <- c("drat", "mpg", "cyl")
numtextinputs <- length(some_things)
some_UI <- function(id) {
ns <- NS(id)
tagList(
fluidRow(
column(
12,
map(some_things, ~textInput(ns(.x), label = .x, value = .x ))
)
)
)
}
some_server <- function(input, output, session) {
#extract values of all the text boxes without explicitly naming them
#by using the ids we used to create the textInputs
test <- reactive({
map_chr(some_things, ~input[[.x]])
})
#return the vector of values from module *note no () are needed after test
# i.e. return(test) NOT return(test())
return(test)
}
ui <- fluidPage(
some_UI("1"),
verbatimTextOutput("display_changes")
)
server <- function(input, output, session) {
list_of_variables <- callModule(some_server, "1")
output$display_changes <- renderText({
list_of_variables()
})
}
shinyApp(ui, server)