Search code examples
shinynestedshinymodulesbs4dash

Shiny output elements not displaying in bs4Dash with nested modules


EDIT: The author of the bs4Dash package, David Granjon, recently provided an answer to the question asked in the Github issue referenced below and closed it.

  1. My question is very likely related to this issue in bs4Dash github repo, but no answer was provided there.
  2. The full reproducible codes are at the end of the question

My goal

I am making a modularized Shiny application and am attempting to do it with the bs4Dash package. This is what the application looks like:

Picture 1

The app

The end application has several sections (I only made the Introduction for this example) and each section contains at least one bs4TabCard. The tabcard in the picture above has one uiOutput and one rhandsontableOutput element, which are rendered in the server function. Note that these are both ***Output elements. In the reproducible code for Picture 1 (which you can find at the end of the question), I do not use any module. However, my goal is to use several modules because the application has the potential to become quite large. For this simple example, I try to use two modules: one module for each section (i.e. each bs4TabItem) and one module for each tabcard. This means that the two modules will invariably be nested: the tabcard module will be inside the section module.

Picture 2

Show modules in the app

The issue

The issue is that when I implement the modules, the ***Output elements are not displayed:

Picture 3

Modularized app with issue

The surprising thing is that ***Input elements are displayed. I made a third module containing a numericInput only and placed it in the second tab of the tabcard. The picture below shows that the numericInput is displayed with no problem:

Picture 4

Modularized app works with input elements

I did my homework

In this issue, a similar problem is reported, but there has not been any solution offered and my digging around proved unsuccessful. It seems that there is a problem when an output element is placed deep inside several nested containers in bs4Dash.

The reproducible code

Reproducible code for picture 1

library(shiny)
library(bs4Dash)
library(rhandsontable)

shiny::shinyApp(
  ui = bs4DashPage(
    old_school = FALSE,
    sidebar_min = TRUE,
    sidebar_collapsed = FALSE,
    controlbar_collapsed = FALSE,
    controlbar_overlay = TRUE,
    title = "Basic Dashboard",
    navbar = bs4DashNavbar(),
    sidebar = bs4DashSidebar(

      sidebarMenu(
        bs4Dash::menuItem(
          text = "Introduction",
          tabName = "tab-introduction",
          icon = ""
        )
      )

    ),
    controlbar = bs4DashControlbar(),
    footer = bs4DashFooter(),
    body = bs4DashBody(

      bs4TabItems(

        bs4TabItem(
          tabName = "tab-introduction",

          bs4TabCard(
            id = "tabcard", title = "Tab Card", side = "right",

            bs4TabPanel(
              tabName = "Tab 1",
              uiOutput("ui"),
              rHandsontableOutput("hot")
            ),

            bs4TabPanel(
              tabName = "Tab 2",
              p("Hey")
            )
          )
        )
      )
    )
  ),
  server = function(input, output) {
    output$hot <- renderRHandsontable({ rhandsontable(mtcars[1:10, 1:3]) })
    output$ui <- renderUI({
      numericInput("num_ui", label = "Num In", value = 15)
    })
  }
)

Reproducible code for Picture 3 and Picture 4

library(shiny)
library(bs4Dash)
library(rhandsontable)

# Tabcard module ----------------------------------------------------------

mod_tabcard_ui <- function(id){
  ns <- NS(id)

  bs4TabCard(
    id = ns("tabcard"), title = "Tab Card", side = "right",

    bs4TabPanel(
      tabName = "Tab 1",
      uiOutput(ns("ui")),
      rHandsontableOutput(ns("hot"))
    ),

    bs4TabPanel(
      tabName = "Tab 2",
      mod_numinput_ui(ns("num"))
    )
  )

}

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

  output$hot <- renderRHandsontable({ rhandsontable(mtcars[1:10, 1:3]) })

  output$ui <- renderUI({
    numericInput(session$ns("num_ui"), label = "Num In", value = 15)
  })

  callModule(mod_numinput_server, "num")

}


# Numeric input module ----------------------------------------------------

mod_numinput_ui <- function(id){
  ns <- NS(id)
  numericInput(ns("num"), "Num In", 0, 0, 10)
}

mod_numinput_server <- function(input, output, server){
  return(reactive({input$num}))
}


# Section module ----------------------------------------------------------

mod_section_ui <- function(id){
  ns <- NS(id)

  mod_tabcard_ui(id = "tabcard")

}

mod_section_server <- function(input, output, session){
  callModule(mod_tabcard_server, id = "tabcard")
}


# The app -----------------------------------------------------------------

shiny::shinyApp(
  ui = bs4DashPage(
    old_school = FALSE,
    sidebar_min = TRUE,
    sidebar_collapsed = FALSE,
    controlbar_collapsed = FALSE,
    controlbar_overlay = TRUE,
    title = "Basic Dashboard",
    navbar = bs4DashNavbar(),
    sidebar = bs4DashSidebar(

      sidebarMenu(
        bs4Dash::menuItem(
          text = "Introduction",
          tabName = "tab-introduction",
          icon = ""
        )
      )

    ),
    controlbar = bs4DashControlbar(),
    footer = bs4DashFooter(),
    body = bs4DashBody(

      bs4TabItems(

        bs4TabItem(
          tabName = "tab-introduction",
          mod_section_ui(id = "mod")
        )
      )
    )
  ),

  server = function(input, output) {
    callModule(mod_section_server, id = "mod")
  }
)

Solution

  • you are missing a namespace in the mod_section_ui module. It should be:

    mod_section_ui <- function(id){
      ns <- NS(id)
      mod_tabcard_ui(id = ns("tabcard"))
    }