我有一个
shiny
应用程序,可以在用户单击按钮时生成并保存报告。
我想通过更改按钮的标签和图标来显示按钮中的保存状态。
我使用
downloadButton
来允许用户指定位置和文件名。 该报告是使用 officer
保存的 print(obj, target)
Word 文档。
预期流量为:
实际流程是:
在将状态更改为“已保存”之前,脚本不会等待用户选择文件或查看文件是否正确保存。在这种情况下,文件正确保存,但脚本在打印到目标之前无法知道这一点,并且在用户提供文件名/位置之前无法打印到目标。
如何修改此设置,使状态更改与文件是否正确保存相关?
library(shiny)
library(shinyjs)
library(officer)
ui <- fluidPage(
useShinyjs(), # Initialize shinyjs
uiOutput("save_bullet_reportui")
)
server <- function(input, output, session) {
values <- reactiveValues(
bullet_report_status = "save"
)
observeEvent(values$bullet_report_status, {
status <- isolate(values$bullet_report_status)
print(paste0("status changed to: ", status))
output$save_bullet_reportui <- renderUI({
if(status == "save"){
downloadButton("save_bullet_report", "Save to .docx", icon = icon("file-word"))
}else if(status == "saving"){
downloadButton("save_bullet_report", "Saving...",
icon = icon("spinner", class = "fa-spin"))
}else if(status == "success"){
downloadButton("save_bullet_report", "Saved",
icon = icon("check", style = "color: #00AA00"))
}else if(status == "failed"){
downloadButton("save_bullet_report", "Save Failed",
icon = icon("exclamation-triangle", style = "color: #AA0000"))
}
})
})
output$save_bullet_report <- downloadHandler(
filename = function(){
paste0("test_report_", Sys.Date(), ".docx")
},
content = function(file){
values$bullet_report_status <- "saving"
br_doc_obj <- read_docx() |>
body_add_par("hello world")
tryCatch({
print(br_doc_obj, target = file)
values$bullet_report_status <- "success"
}, error = function(e){
shinyjs::delay(100, {
values$bullet_report_status <- "failed"
})
})
})
}
shinyApp(ui, server)
编辑: 代码完全按照指示工作。
print
正在执行,没有错误(因此状态立即更改为“已保存”),但与下载功能无关。 print
正在将文件保存到服务器,然后一旦 content
功能完成,downloadHandler 就会开始下载过程(即启动文件对话框)。 我将“已保存”视为“成功下载”,这不是同一件事。这仍然是一个相关的测试,但我的问题确实是,“如何测试 downloadHandler 是否正确完成下载过程,以便我可以定义状态?”
目前对流程的理解是:
print
content
完成——剩下的事情发生在“幕后”downloadHandler
启动文件对话框以从用户处获取下载位置和文件名我原以为
updateActionButton
适用于downloadButton
,但事实似乎并非如此。 因此,我编写了以下代码来模仿 downloadButton
,但使用 actionButton
。
关键思想是在一个代码块内处理更新,而不是与决斗的观察者来回跳转。
library(shiny)
library(officer)
ui <- fluidPage(
actionButton("save_bullet_report", "Save to .docx", icon = icon("file-word"))
)
server <- function(input, output, session) {
observeEvent(input$save_bullet_report, {
# The following appears ineffective...
updateActionButton(session, "save_bullet_report",
label = "Saving...",
icon = icon("spinner", class = "fa-spin"))
# ... but this works:
notif <- showNotification("Trying to save...", duration = NULL, type = "message")
Sys.sleep(1) # for demo only
br_doc_obj <- read_docx() |>
body_add_par("hello world")
tryCatch({
path <- file.path(tempdir(), paste0("test_report_", Sys.Date(), ".docx"))
# stop() # uncomment to test error
print(br_doc_obj, target = path)
browseURL(path)
removeNotification(notif)
updateActionButton(session, "save_bullet_report",
label = "Saved",
icon = icon("check", style = "color: #00AA00"))
}, error = function(e){
removeNotification(notif)
updateActionButton(session, "save_bullet_report",
label = "Save Failed",
icon = icon("exclamation-triangle", style = "color: #AA0000"))
})
})
}
shinyApp(ui, server)
不幸的是,按钮的更新似乎只有在 R 块完成后才会生效。 因此,我不知道如何更新
actionButton
以显示工作正在进行中。 但是,showNotification
在 R 代码的报告生成块期间完全有效。
事实上,如果你想使用
downloadButton
,你可以在代码过程中使用showNotification
来处理多次更新。
downloadButton
的一个缺点是,如果报告生成失败,shiny仍然会让用户下载一个空文件。 但对于自制的 actionButton
观察者,只有在确定报告生成正确时才能调用 browseURL
。
这在本地有效。 我还没有在远程闪亮服务器部署上尝试过
browseURL
。 但我已使用 browseURL
启动从远程 RStudio 服务器的下载。