Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updateTabItems not working when selecting dynamically rendered menuSubItem #406

Open
oelhammouchi opened this issue Mar 18, 2024 · 2 comments

Comments

@oelhammouchi
Copy link

Hi, first of all, thank you so much for your work on this awesome package. I'm using it to build an app at my company, but I'm experiencing the following issue. When dashboardBody contains dynamically rendered menuSubItems, I cannot manage to select one of them using updateTabItems. Unfortunately I cannot share the code since it's proprietary, but I managed to reproduce the issue by adapting the code from this post:

library(shiny)
library(shinydashboard)

# subitems, could be dynamic from user input or database
data_subitems <- c("one", "two", "three")

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    uiOutput("mysidebar")
  ),
  dashboardBody(
    uiOutput("mycontent")
  )
  
)

server <- function(input, output, session) {
  
  # This is to get the desired menuItem selected initially. 
  # selected=T seems not to work with a dynamic sidebarMenu.
  observeEvent(session, {
    updateTabItems(session, "tabs", selected = "subs_one")
  })
  
  # Use reactive values when working with Shiny.
  subitems <- reactiveVal(value = data_subitems)
  
  # dynamic sidebar menu #
  output$mysidebar <- renderUI({
    sidebarMenu(id = "tabs",
      menuItem("Start", tabName = "initial", icon = icon("star"), selected = T),
      menuItem("Subs", id = "subs", tabName = "subs",  icon = icon("dashboard"), 
               startExpanded = T,
               lapply(subitems(), function(x) {
                 menuSubItem(x, tabName = paste0("sub_", x)) } )),
      menuItem("Setup", tabName = "setup")
    )
  })
  
  # dynamic content #
  output$mycontent <- renderUI({
    
    itemsSubs <- lapply(subitems(), function(x){
      tabItem(tabName = paste0("sub_", x), uiOutput(paste0("sub_", x)))
    })
    
    items <- c(
      list(
        tabItem(tabName = "initial",
                "Welcome on the initial page!"
        )
      ),
      
      itemsSubs,
      
      list(
        tabItem(tabName = "setup",
                
                textInput("add_subitem", "Add subitem"),
                actionButton("add", "add!"),
                
                selectInput("rm_subitem", "Remove subitem", choices = subitems()),
                actionButton("rm", "remove!")
        )
      )
    )
    
    do.call(tabItems, items)
  })
  
  # dynamic content in the dynamic subitems #
  observe({ 
    lapply(subitems(), function(x){
      output[[paste0("sub_", x)]] <- renderUI ({
        list(fluidRow(
          box("hello ", x)
        )
        )
      })
    })
  })

  # add and remove tabs
  observeEvent(input$add, {
    req(input$add_subitem)

    s <- c(subitems(), input$add_subitem)
    subitems(s)

    updateTabItems(session, "tabs", selected = "setup")
  })
  
  observeEvent(input$rm, {
    req(input$rm_subitem)
    
    s <- subitems()[-which(subitems() == input$rm_subitem)]
    subitems(s)
    
    updateTabItems(session, "tabs", selected = "setup")
  })

}
    
shinyApp(ui, server)

This is the result on initial page load:

Screenshot 2024-03-18 at 17 56 06

Note that the issue goes away completely if I select a top-level tab instead. Any idea what might be causing this and how to remedy it?

@YongbingDing
Copy link

YongbingDing commented Mar 18, 2024 via email

@gadenbuie
Copy link
Member

Hi @OthmanElHammouchi, thanks for your question! It would be helpful if you could rewrite the example so that it includes only the code needed to see and reproduce your issue.

That said, in a quick scan, I noticed this observe:

  observeEvent(input$add, {
    req(input$add_subitem)

    s <- c(subitems(), input$add_subitem)
    subitems(s)

    updateTabItems(session, "tabs", selected = "setup")
  })

What happens here is that the observer updates subitems() and then calls updateTabItems(). These are called one after the other in the same observeEvent block, which means that when updateTabItems() is called the renderUI() that creates the menu hasn't been re-rendered yet.

The timeline of events is roughly:

  1. Update subitems()
  2. Update the currently selected tab
  3. Re-render the menu because subitems() was updated, losing the currently selected tab.

To make this work, you'll need to separate the new tab addition and the tab selection. Hope that helps, and if not please make a smaller reprex (reproducible example) and I'll take another look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants