zhanxw
zhanxw

Reputation: 3299

How to detect a button pressed in Shiny modules in R

I am stuck about how to know whether a button inside a Shiny module is pressed. In this simplified example below, I created a module (buttonUI, buttonServer): there is a button inside this module, and my goal is to "know" (detect) this button is pressed from outside of the module.

buttonUI <- function(id) {

  ns <- NS(id)
  tagList(actionButton(ns("btn"), "a button label")
  )}

buttonServer <- function(id, parent) {
  moduleServer(id,
               ## Below is the module function
               function(input, output, session) {
                 
                 ns <- session$ns
                 ret <- reactiveVal(0)
                 observeEvent(input$btn,{
                   message("inner", ret())
                   ret(ret()+1)
                 })
                 
                   list(n = reactive(ret))
               })
}

ui <- fluidPage(
  buttonUI("mod")
)
server <- function(input, output, session) {
  v = buttonServer("mod")
  observeEvent(v$n, {
    message("outer")
  })
}
shinyApp(ui, server)  

I expected to see many outputs of "outer" when I clicked the button, but I do not see any.

PS: I have tried to return a single reactive value (return(ret)) instead of a list (e.g., list(n = reactive(ret))). I found return(ret) will work, but do not know why it works. However, I need the module to return a list instead of a single value.

Upvotes: 3

Views: 1923

Answers (2)

Pabort
Pabort

Reputation: 348

Here I used a simple trick. As stated before, you can return a reactive value from a moduleServer and use that value to determine if the button was pressed

In my case, I used an eventReactive() so you can tie a reactive value directly to the actions related to the button

library(shiny)

buttonUI <- function(id) {
  ns <- NS(id)
  actionButton(ns("btn"), "a button label")
}

buttonServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    isPressed <- eventReactive(input$btn, {
      if(input$btn){
        "The button was pressed"
      } else {
        "The button was NOT pressed"
      }
    }, ignoreNULL = FALSE)
    return(isPressed())
  })
}

ui <- fluidPage(
  buttonUI("mod"),
  textOutput("text")
)
server <- function(input, output, session) {
  
  output$text <- renderText({
    buttonServer("mod")
  })

}

shinyApp(ui, server)  

Upvotes: 0

gdevaux
gdevaux

Reputation: 2505

There is a trick to pass values from outside to inside shiny module and from inside to outside. It consists in using reactiveValues : you initialise a reactiveValues in your server, you pass it as an argument in you server module, and it is changed inside the module AND outside the module. You can check this page for more examples.

PS: reactiveValues is a list, so you can pass as much variables as you want inside/ outstide your module(s)

buttonUI <- function(id) {
  ns <- NS(id)
  tagList(actionButton(ns("btn"), "a button label")
)}

buttonServer <- function(id, parent, rv) { #rv is an argument
  moduleServer(id,
   ## Below is the module function
   function(input, output, session) {
     
     ns <- session$ns
     ret <- reactiveVal(0)
     observeEvent(input$btn,{
       rv$btn <- input$btn #increment rv
       message("rv_inner", rv$btn)
       message("inner", ret())
       ret(ret()+1)
     })
     
     list(n = reactive(ret)) # no need to return rv
   })
}

ui <- fluidPage(
  buttonUI("mod")
)
server <- function(input, output, session) {
  
  rv <- reactiveValues(btn = NULL) # initialise reactiveValues
  
  v = buttonServer("mod", rv = rv) # pass reactiveValues as argument
  observeEvent(v$n, {
    message("outer")
  })
  observeEvent(rv$btn, { #check rv$btn value
    message("rv_outer", rv$btn)
  })
 
}
shinyApp(ui, server)  

Upvotes: 1

Related Questions