Search code examples
rshinyreactiveshinyjs

How to use reactiveValues instead of reactiveVal as a module parameter?


Working minmal example

I am trying to disable a module UI with the package shinyjs. From the main app I use a reactive paramater called enabled:


library(shiny)
library(shinyjs)

# ____________________________________________
##########
# MODULE #
##########

subUI <- function(id) {
  ns <- NS(id)
  tagList(
    useShinyjs(),
    actionButton(ns("click"), "CLICK!")
  )
}

subServer <- function(id, enabled) {
  stopifnot(is.reactive(enabled))
  moduleServer(
    id,
    function(input, output, session) {
      observe({
        if(enabled()) {
          enable("click")
        } else {
          disable("click")
        }
      })
    }
  )
}

# ____________________________________________
##############
# MAIN APP   #
##############
ui <- fluidPage(
  actionButton("toggle", "Toggle"),
  subUI("sub")
)

server <- function(input, output, session) {
  
  enabled <- reactiveVal(TRUE)
  
  
  subServer("sub", enabled)
  
  observeEvent(input$toggle, { enabled(! enabled()) })
}

shinyApp(ui, server, options = list(
  launch.browser = TRUE
))

Issue

In my real app I got a lots of reactive values to deal with and I am using reactiveValues() instead of reactiveVal() but I do not manage to adapt the above example. I have tried:


library(shiny)
library(shinyjs)

# ____________________________________________
##############
# SUB MODULE #
##############

subUI <- function(id) {
  ns <- NS(id)
  tagList(
    useShinyjs(),
    actionButton(ns("click"), "CLICK!")
  )
}

subServer <- function(id, enabled) {
  # stopifnot(is.reactive(enabled))
  
  moduleServer(
    id,
    function(input, output, session) {
      observe({
        if(enabled) {
          enable("click")
        } else {
          disable("click")
        }
      })
    }
  )
}

# ____________________________________________
##############
# MAIN APP   #
##############
ui <- fluidPage(
  actionButton("toggle", "Toggle"),
  subUI("sub")
)

server <- function(input, output, session) {
  
  r <- reactiveValues()
  r$enabled <- TRUE
  
  subServer("sub", r$enabled)
  
  observeEvent(input$toggle, { r$enabled <- ! r$enabled })
}

shinyApp(ui, server, options = list(
  launch.browser = TRUE
))

NB: I had to comment out this line # stopifnot(is.reactive(enabled)) in the submodule server which raises an error I cannot understand:


Error in r$enabled : 
  Can't access reactive value 'enabled' outside of reactive consumer.

I think it was the purpose of is.reactive() to deal with reactive value...


Solution

  • Here are two example approaches. The first one, simply wraps the reactiveValue in a reactive() so it is closely aligned with your original working design. The second one, passes the entire reactiveValues object and does relevant test, and uses it in place, perhaps its what you were searching for, unclear which you would prefer.

    1)

    library(shiny)
    library(shinyjs)
    
    # ____________________________________________
    ##############
    # SUB MODULE #
    ##############
    
    subUI <- function(id) {
      ns <- NS(id)
      tagList(
        useShinyjs(),
        actionButton(ns("click"), "CLICK!")
      )
    }
    
    subServer <- function(id, enabled) {
      stopifnot(is.reactive(enabled))
      moduleServer(
        id,
        function(input, output, session) {
          observe({
            if(enabled()) {
              enable("click")
            } else {
              disable("click")
            }
          })
        }
      )
    }
    
    # ____________________________________________
    ##############
    # MAIN APP   #
    ##############
    ui <- fluidPage(
      actionButton("toggle", "Toggle"),
      subUI("sub")
    )
    
    server <- function(input, output, session) {
      
      r <- reactiveValues()
      r$enabled <- TRUE
      
      
      subServer("sub", reactive(r$enabled))
      
      observeEvent(input$toggle, {
        r$enabled <- !r$enabled
        })
    }
    
    shinyApp(ui, server, options = list(
      launch.browser = TRUE
    ))
    

    2)

    library(shiny)
    library(shinyjs)
    
    # ____________________________________________
    ##############
    # SUB MODULE #
    ##############
    
    subUI <- function(id) {
      ns <- NS(id)
      tagList(
        useShinyjs(),
        actionButton(ns("click"), "CLICK!")
      )
    }
    
    subServer <- function(id, enabled_container) {
      stopifnot(is.reactivevalues(enabled_container))
    
      
      moduleServer(
        id,
        function(input, output, session) {
          observe({
            if(enabled_container$enabled) {
              enable("click")
            } else {
              disable("click")
            }
          })
        }
      )
    }
    
    # ____________________________________________
    ##############
    # MAIN APP   #
    ##############
    ui <- fluidPage(
      actionButton("toggle", "Toggle"),
      subUI("sub")
    )
    
    server <- function(input, output, session) {
      
      r <- reactiveValues()
      r$enabled <- TRUE
      
      
      subServer("sub", r )
      
      observeEvent(input$toggle, {
        r$enabled <- !r$enabled
      })
    }
    
    shinyApp(ui, server, options = list(
      launch.browser = TRUE
    ))