在 R shiny 中,如何反转反应链?

In R shiny, how to reverse the reactivity chain?

在下面的 MWE 代码中,第一个输入框默认被渲染和绘制。显示并绘制默认值,用户可以更改它。单击“显示”复选框会呈现第二个输入框,其值链接到第一个输入框,并且在这个第二个输入框中,用户可以选择插入另一个值,然后优先于第一个输入并绘制。 (在完整的 App 中,这是从中提取的,第二个输入框执行其他计算,因此此处显示的内容已简化)。因此,反应链起作用:input1 -> input2,input2 优先于 input1。

但是,我该如何扭转这个反应链?以便 input1 中显示的值发生变化以反映输入到 input2 中的内容?在下面的 MWE 中,我尝试在标记为 << comment/uncomment this line to see 的行中执行此操作,但是当该行未被注释时(允许我尝试解决 运行),即使“显示”复选框也隐藏了第二个输入框对于第二个输入,仍然检查显示。第二个输入框应继续显示,直到取消选中。

请参见下图。

MWE 代码:

library(shiny)
library(shinyjs)
library(shinyMatrix)

### Checkbox matrix ###
f <- function(action,i){as.character(checkboxInput(paste0(action,i),label=NULL))}
actions       <- c("show", "reset")
tbl           <- t(outer(actions, c(1,2), FUN = Vectorize(f)))
colnames(tbl) <- c("Show", "Reset")
rownames(tbl) <- c("2nd input", "3rd input")
### Checkbox matrix ###

xDflt <- 5
userInput <- function(inputId,x,y){
  matrixInput(inputId, 
              value = matrix(c(x), 1, 1, dimnames = list(c(y),NULL)),
              rows =  list(extend = FALSE, names = TRUE),
              cols =  list(extend = FALSE, names = FALSE, editableNames = FALSE),
              class = "numeric")}

ui <- fluidPage(
  tags$head(
    tags$style(HTML(
      "td .checkbox {margin-top: 0; margin-bottom: 0;}
       td .form-group {margin-bottom: 0;}"
    ))
  ),
  br(),
  sidebarLayout(
    sidebarPanel(
      uiOutput("panel"),
      hidden(uiOutput("secondInput")),
    ),
    mainPanel(plotOutput("plot1"))
  )
)

server <- function(input, output){
  
  input1      <- reactive(input$input1)
  input2      <- reactive(input$input2)
  
  output$panel <- renderUI({
    tagList(
      useShinyjs(),
      userInput("input1",
                # if(isTruthy(input2())){input$input2[1,1]} else # << comment/uncomment this line to see
                  {xDflt},
                "1st input"),
      strong(helpText("Generate curves (Y|X):")),
      tableOutput("checkboxes") 
    )
  })
  
  ### Begin checkbox matrix ###
  output[["checkboxes"]] <- 
    renderTable({tbl}, 
      rownames = TRUE, align = "c",
      sanitize.text.function = function(x) x
    )

  observeEvent(input[["show1"]], {
    if(input[["show1"]] ){
      shinyjs::show("secondInput")
    } else {
      shinyjs::hide("secondInput")
    }
  })
  ### End checkbox matrix ###
 
  output$secondInput <- renderUI({
    req(input1())
    userInput("input2",input$input1[1,1],"2nd input")
  })
  
  outputOptions(output,"secondInput",suspendWhenHidden = FALSE) 
  
  output$plot1 <-renderPlot({
    req(input2())
    plot(rep(input2(),times=5))
  })

}

shinyApp(ui, server)

您的复选框变为未选中状态,因为您将它们与 output$panel 一起渲染和重新渲染。您忘记将 uiOutput("checkboxes") 添加到 UI。以下是步骤:

  1. output$panel <- renderUI() 中删除 tableOutput("checkboxes")
  2. uiOutput("checkboxes"), 添加到 UI。
  3. 取消注释行 if(isTruthy(input2() ...

这应该可以解决问题。

关于代码的一些评论。我发现它不必要地复杂。例如。您在 userInput 函数中使用 c(x) 是不必要的。您将 input$input1 包装在一个反应​​式表达式中,该表达式不会添加任何内容(可能除了可读性)但会增加反应性链的复杂性。有几种方法可以使代码更易于理解和阅读,这可能有助于避免简单的错误,例如缺少 uiOutput.