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

Shiny中如何在多个conditionalPanel间同步保留选择状态?

解决Shiny标签页间基因选择框同步问题

问题根源

Shiny禁止UI组件使用重复ID,而使用不同ID的选择框默认不会共享选中状态,导致切换标签页后选择重置。解决核心是维护一个全局共享的基因选择状态,同步两个标签页的选择框。

解决方案步骤

  1. 为两个标签页的基因选择框分配不同ID(list_of_genes1和list_of_genes2)
  2. 创建reactiveVal()存储当前选中的基因,作为唯一数据源
  3. 监听两个选择框的变化,相互同步选中值
  4. 所有依赖基因选择的逻辑(数据过滤、绘图)都基于这个共享状态

修改后的完整代码

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)

关键修改说明

  1. UI部分:将两个基因选择框的ID改为list_of_genes1和list_of_genes2,避免重复ID冲突
  2. 共享状态:用selected_gene <- reactiveVal(gene_labels[1])创建全局共享的选中基因状态
  3. 同步逻辑:通过observeEvent监听两个选择框的变化,相互同步选中值,并更新共享状态
  4. 数据依赖:所有依赖基因选择的逻辑(如filtered_data)都使用selected_gene(),确保两个标签页使用同一数据源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 13:50:19