rshinybs4dash

Is it possible to conditionally show the controlbar in shiny apps?


I want to show the controlbar (right sidebar) only for several tabs in the left sidebar.

library(shiny)
library(bs4Dash)

shinyApp(
  ui = dashboardPage(
    header = dashboardHeader(
      title = "My dashboard"
    ),
    sidebar = dashboardSidebar(
      
      sidebarMenu(
        id = "sidebarMenu",
        menuItem(
          text = "Tab 1",
          tabName = "tab1"
        ),
        menuItem(
          text = "Tab 2",
          tabName = "tab2"
        )
      )
      
    ),
    body = dashboardBody(),
    controlbar = dashboardControlbar(),
    title = "DashboardPage"
  ),
  server = function(input, output) { }
)

I try to develop a shiny app where for some of the tabs a controlbar is necessary, for others the controlbar should show certain options and for others it is obvious at all. What is the most elegant way to ensure this? Should i "outsource" the appearance into seperate modules or are there other recommendations?


Solution

  • It is possible, and there are a lot of different ways to achieve this. Different methods have their benefits and shortcomings. I can demonstrate to basic Shiny methods for conditional UI elements that are used the most. The common thing among the "easy" solutions to the conditional UI per tab problem is to evaluate somehow on which tab the users view currently is.

    First method would be to check the Javascript context and adapt the UI using conditionalPanel:

    shinyApp(
      ui = dashboardPage(
        header = dashboardHeader(
          title = "My dashboard"
        ),
        sidebar = dashboardSidebar(
          sidebarMenu(
            id = "sidebarMenu",
            menuItem(
              text = "Tab 1",
              tabName = "tab1"
            ),
            menuItem(
              text = "Tab 2",
              tabName = "tab2"
            ),
            menuItem(
              text = "Tab 3",
              tabName = "tab3"
            )
          )
        ),
        body = dashboardBody(),
        controlbar = dashboardControlbar(
          id= "controlbar",
          collapsed = TRUE,
          conditionalPanel(
            condition = "input.sidebarMenu=='tab1'||input.sidebarMenu=='tab3'",
            controlbarMenu(
              controlbarItem(title = "Item1"),
              controlbarItem(title = "Item2")
              )
            ),
            conditionalPanel(
              condition = "input.sidebarMenu=='tab2'",
              controlbarMenu(
                controlbarItem(title = "Item3")
              )
            ),
            conditionalPanel(
              condition = "input.sidebarMenu=='tab3'",
              controlbarMenu(
              controlbarItem(title = "Item4")
              )
          )
        ),
        title = "DashboardPage"
      ),
      server = function(input, output,session) { }
    )
    

    The benefit is clearly that this should render faster since it gets evaluated inside the UI (clientside). The downside is that we have to use Javascript (what we might dont want to do) and also we create logic in the UI function, making it more cluttered.

    The next method would be to use R and render some parts of the UI in the Server function and send it to an output function in the UI

    shinyApp(
      ui = dashboardPage(
        header = dashboardHeader(
          title = "My dashboard"
        ),
        sidebar = dashboardSidebar(
          sidebarMenu(
            id = "sidebarMenu",
            menuItem(
              text = "Tab 1",
              tabName = "tab1"
            ),
            menuItem(
              text = "Tab 2",
              tabName = "tab2"
            ),
            menuItem(
              text = "Tab 3",
              tabName = "tab3"
            )
          )
        ),
        body = dashboardBody(),
        controlbar = dashboardControlbar(
          id= "controlbar",
          collapsed = TRUE,
          sidebarMenuOutput("Menu")
          ),
        title = "DashboardPage"
      ),
      server = function(input, output,session) { 
        observeEvent(input$sidebarMenu, {
            output$Menu <- renderMenu({
              if(input$sidebarMenu=="tab1") {
              controlbarMenu(
                controlbarItem(
                  title = "Item1"
                )
              )
              }else if(input$sidebarMenu=="tab2"){
                controlbarMenu(
                  controlbarItem(
                    title = "Item2"
                  ),
                  controlbarItem(
                    title = "Item2_2"
                  )
                )
              }else{
                controlbarMenu()
              }
            })
        })
        }
    )
    

    The benefit is that we have the logic in the Server function, and our UI gets more concise. The downsight is that we add extra computational work to the server, which could be done on the clients side. We also will have to write some if else statments or map or apply and its not clear what amount of the UI should be rendered in the server function. Also it gets more complex if you want to add a feature, so often times you have rewrite this alot during development if your not careful and plan ahead for reactivity, etc.