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

Shiny模块中多过滤器动态更新失效问题求助

Shiny模块三级过滤器联动问题修复

我们在Shiny应用中用Shiny Modules实现Campus(校区)、Specialty(专科)、Department(科室)的三级联动过滤器,要求后续选项随前置选择动态更新,但切换Campus时,Specialty模块的observer无法触发更新。以下是原代码:

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

data <- data.frame(
  CAMPUS = c("Campus A", "Campus A", "Campus B", "Campus B", "Campus C"),
  SPECIALTY = c("Cardiology", "Neurology", "Cardiology", "Orthopedics", "Oncology"),
  DEPARTMENT = c("Cardiology Department", "Neurology Department", "Cardiology Department", "Orthopedics Department", "Oncology Department")
)


# Define UI for Campus
CampusInput <- function(id, data) {
  
  campus_choices <- data %>% 
    select(CAMPUS) %>% distinct() %>% pull()
  
  box(
    title = "Select Campus:",
    width = 12,
    height = "100px",
    solidHeader = FALSE,
    pickerInput(NS(id, "selectedCampus"),
                label=NULL,
                choices=  campus_choices,
                multiple=TRUE,
                selected = campus_choices[1]))
}



# Define Server for Campus
CampusServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    reactive({
      input$selectedCampus
    })
  })
}


# Define UI for Specialty
SpecialtyInput <- function(id) {
  
  box(
    title = "Select Specialty:",
    width = 12,
    height = "100px",
    solidHeader = FALSE,
    pickerInput(NS(id,"selectedSpecialty"),
                label=NULL,
                choices= NULL,
                multiple=TRUE,
                selected = NULL))
}


# Define Server for Specialty
SpecialtyServer <- function(id, data, campus) {
  moduleServer(id, function(input, output, session) {
    observeEvent(campus, {
      if(!is.null(campus)) {
        
        print(campus)
        specailty_choices <-  data %>% filter(CAMPUS %in% campus) %>%
          select(SPECIALTY) %>% distinct() %>% pull()
        
        updatePickerInput(session,
                          inputId = id,
                          choices = specailty_choices,
                          selected = specailty_choices)
      }
    })
    
  })
  
}



# Define UI for Department
DepartmentInput <- function(id) {
  box(
    title = "Select Department:",
    width = 12,
    height = "100px",
    solidHeader = FALSE,
    pickerInput(NS(id,"selectedDepartment"),
                label=NULL,
                choices= NULL,
                multiple=TRUE,
                selected = NULL))
}


# Define Server for Department
DepartmentServer <- function(id, data, campus, specialty) {
  moduleServer(id, function(input, output, session) {
    observeEvent(specialty, {
      if(!is.null(specialty)) {
        
        department_choices <-  data %>% filter(CAMPUS %in% campus, CAMPUS_SPECIALTY %in% specialty ) %>%
          select(DEPARTMENT) %>% distinct() %>% pull()
        
        updatePickerInput(session,
                          inputId = id,
                          choices = department_choices,
                          selected = department_choices)
      }
    })
  })
  
}


#Define UI for the app
ui <- fluidPage(
  CampusInput("selectedCampus", data = data),
  SpecialtyInput("selectedSpecialty"),
  DepartmentInput("selectedDepartment"),
  textOutput("result")
  
)

#Define Server for the app
server <- function(input, output, session) {
  selected_campus <- CampusServer("selectedCampus")
  selected_specialty <- SpecialtyServer("selectedSpecialty", data = data, campus = selected_campus())
  selected_department <- DepartmentServer("selectedDepartment", data = data, 
                                          campus = selected_campus(), specialty = selected_specialty())
  
  output$result <- renderText(selected_department())
}

shinyApp(ui, server)

错误原因分析

  1. Reactive传递错误:调用SpecialtyServer时传入的是selected_campus()(reactive的当前静态值),而非reactive对象本身。observeEvent监听静态值无法触发更新,必须监听reactive对象。
  2. updatePickerInput的inputId错误:模块内的pickerInput实际ID是"selectedSpecialty"(通过NS生成),原代码用id作为inputId,导致无法定位到目标控件。
  3. 模块未返回选中值:SpecialtyServer和DepartmentServer没有返回用户选中值的reactive,后续模块无法获取有效输入。
  4. 数据列名不匹配:DepartmentServer的过滤条件使用了不存在的CAMPUS_SPECIALTY列,实际应为SPECIALTY。

修复后的完整代码

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

