Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return input values from callModule function in shiny

Tags:

r

shiny

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:

  1. Create the selectInput objects dynamically conditional on the available quarter end-dates of the data set.
  2. Use the selected quarter-end dates to limit the specific data set accordingly.

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.

like image 887
Christoph_J Avatar asked Apr 13 '17 09:04

Christoph_J


1 Answers

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)
like image 163
DeanAttali Avatar answered Nov 12 '22 03:11

DeanAttali