Rishi
Rishi

Reputation: 65

Using variable value from one Shiny module in another module

I have three modules:

The choice for textInput and renderUI in the second and third module is deliberate. The code works without the third module, but throws the following error when the third module is included: Error in $: object of type 'closure' is not subsettable. Below is the minimal example code. Help would be much appreciated!

first_module.R

#Define ui
first_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(numericInput(
    inputId = ns("first_input"),
    label = "First input:",
    value = 1
  ))
}

#Define server logic
first_module_server <- function(input, output, session) {
  return(input)
}

second_module.R

#Define ui
second_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(uiOutput(outputId = ns("second_input")))
}

#Define server logic
second_module_server <- function(input, output, session, first_module_res) {
    ns <- session$ns
    
    observe({
      second_input <- first_module_res$first_input + 1
      output$second_input <- renderUI({
        disabled(textInput(
          inputId = ns("second_input"),
          label = "Second input:",
          value = second_input
        ))
      })
    })
    return(reactive({second_input}))
  }

third_module.R

#Define ui
third_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(uiOutput(outputId = ns("third_input")))
}

#Define server logic
third_module_server <- function(input, output, session, second_module_res) {
    ns <- session$ns
    
    observe({
      third_input <- second_module_res$second_input + 1
      output$third_input <- renderUI({
        disabled(textInput(
          inputId = ns("third_input"),
          label = "Third input:",
          value = third_input
        ))
      })
    })
  }

app.R

library(shiny)
library(shinyjs)

# Define UI
ui <- fluidPage(
    
    useShinyjs(),

    # Application title
    titlePanel("Demo"),

    # Sidebar 
    sidebarLayout(
        sidebarPanel(
            first_module_ui("first")
        ),

        mainPanel(
            second_module_ui("second"),
            third_module_ui("third")
        )
    )
)

# Define server logic 
server <- function(input, output, session) {
    
    callModule(first_module_server, "first")
    first_module_res <- callModule(first_module_server, "first")
    
    callModule(second_module_server, "second", first_module_res)
    second_module_res <- callModule(second_module_server, "second", first_module_res)
    
    callModule(third_module_server, "third", second_module_res)
    
}

# Run the application 
shinyApp(ui = ui, server = server)

Upvotes: 2

Views: 2896

Answers (1)

starja
starja

Reputation: 10375

Your code has some issues:

  • you don't need observe, you can use reactive instead because your interest is in the return value (see here)
  • you should make the calculated values to reactives
  • you don't need to call a module twice

Your code didn't work because of how you return the value from the module server function. From the first module, you return the complete input This allows you in the second module to access the values from the input of the first module as you would access it without any modules in the server function from the main app. This means that you don't need brackets to evaluate the reactive, you can just do first_module_res$first_input as you would do with input$first_input.

However, the second module doesn't return input, but a reactive created by you (via reactive({}) in the return value). This now becomes the value that is inputted into the third module and needs to be evaluated there with brackets: second_module_res(). Also note that you directly evaluate the reactive, because it is the only returned value (and not the complete input of the second module).

library(shiny)
library(shinyjs)

#Define ui
first_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(numericInput(
    inputId = ns("first_input"),
    label = "First input:",
    value = 1
  ))
}

#Define server logic
first_module_server <- function(input, output, session) {
  return(input)
}

#Define ui
second_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(uiOutput(outputId = ns("second_input")))
}

#Define server logic
second_module_server <- function(input, output, session, first_module_res) {
  ns <- session$ns
  
  second_input <- reactive({
    first_module_res$first_input + 1
  })
  
    output$second_input <- renderUI({
      disabled(textInput(
        inputId = ns("second_input"),
        label = "Second input:",
        value = second_input()
      ))
    })
  return(reactive({second_input()}))
}

#Define ui
third_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(uiOutput(outputId = ns("third_input")))
}

#Define server logic
third_module_server <- function(input, output, session, second_module_res) {
  ns <- session$ns
  
  third_input <- reactive({
    second_module_res() + 1
  })
  
    output$third_input <- renderUI({
      disabled(textInput(
        inputId = ns("third_input"),
        label = "Third input:",
        value = third_input()
      ))
    })
}

# Define UI
ui <- fluidPage(
  
  useShinyjs(),
  
  # Application title
  titlePanel("Demo"),
  
  # Sidebar 
  sidebarLayout(
    sidebarPanel(
      first_module_ui("first")
    ),
    
    mainPanel(
      second_module_ui("second"),
      third_module_ui("third")
    )
  )
)

# Define server logic 
server <- function(input, output, session) {
  
  first_module_res <- callModule(first_module_server, "first")
  
  second_module_res <- callModule(second_module_server, "second", first_module_res)
  
  callModule(third_module_server, "third", second_module_res)
  
}

# Run the application 
shinyApp(ui = ui, server = server)

Edit

You can return a list from a module with several reactives:

library(shiny)
library(shinyjs)

#Define ui
first_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(numericInput(
    inputId = ns("first_input"),
    label = "First input:",
    value = 1
  ))
}

#Define server logic
first_module_server <- function(input, output, session) {
  return(input)
}

#Define ui
second_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(uiOutput(outputId = ns("second_input")),
          numericInput(
            inputId = ns("additional_input"),
            label = "Additional input",
            value = 5
          ))
}

#Define server logic
second_module_server <- function(input, output, session, first_module_res) {
  ns <- session$ns
  
  second_input <- reactive({
    first_module_res$first_input + 1
  })
  
  output$second_input <- renderUI({
    disabled(textInput(
      inputId = ns("second_input"),
      label = "Second input:",
      value = second_input()
    ))
  })
  return(list(
    second_input = reactive({second_input()}),
    additional_input = reactive({input$additional_input})
  ))
}

#Define ui
third_module_ui <- function(id) {
  ns <- NS(id)
  
  tagList(uiOutput(outputId = ns("third_input")),
          verbatimTextOutput(outputId = ns("fourth_output")))
}

#Define server logic
third_module_server <- function(input, output, session, second_module_res) {
  ns <- session$ns
  
  third_input <- reactive({
    second_module_res$second_input() + 1
  })
  
  output$third_input <- renderUI({
    disabled(textInput(
      inputId = ns("third_input"),
      label = "Third input:",
      value = third_input()
    ))
  })
  
  output$fourth_output <- renderPrint({
    second_module_res$additional_input()
  })
}

# Define UI
ui <- fluidPage(
  
  useShinyjs(),
  
  # Application title
  titlePanel("Demo"),
  
  # Sidebar 
  sidebarLayout(
    sidebarPanel(
      first_module_ui("first")
    ),
    
    mainPanel(
      second_module_ui("second"),
      third_module_ui("third")
    )
  )
)

# Define server logic 
server <- function(input, output, session) {
  
  first_module_res <- callModule(first_module_server, "first")
  
  second_module_res <- callModule(second_module_server, "second", first_module_res)
  
  callModule(third_module_server, "third", second_module_res)
  
}

# Run the application 
shinyApp(ui = ui, server = server)

Upvotes: 4

Related Questions