Search code examples
rshinyr6shinymodules

Shiny app modules: How to pass variables defined in the Server as arguments of functions in the UI?


i am using R6 to pass variables across and along the main server and server modules, following Jiwan Heo's approach as described in his article.

Using a simplified example (without graph to minimise code length) of the mentioned article, this works:

library(shiny)
library(dplyr)
library(ggplot2)
library(shinydashboard)


IrisR6 <- R6::R6Class(
  "IrisR6",
  public = list(
    n_rows = NULL,
    multiplier = NULL,
    orig_data = iris,
    res_data = NULL,
    manip_data = function(dat) {
      dat %>% 
        head(self$n_rows) %>% 
        mutate(Sepal.Length = Sepal.Length * self$multiplier)
    }
  )
)


mod_manip_ui <- function(id) {
  ns <- NS(id)
  
  tagList(
    numericInput(ns("n_rows"), 
                 "Number of rows to display",
                 value = 10,
                 min = 1,
                 max = 150),
    
    numericInput(ns("multiplier"), 
                 "A random calculation",
                 value = 1,
                 min = 1,
                 max = 10),
    actionButton(ns("go"), "Go!")
  )
}

mod_manip_server <- function(id, r6) {
  moduleServer(id, function(input, output, session) {
    
    observeEvent(input$go, {
      r6$n_rows <- input$n_rows
      r6$multiplier <- input$multiplier
      
      new_data <- r6$manip_data(dat = r6$orig_data)
      r6$res_data <- new_data
      
      gargoyle::trigger("update_iris")
    })
    
  })
}


mod_table_ui <- function(id, r6) {
  ns <- NS(id)
  tagList(
    textOutput(ns("text")),
    ###### This doesn't work ######
    # shinydashboard::valueBox(
    #   value = r6$n_rows,
    #   subtitle = "nr of rows"
    # ),
    ############################### 
    ###### This works: ###### 
    shinydashboard::valueBox(
      value = rnorm(1),
      subtitle = "random nr"
    ),
    ######################### 
    tableOutput(ns("table"))
  )
}

mod_table_server <- function(id, r6) {
  moduleServer(id, function(input, output, session) {
    
    observeEvent(gargoyle::watch("update_iris"), {
      
      output$text <- renderText(paste("Multiplier:", r6$multiplier))
      
      output$table <- renderTable({
        req(!is.null(r6$res_data))
        
        r6$res_data
      })
      
    })
  })
}


ui <- fluidPage(
  column(12, mod_manip_ui("mod_manip_1")),
  column(6, mod_table_ui("mod_table_1"))
)

server <- function(session, input, output) {
  
  r6 <- IrisR6$new()
  gargoyle::init("update_iris")
  
  mod_manip_server("mod_manip_1", r6 = r6)
  mod_table_server("mod_table_1", r6 = r6)
  mod_graph_server("mod_graph_1", r6 = r6)
}


shinyApp(ui, server)

enter image description here

Now if you look to mod_table_ui() and use r6$n_rows as an argument of valueBox() the code doesn't work (see the commented part inside the mod_table_ui() of the code above).

What is the best approach to pass external variables defined in the main server of server modules, as arguments of functions used inside the main or modular ui?


Solution

  • As suggested by Stéphane's comment above, using renderUI() allows to pass variables available in the server to arguments of functions used normally at the UI. So replacing mod_table_ui() and mod_table_server() from the code above with the functions below will do the job:

    mod_table_ui <- function(id) {
      ns <- NS(id)
      
      tagList(
        textOutput(ns("text")),
        uiOutput(ns('renderUIway')),
        tableOutput(ns("table"))
      )
    }
    
    
    mod_table_server <- function(id, r6) {
      moduleServer(id, function(input, output, session) {
        
        observeEvent(gargoyle::watch("update_iris"), {
          
          output$text <- renderText(paste("Multiplier:", r6$multiplier))
          
          output$table <- renderTable({
            req(!is.null(r6$res_data))
            r6$res_data
          })
          
          output$renderUIway <- renderUI({
            tagList(
              ###### This now also works! ######
              shinydashboard::valueBox(
                value = r6$n_rows,
                subtitle = "nr of rows"
              )
              ###############################
              ###### This works: ######
              # shinydashboard::valueBox(
              #   value = rnorm(1),
              #   subtitle = "random nr"
              # )
              #########################
            )
          })
        })
      })
    }
    

    enter image description here