【问题标题】:downloadHandler: Set reactiveValues inside content functiondownloadHandler:在内容函数中设置 reactiveValues
【发布时间】:2018-01-09 12:46:23
【问题描述】:

是否可以在 downloadHandler 的内容部分中设置 reactiveValues?我试过了,不明白这种行为。

一个简单的例子可以是一个显示下载按钮被点击频率的计数器:

library(shiny)
ui <- fluidPage(
  downloadButton("downloadData", "Download"),
  textOutput("nDownloads"),
  actionButton("trig", "get number")
)

server <- function(input, output) {
  # Our dataset
  data <- mtcars
  r.nDownloads <- reactiveValues(n=0)
  output$nDownloads <- renderText({
    input$trig
    paste("number of downloads:", r.nDownloads$n)
  })

  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      r.nDownloads$n <- r.nDownloads$n + 1
      write.csv(data, file)
    }
  )
}

shinyApp(ui, server)

如果单击下载按钮,则 textOutput 显示为灰色,但未更新。我添加了一个操作按钮作为触发器来强制更新 renderText。令人惊讶的是(至少对我而言)这有效:显示了正确的数字。

所以,由于某种原因,reactiveValue 被 downloadHandler 改变了,但是它的依赖只是失效了,而不是更新了。

当然,正确的方法是让“数据”对象反应并在那里进行计数。但我很好奇如何解释所描述的行为。

编辑:

好的,现在我真的很困惑:我尝试了上面提到的方法:使“数据”具有反应性并在那里进行计数。这不再是简单的下载计数,因为只有在无效时才会重新计算数据反应。

这是一个示例,其中包含用于“数据”无效的附加输入:

library(shiny)

ui <- fluidPage(
  numericInput("nRows", label = "nRows", min=1, max=32, value=15),
  downloadButton("downloadData", "Download"),
  textOutput("nDownloads")
)

server <- function(input, output) {
  r.nDownloads <- reactiveValues(n=0)
  # Our dataset
  data <- reactive({
    isolate({
      r.nDownloads$n <- r.nDownloads$n + 1
    })
    mtcars[1:input$nRows,]
  })
  output$nDownloads <- renderText({
    paste("number of downloads:", r.nDownloads$n)
  })

  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      write.csv(data(), file)
    }
  )
}

shinyApp(ui, server)

但是,我仍然看到类似的行为:单击下载按钮会使文本变灰,更改“nRows”会产生预期的下载次数(现在 nRows 更改后的下载次数;- )) 出现。

现在我遇到了一个实际问题:在我的真实应用程序中,可以下载一个相当复杂的 Excel 文件。在准备和格式化 Excel 文件时,可能会发生一些事件,这些事件会导致应用程序做出某些反应。这就是为什么下载应该触发一些东西。我可以看到的替代方法是,在用户点击下载之前准备 Excel 文件(我想避免这种情况,因为这可能需要几秒钟,具体取决于文件/格式的复杂性) .

我是否遗漏了一些明显的东西?如果没有,我会很感激任何想法,下载事件如何触发应用程序的其余部分。

【问题讨论】:

    标签: r shiny


    【解决方案1】:

    解决方案是删除reactiveValues 的隔离,因为这会阻止计数器在numericInput 被触发之前被更新。这是因为data() 依赖于input$nrows

    library(shiny)
    
    ui <- fluidPage(
      numericInput("nRows", label = "nRows", min = 1, max = 32, value = 15),
      downloadButton("downloadData", "Download"),
      textOutput("nDownloads")
    )
    
    server <- function(input, output) {
      r.nDownloads <- reactiveValues(n = 0)
      # Our dataset
      data <- reactive({
        r.nDownloads$n <- r.nDownloads$n + 1
        mtcars[1:input$nRows,]
      })
    
      output$nDownloads <- renderText({
        paste("number of downloads:", r.nDownloads$n)
      })
    
      output$downloadData <- downloadHandler(
        filename = function() {
          paste("data-", Sys.Date(), ".csv", sep = "")
        },
        content = function(file) {
          write.csv(data(), file)
        }
      )
    }
    
    shinyApp(ui, server)
    

    关于更深层次的问题,如果不能保证用户会下载该文件,则不断地准备一个复杂的 Excel 文件将是低效的。你可以尝试做的是:

    1. 将您的数据保存在reactive 方法中(例如data())。
    2. 编写一个方法来准备您的数据以供下载(例如prepExcel(data)),该方法会返回您准备好的数据。
    3. 将 (1) 和 (2) 传递到 downloadHandler() 的内容中,如下所示:write_xx(prepExcel(data())) 或将数据通过管道传递到 write_xx 函数中,如下所示 data() %&gt;% prepExcel() %&gt;% write_xx() 其中xx 是用于输出的方法你的最终文件,例如write_xlsxwrite_csv 等。

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-18
      • 2020-04-20
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多