rshinyshinymodules

Issues with Rendering Shiny Modules Across Different Tabs


I'm currently working on a Shiny application that utilizes modules to create tables in different tabs. My intent is to render different datasets depending on the selected tab using one module to manage the DTOutput. However, I'm encountering some challenges in dynamically calling and rendering the module based on the selected tab. Below is a simplified example:

library(shiny)
library(DT)

tableModuleUI <- function(id) {
  ns <- NS(id)
  DTOutput(ns("table"))
}

tableModule <- function(input, output, session, data) {
  output$table <- renderDT({
    data()
  })
}

ui <- fluidPage(
  tabsetPanel(
    id = "tabModeTransport",
    tabPanel("Mode A", tableModuleUI("modeA")),
    tabPanel("Mode B", tableModuleUI("modeB"))
  )
)

server <- function(input, output, session) {

  datasets <- list(
    modeA = mtcars,
    modeB = iris
  )

  observeEvent(input$tabModeTransport, {
    if (input$tabModeTransport == "modeA") {
      callModule(tableModule, "modeA", reactive({datasets$modeA}))
    }
    else if (input$tabModeTransport == "modeB") {
      callModule(tableModule, "modeB", reactive({datasets$modeB}))
    }
  })
}

shinyApp(ui = ui, server = server)

While the above code works in a basic scenario, it seems inefficient, especially when scaling with more tabs and modules, as it requires explicit conditional logic for each tab. I was expecting something more dynamic, where I can render the module for any selected tab without requiring explicit condition checking for each tab.

Here are my questions:

Is there a more efficient approach to manage module calling and rendering for different tabs?

Is it possible to dynamically choose which dataset/module to render based on the active tab without employing multiple conditional checks?

I appreciate any advice or insights into potential solutions or best practices!


Solution

  • You have to use the argument value in tabPanel. Here is the app including this correction as well as the code of my comment, to avoid the if/else:

    library(shiny)
    library(DT)
    
    tableModuleUI <- function(id) {
      ns <- NS(id)
      DTOutput(ns("table"))
    }
    
    tableModule <- function(input, output, session, data) {
      output$table <- renderDT({
        data()
      })
    }
    
    ui <- fluidPage(
      tabsetPanel(
        id = "tabModeTransport",
        tabPanel("Mode A", tableModuleUI("modeA"), value = "modeA"),
        tabPanel("Mode B", tableModuleUI("modeB"), value = "modeB")
      )
    )
    
    server <- function(input, output, session) {
      
      datasets <- list(
        modeA = mtcars,
        modeB = iris
      )
      
      observeEvent(input$tabModeTransport, {
        mode <- input$tabModeTransport
        callModule(tableModule, mode, reactive(datasets[[mode]]))
      })
    }
    
    shinyApp(ui = ui, server = server)