I am currently trying to modularize my shiny app based on this tutorial. In my actual app, I have two selectInput that allow the user to select the first and the last quarter-end date, based on which some statistics for some data set are calculated. Since the available quarter-end dates are dependent on the actual data set and there are different data sets in use, I feed the choices parameter of selectInput dynamically in the server function. The process is as follows:
selectInput objects dynamically conditional on the available quarter end-dates of the data set.I do this over and over again in my app for different data sets, which is why I now want to create a module. However, I struggle to get the selected quarter-end dates back that I could then use to limit my data set.
Below, please find a small app that illustrates my problem.
app.R part
source("module.R")
ui <- fixedPage(
selectQuartersUI("test"),
textOutput("summary")
)
server <- function(input, output, session) {
### Here I want to save the selected input
#(Use 1:10 for illustration purposes, this would actually be
#quarter end-dates that are dynamic based on the specific data set)
testValues <- callModule(selectQuarters, "test", 1:10)
### Use the selected input (here to simply output them, in actual app to limit data set)
#Using testValues returns long function call, calling testValues() returns "Error: could not find function "testValues""
output$summary <- renderText({
sprintf(paste0("Start: ", testValues(), collapse = "_"))
})
}
shinyApp(ui, server)
module.R part
selectQuartersUI <- function(id) {
ns <- NS(id)
fluidRow(
column(3, htmlOutput(ns("startQuarter"))),
column(3, htmlOutput(ns("endQuarter")))
)
}
selectQuarters <- function(input, output, session, vec_dates) {
vec_dates <- sort(unique(vec_dates))
output$startQuarter <- renderUI({
ns <- session$ns
selectInput(ns("startQuarter"), "Start:", vec_dates[1:(length(vec_dates)-1)],
multiple = FALSE,
selected = max(vec_dates[1:(length(vec_dates)-1)]))
})
output$endQuarter <- renderUI({
ns <- session$ns #See "Using renderUI within modules" part in tutorial
selectInput(ns("endQuarter"), "End:", vec_dates,
multiple = FALSE,
selected = max(vec_dates)) #3nd latest quarter
})
#See tutorial: "If a module wants to return reactive expressions to the calling app,
# then return a list of reactive expressions from the function
# Using c() instead causes the same issues
return(list(reactive({input$startQuarter}),
reactive({input$endQuarter})))
}
The part that fails is to return the selected quarter-end dates in testValues, but I'm struggling to see why.
Your code is very close to being correct.
The only thing you need to realize is that what you're returning from the module is a list, not a reactive value. Therefore, you can't access it like a reactive value. But you can access its individual elements like reactive values.
So while testValues() will result in an error, testValues[[1]]() and testValues[[2]]() will give you the two values that you want.
Shiny app code to show that it's really that simple:
ui <- fixedPage(
selectQuartersUI("test"),
textOutput("summary")
)
server <- function(input, output, session) {
testValues <- callModule(selectQuarters, "test", 1:10)
output$summary <- renderText({
sprintf(paste0("Start: ", testValues[[1]](), " end: ", testValues[[2]]()))
})
}
shinyApp(ui, server)
Even better though, you could return a named list (and access the individual elements as reactive values). Inside the module, modify the return statement to
return(list(start = reactive({input$startQuarter}),
end = reactive({input$endQuarter})))
And then the app code becomes
ui <- fixedPage(
selectQuartersUI("test"),
textOutput("summary")
)
server <- function(input, output, session) {
testValues <- callModule(selectQuarters, "test", 1:10)
output$summary <- renderText({
sprintf(paste0("Start: ", testValues$start(), " end: ", testValues$end()))
})
}
shinyApp(ui, server)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With