You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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"
)

问题原因

  1. 多标签选中冲突:动态生成的menuItem都设置了selected=TRUE,导致应用初始化时多个标签页被标记为选中状态,Shiny的标签内容渲染逻辑因此混乱,无法正确触发默认标签页portfolio的内容渲染。
  2. 内容渲染触发时机: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:45:54