Shiny应用:一个通过renderUI条件渲染的下拉选择框,即使条件不再成立也仍然保留

编程语言 2026-07-09

我正在做一个Shiny应用,里面有多个selectInput控件,其中一个是基于其他选择的条件性显示。一个“演示用的”版本的应用:

# Packages and data: ----
library(shiny)
library(bslib)

samlet_liste <-
  list(
    "termin1" = list("variant1" = list("model1" = "1", "model2" = "2", "model3" = "3"),
                     "variant2" = list("model1" = "1", "model2" = "2", "model3" = "3")),
    "termin2" = list("variant1" = list("model1" = "1", "model2" = "2", "model3" = "3"),
                     "variant2" = list("model1" = "1", "model2" = "2", "model3" = "3"))
  )

# UI: ----
ui <- fluidPage(

  theme = bs_theme(version = 5, bootswatch = "cerulean"),

  sidebarLayout(
    sidebarPanel(
      width = 3,
      selectInput("termin", "Termin:", names(samlet_liste)),
      selectInput("variant", "Variant:", choices = NULL),
      selectInput("model", "Model:", choices = NULL),
      uiOutput("conditional_input")
    ),

    mainPanel(

    )
  )
)


# Server: ----
server <- function(input, output, session){

  # Resets variant and model when termin is changed:
  observeEvent(input$termin, {
    req(input$termin)

    updateSelectInput(
      session,
      "variant",
      choices = names(samlet_liste[[input$termin]]),
      selected = character(0)
    )

    updateSelectInput(
      session,
      "model",
      choices = NULL,
      selected = character(0)
    )
  })

  # Resets model when variant is changed:
  observeEvent(input$variant, {
      req(input$variant)

      updateSelectInput(
        session,
        "model",
        choices = names(samlet_liste[[input$termin]][[input$variant]]),
        selected = character(0)
      )
    }
  )

  # Makes conditional selectInput:
  output$conditional_input <- renderUI({
    req(input$variant)

    if (input$variant == c("variant1")) {
      selectInput("cond", "Conditional:", choices = c("none", "cond1", "cond2"))
    } else {
      NULL
    }
  })

}



shinyApp(ui = ui, server = server)

初次运行应用时,使用renderUI生成的条件性selectInput不会出现,只有在我选择variant > “variant1” 之后才会出现。这符合预期。

不过如果我现在将termin从 “termin1” 更新到 “termin2”,variant的选择将被清空,但条件性selectInput仍然存在。

为什么在variant被清空时,条件性selectInput仍然存在?

解决方案

我相信我已经找到了一个解决办法,但不确定这是一个好办法,还是可能引入其他不可预见的问题。如果有人知道更好的做法(所谓的最佳实践),我也很愿意听取意见。

问题似乎在于,尽管variant已被清空,但renderUI的条件仍然先被检查(在variant被清空之前)。freezeReactiveValue函数解决了这个问题——我并不完全清楚原因。

解决方案:

# Packages and data: ----
library(shiny)
library(bslib)

samlet_liste <-
  list(
    "termin1" = list("variant1" = list("model1" = "1", "model2" = "2", "model3" = "3"),
                     "variant2" = list("model1" = "1", "model2" = "2", "model3" = "3")),
    "termin2" = list("variant1" = list("model1" = "1", "model2" = "2", "model3" = "3"),
                     "variant2" = list("model1" = "1", "model2" = "2", "model3" = "3"))
  )

# UI: ----
ui <- fluidPage(
  theme = bs_theme(version = 5, bootswatch = "cerulean"),

  sidebarLayout(
    sidebarPanel(
      width = 3,
      selectInput("termin", "Termin:", choices = names(samlet_liste)),
      selectInput("variant", "Variant:", choices = NULL),
      selectInput("model", "Model:", choices = NULL),
      uiOutput("conditional_input")
    ),
    mainPanel()
  )
)

# Server: ----
server <- function(input, output, session){

  # Resets variant and model when termin is changed:
  observeEvent(input$termin, {
    req(input$termin)

    # Freezing these inputs so downstream elements don't read stale values
    freezeReactiveValue(input, "variant")
    freezeReactiveValue(input, "model")

    updateSelectInput(
      session,
      "variant",
      choices = names(samlet_liste[[input$termin]]),
      selected = character(0)
    )

    updateSelectInput(
      session,
      "model",
      choices = NULL,
      selected = character(0)
    )
  })

  # Updates model when variant is changed:
  observeEvent(input$variant, {
    req(input$variant)

    freezeReactiveValue(input, "model")

    updateSelectInput(
      session,
      "model",
      choices = names(samlet_liste[[input$termin]][[input$variant]]),
      selected = character(0)
    )
  })

  # Makes conditional selectInput:
  output$conditional_input <- renderUI({
    req(input$variant)

    if (input$variant == "variant1") {
      selectInput("cond", "Conditional:", choices = c("none", "cond1", "cond2"))
    } else {
      NULL
    }
  })
}

shinyApp(ui = ui, server = server)

备选方案

la i laps在原始问题的评论中提出了一个更好的解决方案。

他的解决方案(不使用freezeReactiveValue):

# Packages and data: ----
library(shiny)
library(bslib)

samlet_liste <-
  list(
    "termin1" = list("variant1" = list("model1" = "1", "model2" = "2", "model3" = "3"),
                     "variant2" = list("model1" = "1", "model2" = "2", "model3" = "3")),
    "termin2" = list("variant1" = list("model1" = "1", "model2" = "2", "model3" = "3"),
                     "variant2" = list("model1" = "1", "model2" = "2", "model3" = "3"))
  )

# UI: ----
ui <- fluidPage(

  theme = bs_theme(version = 5, bootswatch = "cerulean"),

  sidebarLayout(
    sidebarPanel(
      width = 3,
      selectInput("termin", "Termin:", names(samlet_liste)),
      selectInput("variant", "Variant:", choices = NULL),
      selectInput("model", "Model:", choices = NULL),
      uiOutput("conditional_input")
    ),

    mainPanel(

    )
  )
)


# Server: ----
server <- function(input, output, session){

  # Resets variant and model when termin is changed:
  observeEvent(input$termin, {
    req(input$termin)

    updateSelectInput(
      session,
      "variant",
      choices = c(" ", names(samlet_liste[[input$termin]])),
      selected = " "
    )

    updateSelectInput(
      session,
      "model",
      choices = c(" "),
      selected = " "
    )
  })

  # Resets model when variant is changed:
  observeEvent(input$variant, {
    req(input$variant)

    updateSelectInput(
      session,
      "model",
      choices = c(" ", names(samlet_liste[[input$termin]][[input$variant]])),
      selected = " "
    )
  }
  )

  # Makes conditional selectInput:
  output$conditional_input <- renderUI({
    req(input$variant)

    if (input$variant == c("variant1")) {
      selectInput("cond", "Conditional:", choices = c("none", "cond1", "cond2"))
    } else {
      NULL
    }
  })

}

shinyApp(ui = ui, server = server)
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章