Shiny中如何在多个conditionalPanel间同步保留选择状态?
解决Shiny标签页间基因选择框同步问题
问题根源
Shiny禁止UI组件使用重复ID,而使用不同ID的选择框默认不会共享选中状态,导致切换标签页后选择重置。解决核心是维护一个全局共享的基因选择状态,同步两个标签页的选择框。
解决方案步骤
- 为两个标签页的基因选择框分配不同ID(
list_of_genes1和list_of_genes2) - 创建
reactiveVal()存储当前选中的基因,作为唯一数据源 - 监听两个选择框的变化,相互同步选中值
- 所有依赖基因选择的逻辑(数据过滤、绘图)都基于这个共享状态
修改后的完整代码
library(shiny) library(shinydashboard) library(shinyjs) library(shinyWidgets) library(plotly) library(ggplot2) library(dplyr) group <- c(rep("MINUS", 40), rep("PLUS", 40)) sample <- c(rep("KO1",20),rep("KO2",20), rep("WT1",20), rep("WT2",20)) gene <- c(rep("ABC",4),rep("DEF",7), rep("XYZ",9), rep("ABC",6), rep("XYZ",3),rep("DEF",11),rep("ABC",8),rep("DEF",5), rep("XYZ",7), rep("ABC",7),rep("XYZ",6),rep("DEF",7)) length <- c(5.42, 5.92, 4.73, 5.64, 5.57, 6.22, 5.61, 6.95, 5.28, 3.13, 4.66, 5.06, 4.83, 7.29, 6.92, 5.89, 8.33, 6.40, 6.10, 5.50, 5.32, 5.02, 3.29, 3.57, 2.79, 4.14, 3.85, 3.95, 3.91, 5.34, 4.52, 4.15, 6.19, 4.32, 1.19, 4.67, 3.71, 4.34, 2.93, 2.63, 8.35, 8.07, 6.54, 9.23, 8.01, 8.05, 7.84, 7.62, 9.15, 7.03, 7.93, 7.18, 9.76, 5.89, 7.66, 8.34, 7.55, 6.76, 7.28, 8.98, 7.42, 7.97, 5.86, 6.61, 6.58, 7.42, 5.77, 5.99, 8.11, 8.65, 7.13, 6.42, 7.52, 7.87, 4.66, 7.76, 6.46, 6.21, 8.18, 7.73) data <- data.frame(group, sample, gene, length) # label genes for dropdown menu gene_labels <- sort(unique(data$gene)) # custom function for plotting data plot_distr <- function(data, groupby=NA){ plot <- ggplot2::ggplot(data,ggplot2::aes(x=length,color=!!rlang::sym(groupby)))+ ggplot2::geom_line(stat="density",size=1,ggplot2::aes(y=..ndensity..)) return(plot) } # Define ui logic ---- ui <- fluidPage( shinyWidgets::useShinydashboard(), shinyjs::useShinyjs(), titlePanel("Test application"), sidebarLayout( sidebarPanel( conditionalPanel( condition = "input.my_tabs==1", shiny::helpText("Test of first tab."), shiny::selectizeInput( inputId = "list_of_genes1", # 修改ID为list_of_genes1 label = "Select gene:", choices = NULL ), ), conditionalPanel( condition ="input.my_tabs==2", shiny::helpText("Test of second tab."), shiny::selectizeInput( inputId = "list_of_genes2", # 修改ID为list_of_genes2 label = "Select gene:", choices = NULL ), selectInput( inputId = "selected_grouping_variable", label = "Select grouping variable:", choices = c( "sample" = "sample", "condition (group)" = "group")), checkboxInput("show_comment",label = "Display comments?",value = FALSE), uiOutput("level"), checkboxInput("show_comment2",label = "Plot only selected condition?",value = FALSE) ) ), mainPanel( tabsetPanel(type='tabs', id = "my_tabs", tabPanel("My first tab", value=1), tabPanel("My second tab", value=2, box( width=8, plotlyOutput("distribution_plot", height = "450px"), div(id = "text_div", textOutput("textofinterest"), style="text-align: justify;") ) ) ) ) ) ) # Define server logic ---- server <- function(input, output, session) { # 初始化共享的基因选择状态,默认选中第一个基因 selected_gene <- reactiveVal(gene_labels[1]) # 初始化两个selectizeInput observe({ updateSelectizeInput( session = session, inputId = 'list_of_genes1', label = 'Select gene of interest:', choices = gene_labels, selected = selected_gene(), # 用共享状态设置选中值 server=TRUE, options = list(maxOptions = length(gene_labels)) ) updateSelectizeInput( session = session, inputId = 'list_of_genes2', label = 'Select gene of interest:', choices = gene_labels, selected = selected_gene(), # 用共享状态设置选中值 server=TRUE, options = list(maxOptions = length(gene_labels)) ) }) # 监听第一个标签页的基因选择,同步到共享状态和第二个选择框 observeEvent(input$list_of_genes1, { req(input$list_of_genes1) selected_gene(input$list_of_genes1) updateSelectizeInput(session, "list_of_genes2", selected = input$list_of_genes1) }) # 监听第二个标签页的基因选择,同步到共享状态和第一个选择框 observeEvent(input$list_of_genes2, { req(input$list_of_genes2) selected_gene(input$list_of_genes2) updateSelectizeInput(session, "list_of_genes1", selected = input$list_of_genes2) }) observe({print(input$show_comment2)}) #select var to plot selected_variable_plot <- reactive({ req(input$selected_grouping_variable) switch(input$selected_grouping_variable, sample = "sample", group = "group") }) output$level <- renderUI({ req(input$selected_grouping_variable) choices <- as.list(unique(data[[input$selected_grouping_variable]])) pickerInput(inputId = 'selected_grouping_variable_level', label = 'Select certain condition:', choices = choices, selected=choices[[1]], multiple = TRUE, options = list(`style` = "btn-success")) }) selected_variable_capt <- shiny::reactive({ req(input$selected_grouping_variable) switch(input$selected_grouping_variable, sample = "sample", group = "group") }) # 基于共享状态过滤数据,不再直接用input$xxx filtered_data <- shiny::reactive({ req(selected_gene()) data %>% dplyr::filter(gene == as.character(selected_gene())) }) mydata <- reactive({ req(input$selected_grouping_variable_level) if (input$show_comment2){ df <- filtered_data() %>% mutate(newvar = !!sym(input$selected_grouping_variable)) %>% dplyr::filter(newvar %in% input$selected_grouping_variable_level) %>% select(-newvar) }else df <- filtered_data() df }) # plot output$distribution_plot <- renderPlotly({ req(mydata(),selected_variable_plot()) distr_plot <- plot_distr(data = mydata(), groupby = selected_variable_plot()) distr_plot <- ggplotly(distr_plot) return(distr_plot) }) # caption whichcaption <- reactive(input$selected_grouping_variable) which_caption <- reactive({ req(whichcaption()) if (whichcaption()=="sample") { "I'm a Barbie girl, in a Barbie world" } else { "Life in plastic is fantastic!" } }) # display comments or do not observe({ toggle(id = "text_div", condition = input$show_comment) output$textofinterest <- renderText({ which_caption() }) }) } # Run the app ---- shinyApp(ui = ui, server = server)
关键修改说明
- UI部分:将两个基因选择框的ID改为
list_of_genes1和list_of_genes2,避免重复ID冲突 - 共享状态:用
selected_gene <- reactiveVal(gene_labels[1])创建全局共享的选中基因状态 - 同步逻辑:通过
observeEvent监听两个选择框的变化,相互同步选中值,并更新共享状态 - 数据依赖:所有依赖基因选择的逻辑(如
filtered_data)都使用selected_gene(),确保两个标签页使用同一数据源
内容的提问来源于stack exchange,提问作者ramen
相关产品推荐
相关产品推荐

