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

如何在Golem框架的Shiny模块间传递selectInput响应式数据

解决Shiny模块间通信问题(Golem框架)

问题分析

原代码的核心问题集中在模块通信逻辑和调用方式上:

  • 数据选择模块的Server未返回需要共享的响应式对象,导致下游模块无法获取数据
  • 绘图模块错误地在内部重复初始化数据选择模块,破坏了模块独立性和通信链路
  • 主Server中模块调用方式错误,未正确传递响应式数据
  • UI存在笔误:mod_HydroSurv_ui应为mod_SAR_plot_ui

核心解决思路

Shiny模块间通信的标准模式:

  • 上游模块(数据选择)在Server函数中返回需要共享的响应式对象(如过滤后的数据、选择状态)
  • 下游模块(绘图)通过参数接收这些响应式对象,直接使用
  • 主App Server作为中间层,协调模块初始化和数据传递

修正后的完整代码

library(tidyverse)
library(shiny)
library(shinyWidgets)
library(shinydashboard)
library(plotly)

# 生成示例数据
data <- expand.grid(
  year = 1993:2005,
  species = c("Chinook", "Steelhead"),
  rear_type = c("Hatchery-origin", "Natural-origin"),
  doy = 90:180
) %>%
  mutate(SAR = rnorm(n(), mean = .05, sd = .01))

# 数据选择模块 UI
mod_dataselect_ui <- function(id){
  ns <- NS(id)
  tagList(
    selectInput(
      inputId = ns("select_spp"),
      label = "Select species",
      choices = c("Chinook", "Steelhead"),
      selected = unique(data$species),
      width = "200px",
      multiple = TRUE
    ),
    selectInput(
      inputId = ns("select_rear"),
      label = "Select rearing type",
      choices = c("Hatchery-origin", "Natural-origin"),
      selected = unique(data$rear_type),
      width = "200px",
      multiple = TRUE
    ),
    selectInput(
      inputId = ns("year_display"),
      label = "View by",
      choices = c("All Years", "Year"),
      selected = "All Years"
    ),
    uiOutput(ns("year_picker"))
  )
}

# 数据选择模块 Server - 返回需要共享的响应式对象
mod_dataselect_server <- function(id){
  moduleServer(id, function(input, output, session){
    ns <- session$ns
    
    # 渲染年份选择器
    output$year_picker <- renderUI({
      if (input$year_display == "Year") {
        pickerInput(
          inputId = ns("select_years"),
          label = "Select Year(s)",
          choices = unique(data$year),
          selected = 2000,
          options = list(`actions-box` = TRUE),
          multiple = TRUE
        )
      } else {
        NULL
      }
    })
    
    # 共享的响应式:年份显示模式
    year_display <- reactive(input$year_display)
    
    # 共享的响应式:过滤后的数据
    filtered_data <- reactive({
      req(input$select_spp, input$select_rear)
      
      base_filter <- data %>%
        filter(
          species %in% input$select_spp,
          rear_type %in% input$select_rear
        )
      
      if (input$year_display == "Year") {
        req(input$select_years)
        base_filter %>% filter(year %in% input$select_years)
      } else {
        base_filter
      }
    })
    
    # 返回需要传递给下游模块的对象
    list(
      filtered_data = filtered_data,
      year_display = year_display
    )
  })
}

# 绘图模块 UI
mod_SAR_plot_ui <- function(id){
  ns <- NS(id)
  tagList(
    plotlyOutput(outputId = ns("SAR_plot"))
  )
}

# 绘图模块 Server - 通过参数接收上游模块的响应式数据
mod_SAR_plot_server <- function(id, filtered_data, year_display){
  moduleServer(id, function(input, output, session){
    ns <- session$ns
    
    output$SAR_plot <- renderPlotly({
      req(filtered_data())
      
      p <- ggplot(filtered_data(), aes(x = doy, y = SAR)) +
        geom_point()
      
      # 根据年份显示模式调整分面
      if (year_display() == "All Years") {
        p <- p + facet_grid(rear_type ~ species)
      } else {
        p <- p + facet_grid(rear_type ~ year + species)
      }
      
      ggplotly(p)
    })
  })
}

# 主 UI
app_ui <- function(request) {
  tagList(
    fluidPage(
      dashboardPage(
        dashboardSidebar(
          sidebarMenu(
            id = "tabs",
            menuItem("Plots", tabName = "figs", icon = icon("chart-line")),
            div(
              id = "tabs_filter",
              conditionalPanel(condition = "input.tabs == 'figs'", mod_dataselect_ui("dataselect_1"))
            )
          )
        ),
        dashboardBody(
          tabItems(
            tabItem(tabName = "figs", mod_SAR_plot_ui("SAR_plot_1"))
          )
        )
      )
    )
  )
}

# 主 Server - 协调模块间数据传递
app_server <- function(input, output, session) {
  # 初始化数据选择模块,获取返回的响应式对象
  dataselect_output <- mod_dataselect_server("dataselect_1")
  
  # 将数据传递给绘图模块
  mod_SAR_plot_server(
    "SAR_plot_1",
    filtered_data = dataselect_output$filtered_data,
    year_display = dataselect_output$year_display
  )
  
  # 如果有表格模块,同理传递数据
  # mod_SAR_table_server("SAR_table_1", filtered_data = dataselect_output$filtered_data)
}

# 运行应用
shinyApp(app_ui, app_server)

关键修改点说明

  1. 数据选择模块Server:

    • 添加req()函数确保输入有效后再执行过滤,避免空值报错
    • 明确返回包含filtered_data和year_display的列表,供下游模块调用
  2. 绘图模块Server:

    • 移除内部调用mod_dataselect_server的代码,改为通过参数接收响应式数据
    • 使用req(filtered_data())确保数据加载完成后再绘图
  3. 主Server:

    • 先初始化数据选择模块,获取其返回的响应式对象
    • 将这些对象作为参数传递给绘图模块,实现模块间通信
  4. UI修正:

    • 把错误的mod_HydroSurv_ui改为mod_SAR_plot_ui,匹配绘图模块

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 17:20:59