Search code examples
rshinymodulenamespacesshiny-reactivity

How to reciprocally transmit reactive values between R modules?


I am trying to find a systematic, robust manner for transmitting reactive data between R modules in a two-way flow that I can use as a template going forward. How to transmit data between modules isn't clear to me yet.

In running the example R Shiny code shown at the bottom of this post, the first module mod1 multiplies the base dataframe df values by the user slider input value, and the second module mod2 adds 10 to all of the values from mod1. Then, via the observe() in the mod1_server() function, the first column of the mod2 table is transmitted back to mod1 and the added column in the table is named "colA_mod2". All works fine so far. You can run the code posted at the bottom and see how this works.

However, how do you multiply the values in "colA_mod2" by 100 and transmit those values back to mod2 and append those values as column to the mod2 table, to be labelled as "colA_mod2_mod1"? I think if I can see how this step works I can finally get my head around transmitting values between modules in both directions.

Code:

library(shiny)
library(DT)

mod1_ui <- function(id) {
  ns <- NS(id)
  DTOutput(ns("tbl"))
}

mod2_ui <- function(id) {
  ns <- NS(id)
  DTOutput(ns("tbl"))
}

mod1_server <- function(input, output, session, common) {
  new_dat <- reactive({
    df <- common$df
    svc <- common$svc()
    df * as.numeric(svc)
  })
  
  # the below transmits mod2 data back to mod1
  observe({
    common$mod1_dat <- new_dat()
    common$colA_mod2 <- common$mod2_table$A
  })
  
  output$tbl <- renderDT({
    dat <- new_dat()
    dat$colA_mod2 <- common$colA_mod2 
    dat
  })
}

mod2_server <- function(input, output, session, common) {
  
  output$tbl <- renderDT({
    mod1_dat <- common$mod1_dat
    dat <- data.frame(mod1_dat + 10)
    common$mod2_table <- dat
    dat
  })
}

ui <- fluidPage(
  mainPanel(
    h5(strong("Base table:")),
    DTOutput("table"),
    h5(strong(paste(
      "Module 1 multiplies slider input by values in above table",
      "and then tacks on column A from mod2 called `ColA_mod2´:"))),
    sliderInput("svc", "", min = 0, max = 10, value = 1),
    mod1_ui("mod1"),
    h5(strong(paste(
      "Module 2 then adds 10 to those Module 1 products [and then tacks on",
      "a new column called `ColA_mod2_mod1` which is `ColA_mod2` from mod1",
      "multipled by 100]:"))),
    mod2_ui("mod2")
  )
)

server <- function(input, output, session) {
  
  common <- reactiveValues(df = data.frame(A = 1:2, B = 3:4))
  
  output$table <- renderDT({datatable(common$df)})
  
  common$svc <- reactive(input$svc)
  
  callModule(mod1_server, "mod1", common)
  callModule(mod2_server, "mod2", common)
}

shinyApp(ui, server)

Solution

  • Try this

    library(shiny)
    library(DT)
    
    mod1_ui <- function(id) {
      ns <- NS(id)
      DTOutput(ns("tbl"))
    }
    
    mod1_server <- function(id, common) {
      moduleServer(id, function(input, output, session) {
      
        new_dat <- reactive({
          df <- common$df
          svc <- common$svc()
          df * as.numeric(svc)
        })
        
        # the below transmits mod2 data back to mod1
        observe({
          common$mod1_dat <- new_dat()
          common$colA_mod2 <- common$mod2_table$A
        })
        
        output$tbl <- renderDT({
          dat <- new_dat()
          dat$colA_mod2 <- common$colA_mod2 
          dat
        })
    
      })
    }
    
    mod2_ui <- function(id) {
      ns <- NS(id)
      DTOutput(ns("tbl"))
    }
    
    mod2_server <- function(id, common) {
      moduleServer(id, function(input, output, session) {
        output$tbl <- renderDT({
          mod1_dat <- common$mod1_dat
          dat <- data.frame(mod1_dat + 10)
          if (!is.null(common$colA_mod2)) dat$colA_mod2_mod1 <- common$colA_mod2 * 10
          common$mod2_table <- dat
          dat
        })
      })
    }
    
    ui <- fluidPage(
      mainPanel(
        h5(strong("Base table:")),
        DTOutput("table"),
        h5(strong(paste(
          "Module 1 multiplies slider input by values in above table",
          "and then tacks on column A from mod2 called `ColA_mod2´:"))),
        sliderInput("svc", "", min = 0, max = 10, value = 1),
        mod1_ui("mod1"),
        h5(strong(paste(
          "Module 2 then adds 10 to those Module 1 products [and then tacks on",
          "a new column called `ColA_mod2_mod1` which is `ColA_mod2` from mod1",
          "multipled by 100]:"))),
        mod2_ui("mod2")
      )
    )
    
    server <- function(input, output, session) {
      
      common <- reactiveValues(df = data.frame(A = 1:2, B = 3:4))
      
      output$table <- renderDT({datatable(common$df)})
      
      common$svc <- reactive(input$svc)
      
      mod1_server("mod1", common)
      mod2_server("mod2", common)
      
    }
    
    shinyApp(ui, server)
    

    output