rshinymoduleshinymodules

R Shiny Modules - module return multiple reactiveVals to main server


I have problem with communication between main server and server module. I would like to return from module current reactiveVals and then use them in main app. I created basic app which shows my problem. Module contains 2 buttons and 2 reactiveVals, each of them contains the number of clicks on the corresponding button. in the main server, I would like to know which value has changed and react to this change with some action (in the example, a change in the returned text). unfortunately, the code below returns values from the module only at the time of its launch, then, if there are changes inside the module that we are interested in, I am not informed about it.

How should I modify the code below to get the effect I am interested in?

library(shiny)

moduleUI <- function(id){
  ns <- NS(id)
  wellPanel(
    fluidRow(
      column(3, uiOutput(ns("button1"))),
      column(3, uiOutput(ns("button2")))
    )
  )
}

moduleServer <- function(input, output, session){
  rv_button1 <- reactiveVal(0)
  rv_button2 <- reactiveVal(0)
  
  observeEvent(input$button1, {
    rv_button1(rv_button1()+1)
    print(rv_button1())
  })
  observeEvent(input$button2, {
    rv_button2(rv_button2()+1)
    print(rv_button2())
    
  })
  
  output$button1 <- renderUI({
    actionButton("button1", label = "button 1")
  })
  output$button2 <- renderUI({
    actionButton("button2", label = "button 2")
  })
  
  return(
    list(
      button1 = rv_button1,
      button2 = rv_button2
    )
  )
}

ui <- fluidPage(
  moduleUI("moduleId"),
  fluidRow(uiOutput("text"))
)

server <- function(input, output, session) {
  rv_module <- reactiveVal(
    list(
      button1 = 0, 
      button2 = 0
    )
  )
  rv_text <- reactiveVal("")
  
  rv_module <- callModule(moduleServer, "moduleId")

  observeEvent(rv_module$button1, {
    rv_text("Button 1 was clicked")
  })
  observeEvent(rv_module$button2, {
    rv_text("Button 2 was clicked")
  })
  
  output$text <- renderUI({
    HTML(rv_text())
  })
}

runApp(shinyApp(ui, server))

Solution

  • Few things first :

    Then, to communicate between server and module, or between several module, the easiest way is to pass a reactiveValues as a parameter of your module.

    library(shiny)
    
    moduleUI <- function(id){
      ns <- NS(id)
        column(3, uiOutput(ns("uibutton1"))),
        column(3, uiOutput(ns("uibutton2")))
    }
    
    moduleServer <- function(input, output, session, rv){
      ns <- session$ns # don't forget ns in server side module
      observeEvent(input$button1, {
        rv$btn1 <- rv$btn1 +1
      })
      observeEvent(input$button2, {
        rv$btn2 <- rv$btn2 +1
      })
      
      # do not use the same outputId 
      output$uibutton1 <- renderUI({
        actionButton(ns("button1"), label = "button 1") # use ns()
      })
      output$uibutton2 <- renderUI({
        actionButton(ns("button2"), label = "button 2")
      })
    }
    
    ui <- fluidPage(
      moduleUI("moduleId"),
      uiOutput("text")
    )
    
    server <- function(input, output, session) {
      # Initialise a reactiveValues
      rv_module <- reactiveValues(btn1 = 0, btn2 = 0)
    
      callModule(moduleServer, "moduleId", rv = rv_module)
      
      output$text <- renderUI({
        paste("btn1 : ",rv_module$btn1, " | btn2 : ", rv_module$btn2)
      })
    }
    
    runApp(shinyApp(ui, server))