I'm having a surprising amount of difficulty returning a variable from a module to the server in an R Shiny app. In a module, I'd like to return a value when a button press is observed, so I've wrapped the return()
statement in a block inside observeEvent()
. However, the desired value is not returned, the entire observeEvent()
block appears to be.
I've attempted to create a minimal working example outlining the problem below:
# ui.R
fluidPage(
input_module_ui("input"),
actionButton("print_input_button",
label = "Print Input")
)
# server.R
function(input, output, session) {
# Calling input module.
input_module_return <- callModule(input_module, "input")
observeEvent(input$print_input_button, {
print(input_module_return)
})
}
# global.R
source("modules/input.R")
# input.R
input_module_ui <- function(id) {
ns <- NS(id)
tagList(
textInput(ns("text_input"),
label = h2("Input Text:")),
actionButton(ns("submit_input"),
label = "Submit Input")
)
}
input_module <- function(input, output, session) {
print("I should only print once")
observeEvent(input$submit_input, {
print("Return input")
return(input$text_input)
})
}
When testing this app, I entered "test" in the text input box and submitted my input. However, when I attempted to print my input, instead of printing "test" as I'd expect, the following was printed:
<Observer>
Public:
.autoDestroy: TRUE
.autoDestroyHandle: function ()
clone: function (deep = FALSE)
.createContext: function ()
.ctx: environment
destroy: function ()
.destroyed: FALSE
.domain: session_proxy
.execCount: 3
.func: function ()
initialize: function (observerFunc, label, suspended = FALSE, priority = 0,
.invalidateCallbacks: list
.label: observeEvent(input$submit_input)
.onDomainEnded: function ()
onInvalidate: function (callback)
.onResume: function ()
.prevId: 1896
.priority: 0
resume: function ()
run: function ()
self: Observer, R6
setAutoDestroy: function (autoDestroy)
setPriority: function (priority = 0)
suspend: function ()
.suspended: FALSE
I believe that this corresponds to the last block in input.R
:
observeEvent(input$submit_input, {
print("Return input")
return(input$text_input)
})
How can I get this app to work as intended and return input$text_input
when input$submit_input
is observed?
You were quite close to getting this to work. The trick with shiny modules is that passing variables into and out of them requires passing the variables as reactive values. I made two small changes to your code to get what I think you're hoping to see.
First was to return a reactive version of input$text_input
from the server module (rather than from the observer itself, which should just tell the app what you want to happen):
input_module <- function(input, output, session) {
print("I should only print once")
observeEvent(input$submit_input, {
print("Return input")
})
return(reactive({input$text_input}))
}
The second change is that now the output from input_module
is reactive. If you want the values rather than the function contents, you need to resolve the object using ()
. So, in your server function:
server <- function(input, output, session) {
# Calling input module.
input_module_return <- callModule(input_module, "input")
observeEvent(input$print_input_button, {
print(input_module_return())
})
}
Output:
Listening on http://127.0.0.1:6796
[1] "I should only print once"
[1] "Return input"
[1] "test"
I found the code above to error out in my situation. Thus I thought I could provide a more generalised answer.
library(shiny)
library(tidyverse)
lower_UI <- function(id) {
tagList(
uiOutput(NS(id, "text3")),
textOutput(NS(id,"verbose3")),
actionButton(NS(id,"goButton"), label = "beep me", class = "btn-success")
)
}
lower <- function(id) {
moduleServer(id, function(input, output, session) {
retV = reactiveValues(output = NULL)
output$text3 <- renderUI(textInput(session$ns("text3"), "test", "test"))
output$verbose3 <- renderText(input$text3)
observe({
retV$output = input$text3
}) %>% bindEvent(input$goButton)
return(reactive(retV$output))
})
}
upper_UI <- function(id) {
lower_UI(NS(id, "test"))
}
upper <- function(id) {
moduleServer(id, function(input, output, session) {
uRV <- reactiveValues(one = NULL)
output$one <- lower("test")
observe({
print(uRV$one())
})
})
}
ui <- fluidPage(
upper_UI("upper")
)
server <- function(input, output, session) {
upper("upper")
}
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