如何使用 `renderUI` 响应式更新 Shiny 应用程序中的活动菜单项?

     2023-02-18     292

关键词:

【中文标题】如何使用 `renderUI` 响应式更新 Shiny 应用程序中的活动菜单项?【英文标题】:How can I reactively update the active menuItem in a Shiny app using `renderUI`? 【发布时间】:2021-03-21 19:16:17 【问题描述】:

我正在构建一个闪亮的应用程序,它可以从数据框中动态创建反应性 bs4Box 元素。我想让用户可以点击这些框,以便自动重定向到不同的 menuItem。我已经阅读并遵循了类似的先前 SO 问题,例如 this one 或 this issue,但没有成功。 JavaScript 解决方案like this one 也可以工作?

到目前为止,这是我使用 updatebs4ControlbarMenu 函数的尝试:

library(shiny)
#> Warning: package 'shiny' was built under R version 3.6.3
library(shinyWidgets)
#> Warning: package 'shinyWidgets' was built under R version 3.6.3
library(bs4Dash)
#> 
#> Attaching package: 'bs4Dash'
#> The following objects are masked from 'package:shiny':
#> 
#>     column, tabPanel, tabsetPanel, updateTabsetPanel
#> The following object is masked from 'package:graphics':
#> 
#>     box
library(tidyverse)
#> Warning: package 'ggplot2' was built under R version 3.6.3
#> Warning: package 'tibble' was built under R version 3.6.3
#> Warning: package 'tidyr' was built under R version 3.6.3
#> Warning: package 'readr' was built under R version 3.6.3
#> Warning: package 'purrr' was built under R version 3.6.3
#> Warning: package 'dplyr' was built under R version 3.6.3

shinyApp(
  ui = bs4DashPage(
    sidebar_collapsed = FALSE,
    controlbar_collapsed = TRUE,
    enable_preloader = FALSE,
    navbar = bs4DashNavbar(skin = "dark"),
    sidebar = bs4DashSidebar(
      inputId = "sidebarState",
      bs4SidebarMenu(
        id = "sidebr",
        bs4SidebarMenuItem(
          "Tab 1",
          tabName = "tab1"
        ),
        bs4SidebarMenuItem(
          "Tab 2",
          tabName = "tab2"
        )
      )
    ),
    
    bs4DashBody(
      bs4TabItems(
        bs4TabItem(
          tabName = "tab1",
          h1("Welcome!"),
          fluidRow(
            pickerInput(
              inputId = "car",
              label = "Car", 
              choices = row.names(mtcars),
              selected = head(row.names(mtcars), 3),
              multiple = TRUE,
              options = list(
                `actions-box` = TRUE)
            ),
            pickerInput(
              inputId = "gear",
              label = "Gear", 
              choices = unique(mtcars$gear),
              selected = unique(mtcars$gear),
              multiple = TRUE,
              options = list(
                `actions-box` = TRUE)
            )
          ),
          
          fluidRow(
            column(6,
                   uiOutput("uiboxes")
            )
          )
        ),
        
        bs4TabItem(
          tabName = "tab2",
          h4("Yuhuuu! You've been directed automatically in Tab 2!")
        )
      )
    )
  ),
  server = function(input, output, session) 
    
    submtcars <- reactive(
      req(input$car, input$gear)
      mtcars %>% 
        mutate(
          carnames = rownames(mtcars)) %>% 
        filter(
          carnames %in% input$car &
            gear %in% input$gear
        )
    )
    
    
    observeEvent( submtcars(), 
      
      output$uiboxes <- renderUI(
        n_ex <- nrow(submtcars())
        lapply(1:n_ex, FUN = function(j) 
          print(paste("j is ", j))
          bs4Box(
            title = submtcars()$carnames[j],
            width = 12,
            str_c("Number of gears:", submtcars()$gear[j]),
            
            btnID <- paste0("btnID", j),
            
            print(btnID),
            fluidRow(
              column(
                2,
                actionBttn(
                  inputId = btnID,
                  icon("search-plus")
                )
              )
            )
          )
        )
      )
    )
    
    observeEvent( input$btnID , 
      updatebs4ControlbarMenu(
        session,
        inputId = "sidebr",
        selected = "tab2"
      )
      
    )
  
)
#> 
#> Listening on http://127.0.0.1:5851
#> [1] "j is  1"
#> [1] "btnID1"
#> [1] "j is  2"
#> [1] "btnID2"
#> [1] "j is  3"
#> [1] "btnID3"

由reprex package (v0.3.0) 于 2020 年 12 月 10 日创建

【问题讨论】:

【参考方案1】:

