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

Shiny应用reactivity问题求助:多控件联动与控件显示控制

Shiny应用问题解决方案

问题描述

我正在开发一个Shiny应用,目前仅能实现pickerInput控件(StageBox)的响应性,需要解决以下问题:

  • 当selectInput(type)选择非LifeStage选项时,隐藏StageBox,并根据所选type在地图上展示对应点;
  • 实现sliderInput控件根据selectInput的选择更新地图。

附尝试的示例代码:

library(shiny)
library(shinydashboard)
library(mapview)
library(tidyverse)
library(leaflet)
library(readxl)
library(sf)
library(shinyWidgets)
library(shinythemes)
library(scales)
library(shinyjs)
library(shinycssloaders)

type <- c("LifeStage", "Survey", "ReleaseType")
lifestage <- c("Adult", "Larvae", "Juvenile", "Post-Larvae")

ds <- structure(list(SampleDate = structure(c(14959, 14960, 14978, 
14992, 15358, 15369), class = "Date"), Survey = c("Morning", 
"MidDay", "Night", "Morning", "Morning", "MidDay"), LifeStage = c("Adult", 
"Post-Larvae", "Juvenile", "Adult", "Larvae", "Adult"), lat = c(38.11429, 
38.07435, 38.152333, 38.17354, 38.047967, 38.07868), lon = c(-121.6875, 
-121.7623, -121.684833, -121.94784, -121.909917, -121.75566), 
    ReleaseEvent = c("BY2010", "BY2010", "BY2011", "BY2011", 
    "BY2012", "BY2012"), ReleaseMethod = c("Nice", "No Nice", 
    "Full", "Medium", "No Nice", "Full"), year = c(2010, 2010, 
    2011, 2011, 2012, 2012)), row.names = c(NA, -6L), class = c("tbl_df", 
"tbl", "data.frame"))  

ds$SampleDate <- as.Date(ds$SampleDate,"%m/%d/%Y")
# make ds sf object: 
ds_sf <- st_as_sf(ds,coords = c(5, 4), remove = F, crs = 4326) 
st_crs(ds_sf)
colors <- colorRampPalette(c('yellow', 'red', 'blue', 'green'))#use colorRampPalette so 
map points and legend points match same color


ui <- fluidPage(
  useShinyjs(), 
    sidebarLayout(
    sidebarPanel(width=3,
                 h3("Select Options"),
                 selectInput(inputId = "type",
                             label = "Type",
                             choices = type,
                             selected = "LifeStage"),
                 
                 pickerInput(inputId = "lstages",
                             label = "StageBox",
                             choices = lifestage,
                             selected = "Adult",
                             options = list(`actions-box` = TRUE),
                             multiple = TRUE),
                 
                 selectInput(inputId = "event",
                             label = "Event",
                             choices = ds$ReleaseEvent),

                 sliderInput(inputId =  "Yearslider",
                             label = "Years to plot",
                             sep = "",
                             min = 2010, 
                             max = 2012,
                             step = 1,
                             value = 2011)),
               mainPanel(leafletOutput("map",width = "100%", height="87vh")) 
              )
             )

server <- function(input, output, session) {
  session$onSessionEnded(function() {
    stopApp()
  })
  
  output$map <- renderLeaflet({
    req(input$lstages)  
    final <- mapview(ds_sf[ds_sf$LifeStage %in% input$lstages, ],
                     zcol = "LifeStage",
                     col.regions = colors, 
                     layer.name = "LifeStage", 
                     alpha = 0.2, cex = 4)
    final@map
    })
  }
shinyApp(ui, server)

修改方案

1. 控制StageBox的显示与隐藏

通过shinyjs的toggle函数监听input$type变化,仅当选择LifeStage时显示StageBox:

observe({
  toggle("lstages", condition = input$type == "LifeStage")
})

2. 动态更新筛选控件选项

切换type时,自动更新筛选控件(原event控件)的选项为对应分类的唯一值,并控制其显示状态:

observe({
  choices <- switch(input$type,
                    "LifeStage" = NULL,
                    "Survey" = unique(ds$Survey),
                    "ReleaseType" = unique(ds$ReleaseMethod))
  updateSelectInput(session, "event", 
                    label = paste("Filter by", input$type),
                    choices = choices)
  toggle("event", condition = input$type != "LifeStage")
})

3. 反应式数据过滤

创建反应式对象,结合年份滑块、当前type及对应筛选条件,动态过滤数据集:

filtered_data <- reactive({
  req(input$type, input$Yearslider)
  
  # 先按年份过滤
  data <- ds_sf[ds_sf$year == input$Yearslider, ]
  
  # 根据type选择对应过滤逻辑
  switch(input$type,
         "LifeStage" = {
           req(input$lstages)
           data[data$LifeStage %in% input$lstages, ]
         },
         "Survey" = {
           req(input$event)
           data[data$Survey == input$event, ]
         },
         "ReleaseType" = {
           req(input$event)
           data[data$ReleaseMethod == input$event, ]
         })
})

4. 基于过滤数据渲染地图

修改地图渲染逻辑,使用反应式过滤数据,并根据type自动匹配颜色分类列和图层名称:

output$map <- renderLeaflet({
  req(filtered_data())
  data <- filtered_data()
  
  zcol_val <- switch(input$type,
                     "LifeStage" = "LifeStage",
                     "Survey" = "Survey",
                     "ReleaseType" = "ReleaseMethod")
  
  layer_name_val <- input$type
  
  final <- mapview(data,
                   zcol = zcol_val,
                   col.regions = colors, 
                   layer.name = layer_name_val, 
                   alpha = 0.2, cex = 4)
  final@map
})

完整修改后代码

library(shiny)
library(shinydashboard)
library(mapview)
library(tidyverse)
library(leaflet)
library(readxl)
library(sf)
library(shinyWidgets)
library(shinythemes)
library(scales)
library(shinyjs)
library(shinycssloaders)

type <- c("LifeStage", "Survey", "ReleaseType")
lifestage <- c("Adult", "Larvae", "Juvenile", "Post-Larvae")

ds <- structure(list(SampleDate = structure(c(14959, 14960, 14978, 
14992, 15358, 15369), class = "Date"), Survey = c("Morning", 
"MidDay", "Night", "Morning", "Morning", "MidDay"), LifeStage = c("Adult", 
"Post-Larvae", "Juvenile", "Adult", "Larvae", "Adult"), lat = c(38.11429, 
38.07435, 38.152333, 38.17354, 38.047967, 38.07868), lon = c(-121.6875, 
-121.7623, -121.684833, -121.94784, -121.909917, -121.75566), 
    ReleaseEvent = c("BY2010", "BY2010", "BY2011", "BY2011", 
    "BY2012", "BY2012"), ReleaseMethod = c("Nice", "No Nice", 
    "Full", "Medium", "No Nice", "Full"), year = c(2010, 2010, 
    2011, 2011, 2012, 2012)), row.names = c(NA, -6L), class = c("tbl_df", 
"tbl", "data.frame"))  

ds$SampleDate <- as.Date(ds$SampleDate,"%m/%d/%Y")
# make ds sf object: 
ds_sf <- st_as_sf(ds,coords = c(5, 4), remove = F, crs = 4326) 
st_crs(ds_sf)
colors <- colorRampPalette(c('yellow', 'red', 'blue', 'green'))#use colorRampPalette so 
map points and legend points match same color


ui <- fluidPage(
  useShinyjs(), 
    sidebarLayout(
    sidebarPanel(width=3,
                 h3("Select Options"),
                 selectInput(inputId = "type",
                             label = "Type",
                             choices = type,
                             selected = "LifeStage"),
                 
                 pickerInput(inputId = "lstages",
                             label = "StageBox",
                             choices = lifestage,
                             selected = "Adult",
                             options = list(`actions-box` = TRUE),
                             multiple = TRUE),
                 
                 selectInput(inputId = "event",
                             label = "Filter by",
                             choices = NULL),

                 sliderInput(inputId =  "Yearslider",
                             label = "Years to plot",
                             sep = "",
                             min = 2010, 
                             max = 2012,
                             step = 1,
                             value = 2011)),
               mainPanel(leafletOutput("map",width = "100%", height="87vh")) 
              )
             )

server <- function(input, output, session) {
  session$onSessionEnded(function() {
    stopApp()
  })
  
  # 控制StageBox的显示/隐藏
  observe({
    toggle("lstages", condition = input$type == "LifeStage")
  })
  
  # 动态更新筛选控件的选项和显示状态
  observe({
    choices <- switch(input$type,
                      "LifeStage" = NULL,
                      "Survey" = unique(ds$Survey),
                      "ReleaseType" = unique(ds$ReleaseMethod))
    updateSelectInput(session, "event", 
                      label = paste("Filter by", input$type),
                      choices = choices)
    toggle("event", condition = input$type != "LifeStage")
  })
  
  # 反应式过滤数据
  filtered_data <- reactive({
    req(input$type, input$Yearslider)
    
    # 先按年份过滤
    data <- ds_sf[ds_sf$year == input$Yearslider, ]
    
    # 根据type选择过滤逻辑
    switch(input$type,
           "LifeStage" = {
             req(input$lstages)
             data[data$LifeStage %in% input$lstages, ]
           },
           "Survey" = {
             req(input$event)
             data[data$Survey == input$event, ]
           },
           "ReleaseType" = {
             req(input$event)
             data[data$ReleaseMethod == input$event, ]
           })
  })
  
  # 渲染地图
  output$map <- renderLeaflet({
    req(filtered_data())
    data <- filtered_data()
    
    zcol_val <- switch(input$type,
                       "LifeStage" = "LifeStage",
                       "Survey" = "Survey",
                       "ReleaseType" = "ReleaseMethod")
    
    layer_name_val <- input$type
    
    final <- mapview(data,
                     zcol = zcol_val,
                     col.regions = colors, 
                     layer.name = layer_name_val, 
                     alpha = 0.2, cex = 4)
    final@map
  })
}
shinyApp(ui, server)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 04:25:57