R Shiny:observeEvent()根据打开的选项卡

时间:2017-09-11 14:53:24

标签: r tabs shiny observers reactive

更新:将示例更改为(希望)不那么抽象的

我有一个使用mtcars数据集的两个标签的闪亮应用程序。 “Displacement”包含一个散点图,显示对mpg的disp,“horsepower”包含一个散点图,显示hp与mpg的关系。两个选项卡都包含一个复选框组输入,允许用户根据柱面数过滤结果。 “生成”按钮生成图形,以便在输入更改后不会立即更改。

每个标签都有两个附加按钮。 “比较”是一个切换按钮,它将一个选项卡的复选框输入中的值复制到另一个选项卡的输入并生成结果图,这样您就可以轻松比较两个选项卡如何查找相同的输入。 “恢复默认值”会将复选框输入重置为其初始值。

这两个按钮的工作方式相同。每个按钮都有自己的观察器,一旦按下按钮就会触发更新功能。此函数首先更新输入,然后通过更新为此目的创建的无功值来生成绘图,然后切换到包含该绘图的选项卡。

但是,只有比较按钮才能正常工作,只需单击即可更新输入并生成图形。另一方面,“恢复默认值”按钮仅更新输入。您需要按两次以更新绘图。

我尝试将“恢复默认值”按钮从一个标签移动到另一个标签,然后确实在第一次点击时更新图表,因此行为会根据受影响的图表是否在与按钮相同的选项卡(下面提供了应用程序,因此您可以自己尝试)。我对此行为感到困惑,因为将按钮链接到更新函数的observeEvent()函数编码相同。按钮位于哪个选项卡中无关紧要。

任何人都可以解释是什么导致这个以及我应该改变什么来使两个按钮一键更新复选框输入和绘图输出?

library(shiny)
library(ggplot2)

ui <- fluidPage(
  tabsetPanel(id="App",
    tabPanel(
      "displacement",
      checkboxGroupInput("cyl1", "Number of cylinders", choices=c("4","6","8"), selected="4"),
      actionButton("genDisp", "Generate"),
      actionButton("switchToHp", "Compare"),
      actionButton("resDefDisp", "Restore Default"),
      plotOutput("disp")
    ),
    tabPanel(
      "horsepower",
      checkboxGroupInput("cyl2", "Number of cylinders", choices=c("4","6","8"), selected="8"),
      actionButton("genHp", "Generate"),
      actionButton("switchToDisp", "Compare"),
      actionButton("resDefHp", "Restore Default"),
      plotOutput("hp")
    )
  )
)

server <- function(input, output, session){

  # reactive values used to trigger plot generation
  generateDisp  <- reactiveVal(0)
  generateHp  <- reactiveVal(0)

  # "Generate" Buttons
  observeEvent({
    input$genDisp
  },
  {
    generateDisp(generateDisp()+1)
  })
  observeEvent({
    input$genHp
  },
  {
    generateHp(generateHp()+1)
  })

  # "Compare"/"Restore Defaults" buttons
  observeEvent({
    input$switchToHp
  },{
    updateHp(cyl=input$cyl1)
  })
  observeEvent({
    input$resDefDisp
  },{
    updateDisp(cyl="4")
  })
  observeEvent({
    input$switchToDisp
  },{
    updateDisp(cyl=input$cyl2)
  })
  observeEvent({
    input$resDefHp
  },{
    updateHp(cyl="8")
  })

  # Functions updating graphs
  updateDisp <- function(cyl=NULL)
  {
    updateCheckboxGroupInput(session, "cyl1", "Number of cylinders", selected = cyl)
    generateDisp(generateDisp()+1)
    updateTabsetPanel(session, "App", selected = "displacement")
  }
  updateHp <- function(cyl=NULL)
  {
    updateCheckboxGroupInput(session, "cyl2", "Number of cylinders", selected = cyl)
    generateHp(generateHp()+1)
    updateTabsetPanel(session, "App", selected = "horsepower")
  }

  # output plots
  output$disp <- renderPlot({
    generateDisp()
    isolate({
      data <- filter(mtcars, cyl %in% input$cyl1) %>%
        mutate(cyl=as.factor(cyl))
      ggplot(data, aes(x=mpg, y=disp, color=cyl)) +
        geom_point()
    })
  })
  output$hp <- renderPlot({
    generateHp()
    isolate({
      data <- filter(mtcars, cyl %in% input$cyl2) %>%
        mutate(cyl=as.factor(cyl))
      ggplot(data, aes(x=mpg, y=hp, color=cyl)) +
        geom_point()
    })
  })

}


shinyApp(ui, server)

1 个答案:

答案 0 :(得分:1)

如果您将代码更改为:

observeEvent({
  c(input$updateTalk1, input$updateTalk2)
}, {
  updateNumericInput(session, "talkNumber", "Choose number", value = 2)
  print(input$talkNumber)
  generateTalk(generateTalk() + 1)
  print(input$talkNumber)

  updateTabsetPanel(session, "App", selected = "talk")
})

在将数字输入值更新为2之后,或者甚至在更新了generateTalk的值之后,您会注意到输入$ talkNumber仍然是触发函数时的值,在您的情况3中,而不是2.所以当renderPrint函数运行时,它仍将使用旧值。我不确定基础工作流程,但我想它只会在当前函数(被动调用)结束时更改为新值。

但是当第一个选项卡不可见时,它会被强制重新渲染,因此它会更改以更新所有UI元素,因此设置为数字输入的新值将由renderPrint函数读取。

顺便说一下,我觉得你的例子有点过于抽象,我想如果你能告诉我们你真正想做什么,那么你现在的方式会有更好的解决方案。

编辑:

我找到了可能有用的this帖子。基本上,update * Input函数只会在observeEvent结束后对UI进行更改,因此generateDisp触发的renderPlot使用旧的输入值。