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

使用shiny.fluent和shiny.router的Shiny应用:导航栏选中状态不更新问题

问题:shiny.fluent导航栏选中状态未随shiny.router跳转更新

在使用shiny.fluent和shiny.router构建的Shiny应用中,通过首页route_link()定义的跳转链接可成功跳转到测试页,但左侧垂直导航栏的选中状态不会自动更新,始终停留在Home项。


解决方案1:通过Shiny响应式状态同步导航选中项

核心思路是监听shiny.router的路由变化,动态更新Nav组件的selectedKey参数,让导航栏选中状态与当前路由匹配。

修改后完整代码

library(dplyr)
library(glue)
library(shiny)
library(shiny.fluent)
library(shiny.router)

makeCard <- function(title, content, size = 12, style = "") {
  div(class = glue("card ms-depth-8 ms-sm{size} ms-xl{size}"),
      style = style,
      Stack(
        tokens = list(childrenGap = 5),
        Text(variant = "large", title, block = TRUE),
        content
      ))
}
makePage <- function (title, subtitle, contents) {
  tagList(div(
    class = "page-title",
    span(title, class = "ms-fontSize-32 ms-fontWeight-semibold", style =
           "color: #323130"),
    span(subtitle, class = "ms-fontSize-14 ms-fontWeight-regular", style =
           "color: #605E5C; margin: 14px;")
  ),
  contents)
}

# 将导航改为动态UI输出
navigationUI <- function(id) {
  ns <- NS(id)
  uiOutput(ns("nav"))
}

navigationServer <- function(id, current_route) {
  moduleServer(id, function(input, output, session) {
    output$nav <- renderUI({
      # 根据当前路由匹配对应导航key
      selected_key <- case_when(
        current_route() == "/" ~ "home",
        current_route() == "test" ~ "test",
        TRUE ~ "home"
      )
      
      Nav(
        groups = list(
          list(links = list(
            list(name = 'Home', url = '#!/', key = 'home', icon = 'Home'),
            list(name = 'Test', url = '#!/test', key = 'test', icon = 'AnalyticsReport')
          ))
        ),
        initialSelectedKey = selected_key,
        selectedKey = selected_key, # 动态绑定选中项
        styles = list(
          root = list(
            height = '100%',
            boxSizing = 'border-box',
            overflowY = 'auto'
          )
        )
      )
    })
  })
}

layout <- function(mainUI){
  div(class = "grid-container",
      div(class = "sidenav", navigationUI("nav")),
      div(class = "main", mainUI)
  )
}

home_page <- makePage(
  "This is a Fluent UI app built in Shiny",
  "shiny.react + Fluent UI = shiny.fluent",
  uiOutput(paste0("changepage"))
)
test_page <- makePage(
  "This is a Fluent UI app built in Shiny",
  "shiny.react + Fluent UI = shiny.fluent",
  div(
    makeCard(
      "shiny.react makes it easy to use React libraries in Shiny apps.",
      div(
        Text("my test")
      ))
  )
)

router <- make_router(
  route("/", home_page),
  route("test", test_page))

# 手动添加shiny.router依赖
shiny::addResourcePath("shiny.router", system.file("www", package = "shiny.router"))
shiny_router_js_src <- file.path("shiny.router", "shiny.router.js")
shiny_router_script_tag <- shiny::tags$script(type = "text/javascript", src = shiny_router_js_src)

ui <- fluentPage(
  layout(router$ui),
  tags$head(
    tags$link(href = "mystyle.css", rel = "stylesheet", type = "text/css"),
    shiny_router_script_tag
  ))

server <- function(input, output, session) {
  router$server(input, output, session)
  
  # 响应式变量跟踪当前路由
  current_route <- reactive({
    req(input$router_path)
    input$router_path
  })
  
  # 调用导航模块同步选中状态
  navigationServer("nav", current_route)
  
  output$changepage <- renderUI({
    list(
      tags$ul(
        tags$li(a(href = route_link("test"), "Go to test page"))
      )
    )
  })
}

shinyApp(ui, server)

解决方案2:通过JavaScript监听路由变化更新导航状态

若不想修改R端逻辑,可添加一段JS代码监听URL哈希变化,手动更新Fluent UI导航栏的选中状态。

修改UI部分代码

在tags$head中添加如下脚本:

ui <- fluentPage(
  layout(router$ui),
  tags$head(
    tags$link(href = "mystyle.css", rel = "stylesheet", type = "text/css"),
    shiny_router_script_tag,
    # 新增JS监听逻辑
    tags$script(HTML("
      window.addEventListener('hashchange', function() {
        const hash = window.location.hash;
        let selectedKey = 'home';
        if (hash === '#!/test') {
          selectedKey = 'test';
        }
        
        // 找到导航组件并更新选中状态
        const navElement = document.querySelector('.ms-Nav');
        if (navElement) {
          const reactInstance = window.ShinyReact.getReactInstance(navElement);
          if (reactInstance) {
            reactInstance.setState({ selectedKey: selectedKey });
          }
        }
      });
    "))
  ))

方案对比

  • 方案1符合Shiny响应式开发范式,适合复杂应用,便于后续扩展更多路由
  • 方案2是前端快速修复方案,无需修改大量R端代码,适合简单场景

内容的提问来源于stack exchange,提问作者mazu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 06:05:30