ShinyApp进入时Menu Tab内容未渲染问题排查求助
Shiny应用初始标签页内容不渲染问题修复
问题描述
开发的Shiny应用带有左侧导航,用户进入应用时默认处于「Menu」标签页(对应tabName="portfolio"),但此时mycontent对应的内容并未渲染显示,只有切换到其他标签页再返回「Menu」标签页时,内容才会正常显示。
问题代码
Server.R
server = function(input, output, session) { observe({ shinyjs::runjs('Shiny.setInputValue("selected_tab", "current_tab");') }) useAutoColor() js_code <- " // Change the title color and size using JavaScript var portfolioTextElement = document.querySelector('sidebar'); if (portfolioTextElement) { portfolioTextElement = '#FFF' !important; // Change to your desired color } " shinyjs::runjs(js_code) observeEvent(input$sidebarItemExpanded, { shinyjs::runjs('$(".menu-item-text").removeClass("active");') shinyjs::runjs('$(".menu-item-text").click(function() { $(this).addClass("active"); });') }) #app button -------------------------------------------------------------- output$btnVal <- renderText(input$myAppButton) observeEvent(input$myAppButton, { showModal(modalDialog("Thanks for clicking me!", easyClose = TRUE)) }) observeEvent(input$dark_mode, { toast( title = if (input$dark_mode) "Dark theme on!" else "Light theme on", options = list(position = "topRight", class = "bg-warning", autohide = TRUE) ) }) output$basic_cards_tab <- renderUI({ tagList(tabItem( tabName = "portfolio", fluidRow({ num_rows <- nrow(applicationData) print(num_rows) if (num_rows == 0) { print("This Worked") fluidPage( title = h2("Not Found"), closable = FALSE, width = 8, status = "danger", solidHeader = FALSE, collapsible = FALSE, tags$h5("We're sorry. We were not able to find any application access to you. Please contact Admin for inquiry"), tags$hr(), tags$h6("Contact Details:"), tags$h6("xx@xx.com"), tags$h6("xxx@xx.com"), tags$hr(), tags$iframe( src="https://xx.com/contact", style = "border: none; overflow: hidden; height: 500; width : 70%;", sandbox = "allow-same-origin allow-scripts" ) ) } else { print(applicationData$name[1]) lapply(1:nrow(applicationData), function(i) { print(applicationData$name[1]) box( title = applicationData$name[i], closable = FALSE, width = 4, status = "warning", solidHeader = FALSE, collapsible = FALSE, label = boxLabel( text = HTML(paste('<p style="font-family: Arial;">', applicationData$abbrev[i],'</p>')), status = "primary" ), userPost( id = 1, image = ifelse(is.null(applicationData$image_url[i]), g_default_image, applicationData$image_url[i]), author = NULL, description = applicationData$description[i] ), actionButton(paste0("app_link_", i), "Open", class = "btn-primary btn-sm") ) }) } }) ) ) }) output$mysidebar <- renderUI({ #applicationData <- appData() print(nrow(applicationData)) if(nrow(applicationData) != 0) { lapply(1:nrow(applicationData), function(i) { menuItem( text = HTML(paste('<p style="color:white;">', applicationData$name[i], '</p>')), tabName = paste0("app_tab_", i), selected=TRUE, # 问题点之一 icon = icon("laptop-code", style = "color: #FFFFFF") ) }) } else { NULL } }) output$mycontent<-renderUI({ #applicationData <- appData() items <- c( list( tabItem( tabName = "portfolio", uiOutput("basic_cards_tab")), lapply(1:nrow(applicationData), function(i) { url_link<-applicationData$url[i] tabItem(tabName = paste0("app_tab_", i), tags$iframe( src=url_link, # local deployment style = "border: none; overflow: hidden; height: 1000px; width : 100%; # position: relative; # top:15px; # padding:0;", sandbox = "allow-same-origin allow-scripts" ) ) }) ) # render do.call(tabItems, items) }) observe({ #applicationData <- appData() if(nrow(applicationData) != 0){ lapply(1:nrow(applicationData), function(i) { observeEvent(input[[paste0("app_link_", i)]], { value<-paste0("app_tab_", i) print(input$current_tab) newtab <- switch(input$current_tab, "portfolio" = value,value= "portfolio") updateTabItems(session, "current_tab", newtab) }) }) } }) output$userData <- renderReactable({ applicationData <- appData() userdata <- UserData() reactable(userData ) }) output$admin_tab <- renderUI({ list( tabItem( tabName = "adminTab", reactableOutput('userData') ) ) }) output$homePage <- renderUI({ tagList(tabItem( tabName = "portfolio", box( title = "App", closable = FALSE, width = 8, status = "warning", solidHeader = FALSE, collapsible = FALSE, label = boxLabel( text = "Visualize your data", status = "primary" ), userPost( id = 1, image = "https://www.xx.com/img/home-process.png", author = NULL, description = "Visualize your data" ) ) ) ) }) # update sidebar ---------------------------------------------------------- observeEvent(input$sidebarToggle, { updateSidebar(id = "sidebar") }) }
UI.R
thematic_shiny() ui = dashboardPage( # Creates a loading screen with secondary blue. preloader = list(html = tagList(spin_1(), "Loading ..."), color = "#2078A8"), help = NULL, dark = NULL, fullscreen = TRUE, scrollToTop = TRUE, header = dashboardHeader( tags$style( HTML( " .sidebar { background-color: #155070 !important; font-color: #fff !important; font-family: Source Sans Pro !important; } .current_tab { font-color: #fff !important; background-color: #155070 !important; font-family: Source Sans Pro !important; } .sidebar .treeview-menu > li > a { color: #fff !important; /* Change the font color for menuItem text */ } .nav-link.active { background-color: #9caeb8 !important; } .menu-item-text.active { color: #9caeb8 !important; } " ) ), title = dashboardBrand( title = HTML('<p style = "color:black;font-family: Source Sans Pro">Brand Menu</p>'), color = "white", href = "https://XX.com/", image = "IMG.JPG", opacity = 0.8 ), fixed = TRUE, logout_ui("logout",classbutton = "btn-primary btn-xs",textbutton = HTML('<p style = "color:black;margin: 5px;">Log Out</p>')) ), sidebar = dashboardSidebar( fixed = TRUE, skin = "light", status = "info", id = "sidebar", sidebarMenu( id = "current_tab", flat = FALSE, compact = FALSE, childIndent = TRUE, sidebarHeader(title = HTML('<p style="color:white;"></p>')), menuItem( text=HTML('<p style="color:white;font-family: Source Sans Pro">Menu</p>'), tabName = "portfolio", icon = icon("bars", style = "color: #FFFFFF") ), uiOutput("mysidebar") ) ), body = dashboardBody( useShinyjs(), uiOutput("mycontent"), tags$head( tags$script( HTML("function openWebsite() { window.location.href = 'https://www.example.com/'; }") ), ) ), controlbar = NULL, title = "App Menu" )
问题原因
- 多标签选中冲突:动态生成的
menuItem都设置了selected=TRUE,导致应用初始化时多个标签页被标记为选中状态,Shiny的标签内容渲染逻辑因此混乱,无法正确触发默认标签页portfolio的内容渲染。 - 内容渲染触发时机:
mycontent的renderUI依赖于applicationData,但初始时可能因为数据加载完成后没有触发重新渲染,导致内容未生成。
修复方案
1. 移除多余的selected=TRUE
修改output$mysidebar中的menuItem,去掉selected=TRUE参数,确保只有默认的「Menu」标签页保持选中状态:
output$mysidebar <- renderUI({ print(nrow(applicationData)) if(nrow(applicationData) != 0) { lapply(1:nrow(applicationData), function(i) { menuItem( text = HTML(paste('<p style="color:white;">', applicationData$name[i], '</p>')), tabName = paste0("app_tab_", i), # 移除selected=TRUE icon = icon("laptop-code", style = "color: #FFFFFF") ) }) } else { NULL } })
2. 强制触发初始标签内容渲染
在server函数开头添加观察者,确保应用启动时手动触发mycontent的渲染:
# 应用启动后强制选中默认标签页并触发内容渲染 observe({ session$onFlushed(function() { updateTabItems(session, "current_tab", "portfolio") }, once = TRUE) })
3. 明确数据依赖
如果applicationData是动态加载的,让mycontent的renderUI明确依赖于它,避免数据加载完成后内容未更新:
output$mycontent<-renderUI({ # 明确依赖applicationData,确保数据加载后触发渲染 req(applicationData) items <- c( list( tabItem( tabName = "portfolio", uiOutput("basic_cards_tab")), lapply(1:nrow(applicationData), function(i) { url_link<-applicationData$url[i] tabItem(tabName = paste0("app_tab_", i), tags$iframe( src=url_link, style = "border: none; overflow: hidden; height: 1000px; width : 100%;", sandbox = "allow-same-origin allow-scripts" ) ) }) ) do.call(tabItems, items) })
验证修复
修改后重启应用,进入时默认「Menu」标签页的内容应该会直接渲染显示,无需切换其他标签页再返回。
内容的提问来源于stack exchange,提问作者ben890
相关产品推荐
相关产品推荐

