使用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
相关产品推荐
相关产品推荐

