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