data <- data.frame(
  CAMPUS = c("Campus A", "Campus A", "Campus B", "Campus B", "Campus C"),
  SPECIALTY = c("Cardiology", "Neurology", "Cardiology", "Orthopedics", "Oncology"),
  DEPARTMENT = c("Cardiology Department", "Neurology Department", "Cardiology Department", "Orthopedics Department", "Oncology Department")
)

# Campus输入模块UI
CampusInput <- function(id, data) {
  campus_choices <- data %>% 
    select(CAMPUS) %>% distinct() %>% pull()
  
  box(
    title = "Select Campus:",
    width = 12,
    height = "100px",
    solidHeader = FALSE,
    pickerInput(NS(id, "selectedCampus"),
                label=NULL,
                choices= campus_choices,
                multiple=TRUE,
                selected = campus_choices[1])
  )
}

# Campus模块服务器
CampusServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    reactive({
      input$selectedCampus
    })
  })
}

# Specialty输入模块UI
SpecialtyInput <- function(id) {
  box(
    title = "Select Specialty:",
    width = 12,
    height = "100px",
    solidHeader = FALSE,
    pickerInput(NS(id,"selectedSpecialty"),
                label=NULL,
                choices= NULL,
                multiple=TRUE,
                selected = NULL)
  )
}

# Specialty模块服务器
SpecialtyServer <- function(id, data, campus) {
  moduleServer(id, function(input, output, session) {
    # 监听campus reactive对象的变化
    observeEvent(campus(), {
      req(campus())
      
      specialty_choices <- data %>% 
        filter(CAMPUS %in% campus()) %>%
        select(SPECIALTY) %>% distinct() %>% pull()
      
      updatePickerInput(session,
                        inputId = "selectedSpecialty", # 使用模块内的inputId
                        choices = specialty_choices,
                        selected = specialty_choices)
    })
    
    # 返回选中的Specialty reactive
    reactive({
      input$selectedSpecialty
    })
  })
}

# Department输入模块UI
DepartmentInput <- function(id) {
  box(
    title = "Select Department:",
    width = 12,
    height = "100px",
    solidHeader = FALSE,
    pickerInput(NS(id,"selectedDepartment"),
                label=NULL,
                choices= NULL,
                multiple=TRUE,
                selected = NULL)
  )
}

# Department模块服务器
DepartmentServer <- function(id, data, campus, specialty) {
  moduleServer(id, function(input, output, session) {
    # 同时监听campus和specialty的变化
    observeEvent(c(campus(), specialty()), {
      req(campus(), specialty())
      
      department_choices <- data %>% 
        filter(CAMPUS %in% campus(), SPECIALTY %in% specialty()) %>% # 修正列名
        select(DEPARTMENT) %>% distinct() %>% pull()
      
      updatePickerInput(session,
                        inputId = "selectedDepartment", # 使用模块内的inputId
                        choices = department_choices,
                        selected = department_choices)
    }, ignoreNULL = FALSE)
    
    # 返回选中的Department reactive
    reactive({
      input$selectedDepartment
    })
  })
}

# 应用UI
ui <- fluidPage(
  CampusInput("selectedCampus", data = data),
  SpecialtyInput("selectedSpecialty"),
  DepartmentInput("selectedDepartment"),
  textOutput("result")
)

# 应用服务器
server <- function(input, output, session) {
  selected_campus <- CampusServer("selectedCampus")
  # 传递reactive对象而非直接取值
  selected_specialty <- SpecialtyServer("selectedSpecialty", data = data, campus = selected_campus)
  selected_department <- DepartmentServer("selectedDepartment", data = data, 
                                          campus = selected_campus, specialty = selected_specialty)
  
  output$result <- renderText({
    req(selected_department())
    paste("Selected Departments:", paste(selected_department(), collapse = ", "))
  })
}

shinyApp(ui, server)

关键修复点说明

  • Reactive传递规则:模块间传递动态值时,必须传递reactive对象本身(如selected_campus),而非调用selected_campus()取静态值,确保observeEvent能监听到变化。
  • 模块内控件ID:updatePickerInput的inputId需使用模块内部定义的ID(如"selectedSpecialty"),模块内session会自动处理命名空间,无需手动拼接。
  • 返回reactive输出:每个筛选模块的服务器必须返回用户选中值的reactive,供后续模块调用。
  • 多依赖监听:Department选项同时依赖Campus和Specialty,因此observeEvent要监听c(campus(), specialty()),确保任一前置选项变化时都能触发更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:15:32