您的问题是如何设置observeEvent。你只注册一个监听input$btnID。但是,btnID 只是一个变量,您可以在其中定义操作按钮的不同 ID(在您的情况下为“btnID1”到“btnID3”。因此,您必须使用这些 ID 注册observeEvents。为此,您可以再次使用lapply,如下面的解决方案所示。

library(shiny)
library(shinyWidgets)
library(bs4Dash)
library(tidyverse)

shinyApp(
  ui = bs4DashPage(
    sidebar_collapsed = FALSE,
    controlbar_collapsed = TRUE,
    enable_preloader = FALSE,
    navbar = bs4DashNavbar(skin = "dark"),
    sidebar = bs4DashSidebar(
      inputId = "sidebarState",
      bs4SidebarMenu(
        id = "sidebr",
        bs4SidebarMenuItem(
          "Tab 1",
          tabName = "tab1"
        ),
        bs4SidebarMenuItem(
          "Tab 2",
          tabName = "tab2"
        )
      )
    ),
    
    bs4DashBody(
      bs4TabItems(
        bs4TabItem(
          tabName = "tab1",
          h1("Welcome!"),
          fluidRow(
            pickerInput(
              inputId = "car",
              label = "Car", 
              choices = row.names(mtcars),
              selected = head(row.names(mtcars), 3),
              multiple = TRUE,
              options = list(
                `actions-box` = TRUE)
            ),
            pickerInput(
              inputId = "gear",
              label = "Gear", 
              choices = unique(mtcars$gear),
              selected = unique(mtcars$gear),
              multiple = TRUE,
              options = list(
                `actions-box` = TRUE)
            )
          ),
          
          fluidRow(
            column(6,
                   uiOutput("uiboxes")
            )
          )
        ),
        
        bs4TabItem(
          tabName = "tab2",
          h4("Yuhuuu! You've been directed automatically in Tab 2!")
        )
      )
    )
  ),
  server = function(input, output, session) 
    
    submtcars <- reactive(
      req(input$car, input$gear)
      mtcars %>% 
        mutate(
          carnames = rownames(mtcars)) %>% 
        filter(
          carnames %in% input$car &
            gear %in% input$gear
        )
    )
    
    
    observeEvent( submtcars(), 
      n_ex <- nrow(submtcars())
      output$uiboxes <- renderUI(
        
        lapply(1:n_ex, FUN = function(j) 
          print(paste("j is ", j))
          bs4Box(
            title = submtcars()$carnames[j],
            width = 12,
            str_c("Number of gears:", submtcars()$gear[j]),
            
            btnID <- paste0("btnID", j),
            
            print(btnID),
            fluidRow(
              column(
                2,
                actionBttn(
                  inputId = btnID,
                  icon("search-plus")
                )
              )
            )
          )
        )
      )
      
      lapply(1:n_ex, function(j) 
        btnID <- paste0("btnID", j)
        observeEvent(input[[btnID]] , 
          updatebs4ControlbarMenu(
            session,
            inputId = "sidebr",
            selected = "tab2"
          )
        )
      )
    )
    
  
)

但是,我认为使用 lapplyrenderUI 的方法不是很好,因为如果您的输入之一发生变化,所有框都会重新呈现,而有些框可能会保持不变。因此,我推荐使用insertUI/removeUI 的方法。看看我的一些旧答案,例如https://***.com/a/63758952/12647315

【讨论】:

谢谢@starja。虽然它可以工作,但我的应用程序是模块化的,我正在努力实施这个解决方案,正如这里另一个问题中所解释的那样:***.com/q/65262056/7375944

如何使用 Spring 响应式通过增量进度更新逐个处理每个产品?

】如何使用Spring响应式通过增量进度更新逐个处理每个产品?【英文标题】:HowtoprocesseachproductonebyonewithincrementalprogressupdateusingSpringreactive?【发布时间】:2019-11-0404:38:33【问题描述】:我需要有关SpringReactive的帮助,其中休息调... 查看详情

如何获取未更新的响应式表单数据?

】如何获取未更新的响应式表单数据?【英文标题】:Howtogetreactiveformdatathatwasn\'tupdated?【发布时间】:2019-12-3003:30:03【问题描述】:蓝光:最终,我只是想让这一段代码发挥作用,但我无法获得具有更新值且没有空值的完整对... 查看详情

如何使用响应式表单将表单绑定到 Angular 6 中的模型?

】如何使用响应式表单将表单绑定到Angular6中的模型?【英文标题】:HowcanIbindaformtoamodelinAngular6usingreactiveforms?【发布时间】:2018-12-2221:02:40【问题描述】:在Angular6之前,我使用[(ngModel)]将表单字段直接绑定到模型。现在已弃用... 查看详情

使用 JavaScript 创建的 Svelte 组件不接收响应式更新

】使用JavaScript创建的Svelte组件不接收响应式更新【英文标题】:SveltecomponentcreatedwithJavaScriptnotreceivingreactiveupdates【发布时间】:2021-08-2403:38:26【问题描述】:我使用以下JS在按钮单击时动态创建一个组件:<script>exportletorder;f... 查看详情

如何使用 JCrop 获得响应式图像

】如何使用JCrop获得响应式图像【英文标题】:HowtogetresponsiveimagesworkingwithJCrop【发布时间】:2014-12-1404:09:31【问题描述】:我看到了一些解决方案,其中使用truesize属性让JCrop正确处理响应式图像。但是,在使用JCrop时,我很难让... 查看详情

小丸子看vue如何响应式(代码片段)

小丸子看vue如何响应式几个月跟着大佬们投入在vue项目里,对vue算是从0开始啦。相比较之前使用的regular和angular的响应式的脏检查机制,发现vue的响应式是命中式的。使用angular或regular的时候,发现更新一个值,... 查看详情

在 Angular 2 中,响应式表单不使用 ngModel 和 formArrayName 更新值

】在Angular2中,响应式表单不使用ngModel和formArrayName更新值【英文标题】:InAngular2reactiveformdoesn\'tupdatevaluesusingngModelandformArrayName【发布时间】:2017-11-1000:19:06【问题描述】:我在Angular2中有一个简单的反应式表单,但我的表单没... 查看详情

响应式网页设计

...体的大小和比例需要随着屏幕的变化而自适应调整。了解如何正确配置图片和媒体可以使页面加载更快,并保证在不同的设备上呈现出最佳的视觉效果。尝试实践,通过练手项目来深入理解响应式网页设计,可以使用响应式网格... 查看详情

R Shiny 模块不会在同一事件中响应式更新

...ameevent【发布时间】:2019-12-1508:15:22【问题描述】:在R中使用模块时,我遇到了反应性问题。如果我更新一个模块,然后尝试用这些更新的值更新另一个模块,我会在更新之前获取这些值。>我已经编写了一些基本代码来说明... 查看详情

如何使用 Responsive Filemananger 在 Ckeditor 中制作响应式图像

】如何使用ResponsiveFilemananger在Ckeditor中制作响应式图像【英文标题】:HowmakeresponsiveimagesinCkeditorusingResponsiveFilemananger【发布时间】:2019-04-2704:26:07【问题描述】:我正在使用Ckeditor和响应式文件管理器。我希望上传的图像是响应... 查看详情

vue响应式原理

...这样可以回避一些常见的问题,下面我们来介绍一下Vue是如何侦测数据并响应视图的。Object.definePropertyVue数据响应核心就是使用了 Object.d 查看详情

如何使用 d3 创建响应式地图

】如何使用d3创建响应式地图【英文标题】:Howtocreatearesponsivemapusingd3【发布时间】:2014-01-2000:45:44【问题描述】:我正在尝试使我的地图具有响应性,因此它会根据浏览器窗口的大小进行调整。这是我目前所拥有的,style.css#mapb... 查看详情

响应式画布 vuejs

...。我在挂载时触发resizeCanvas(),但容器的高度和宽度为0。如何拥有适合其容器的画布,以便我可以使用css更改容器大小并更新画布?(对不起我的英语,我不流利)<templatelang="html"><divclass 查看详情

如何使用 vuetify 制作移动响应式导航栏

】如何使用vuetify制作移动响应式导航栏【英文标题】:Howtomakemobileresponsivenavbarusingvuetify【发布时间】:2021-02-0316:41:51【问题描述】:我有一个vuetify选项卡组件,我希望将它用作我的导航菜单。我想使用vuetify创建一个响应式导... 查看详情

如何使用响应式菜单防止正文滚动

】如何使用响应式菜单防止正文滚动【英文标题】:HowtopreventbodyfromscrollingwithResponsivemenu【发布时间】:2021-05-0501:49:00【问题描述】:我正在尝试将ResponsiveMenu插件正确地实现到wordpress主题中。推送侧菜单打开时会出现此问题,... 查看详情

如何使 Adsense 广告与响应式网站一起使用

】如何使Adsense广告与响应式网站一起使用【英文标题】:HowtomakeAdsenseadsworkwithresponsivewebsite【发布时间】:2013-01-0309:00:03【问题描述】:我查看了当前的解决方案,这两个帖子已经部分涵盖了这个问题;MakingAdsenseResponsive和Injavas... 查看详情

如何使用 ReactJs 构建响应式网站 [关闭]

】如何使用ReactJs构建响应式网站[关闭]【英文标题】:HowtobuildaresponsivewebsitewithReactJs[closed]【发布时间】:2015-12-2022:42:01【问题描述】:我有点困惑。到目前为止,我一直在使用bootstrap或jquerymobile来构建基于jQuery的应用程序。我... 查看详情

如何使用 WebClient 使用响应式 Spring Rest API

】如何使用WebClient使用响应式SpringRestAPI【英文标题】:HowtoconsumeaReactiveSpringRestAPIwithWebClient【发布时间】:2017-10-2501:36:42【问题描述】:我需要在后端作业(可执行jar)上使用反应式休息API(使用springwebflux构建)。我读过SpringWe... 查看详情