r - Multiple inputs to reactive value R Shiny
问题描述
For part of a Shiny application I am building, I need to have the user select a directory. The directory path is stored in a reactive variable. The directory can either be selected by the user from a file window or the path can be manually entered by textInput
. I have figured out how to do this, but I don't understand why the solution I have works! A minimal example of the working app:
library(shiny)
ui <- fluidPage(
actionButton("button1", "First Button"),
textInput("inText", "Input Text"),
actionButton("button2", "Second Button"),
textOutput("outText"),
textOutput("outFiles")
)
server <- function(input, output) {
values <- reactiveValues(inDir = NULL)
observeEvent(input$button1, {values$inDir <- tcltk::tk_choose.dir()})
observeEvent(input$button2, {values$inDir <- input$inText})
inPath <- eventReactive(values$inDir, {values$inDir})
output$outText <- renderText(inPath())
fileList <- reactive(list.files(path=inPath()))
output$outFiles <- renderPrint(fileList())
}
shinyApp(ui, server)
The first thing I tried was to just use eventReactive
and assign the two sources of input to the reactive variable:
server <- function(input, output) {
inPath <- eventReactive(input$button1, {tcltk::tk_choose.dir()})
inPath <- eventReactive(input$button2, {input$inText})
output$outText <- renderText(inPath())
fileList <- reactive(list.files(path=inPath()))
output$outFiles <- renderPrint(fileList())
}
The effect of this as far as I can tell is that only one of the buttons does anything. What I don't really understand is why this doesn't work. What I thought would happen is that the first button pushed would create inPath
and then subsequent pushes would update the value and trigger updates to dependent values (here output$outText
). What exactly is happening here then?
The second thing I tried, which was almost there, was based off of this answer:
server <- function(input, output) {
values <- reactiveValues(inDir = NULL)
observeEvent(input$button1, {values$inDir <- tcltk::tk_choose.dir()})
observeEvent(input$button2, {values$inDir <- input$inText})
inPath <- reactive({if(is.null(values$inDir)) return()
values$inDir})
output$outText <- renderText(inPath())
fileList <- reactive(list.files(path=inPath()))
output$outFiles <- renderPrint(fileList())
}
This works correctly except that it shows an "Error: invalid 'path' argument" message for list.files
. I think this may mean that fileList
is being evaluated with inPath = NULL
. Why does this happen when I use reactive
instead of eventReactive
?
Thanks!
解决方案
您可以摆脱inPath
反应式并直接使用values$inDir
。与req()
您一起等到值可用。否则你会得到同样的错误(invalid 'path' argument)。
reactive
立即触发,而将eventReactive
等待给定事件发生并调用 eventReactive。
并且if(is.null(values$inDir)) return()
无法正常工作,因为如果values$inDir
为 NULL,它将返回 NULL,然后将其传递给list.files
. 并list.files(NULL)
给出错误:invalid 'path' argument
。
将其替换为req(values$inDir)
,您将不会收到该错误。
而你的例子2 inPath
- eventReactive's 将不起作用,因为第一个将被第二个覆盖,所以input$button1
不会触发任何东西。
library(shiny)
ui <- fluidPage(
actionButton("button1", "First Button"),
textInput("inText", "Input Text"),
actionButton("button2", "Second Button"),
textOutput("outText"),
textOutput("outFiles")
)
server <- function(input, output) {
values <- reactiveValues(inDir = NULL)
observeEvent(input$button1, {values$inDir <- tcltk::tk_choose.dir()})
observeEvent(input$button2, {values$inDir <- input$inText})
output$outText <- renderText(values$inDir)
fileList <- reactive({
req(values$inDir);
list.files(path=values$inDir)
})
output$outFiles <- renderPrint(fileList())
}
shinyApp(ui, server)
您也可以使用eventReactive
forbutton1
和observeEvent
for button2
,但请注意,您需要额外observe({ inPath() })
的才能使其工作。我更喜欢上面的解决方案,因为它更清楚发生了什么并且代码更少。
server <- function(input, output) {
values <- reactiveValues(inDir = NULL)
inPath = eventReactive(input$button1, {values$inDir <- tcltk::tk_choose.dir()})
observe({
inPath()
})
observeEvent(input$button2, {values$inDir <- input$inText})
output$outText <- renderText(values$inDir)
fileList <- reactive({
req(values$inDir);
list.files(path=values$inDir)
})
output$outFiles <- renderPrint(fileList())
}
为了说明为什么if(is.null(values$inDir)) return()
不起作用,请考虑以下函数:
test <- function() { if (TRUE) return() }
test()
尽管if 条件的计算结果为TRUE,但仍然会有一个返回值(在本例中为NULL),它将传递给以下函数,在您的情况下list.files
,这将导致错误。
推荐阅读
- linux - Linux/Golang:无法访问 /sys/kernel/debug/tracing/kprobe_events
- python - 在数据帧上应用 lambda 函数时将数据帧作为参数传递
- kotlin - ArchUnit 不区分大小写的名称匹配
- c# - 计算未执行以计算 c#asp.net sql linq 中 2 个日期之间的总天数差异
- javascript - 使用 Apple REST API 登录时无法获取电子邮件地址或 Apple ID
- safari - Safari ITP 2.3:所有脚本可写网站数据的生命周期上限
- r - “`[[<-`(`*tmp*`, i, value = ) 中的错误:在级别 1 没有这样的索引”将 for 循环结果分配给列表
- python - 在 docker build 中运行 pip install requirements.txt 失败
- c# - 如何在 C#.net 中使用 YOLO 提高对象检测的速度
- python - 关键字参数后的位置参数和 * 参数后的常规参数