r - 为什么 Shiny.onInputChange 在 R Shiny 中有一个不稳定的行为?
问题描述
我正在构建一个 R 闪亮的应用程序,它将从用户那里获取一条消息并将其存储在一个文本文件中。该文件将同时显示为表格,用户可以使用内置按钮删除一些消息。这个内置按钮是使用 Shiny.onInputChange 实现的。
以下代码是完全可复现的,只需加载三页代码(ui、serve、global)。然后点击“Click me”,然后点击“Publier”(=publish in french),它将填充文本文件并更新表格。
现在,通过单击“Retirer”(= 法语中的 Delete)删除行,如果您多次执行此操作,您会注意到,有时它有效,有时无效,这不是程序应有的行为方式。我无法解释或查明这种不稳定行为的原因。
----------------------
ui.server
# Define UI for application that draws a histogram
shinyUI(fluidPage(
title="Civilia",
theme = "shiny.css",
navbarPage(
########################
fluidPage(
br(),
br(),
br(),
br(),
fluidRow(column(12,offset=0,actionButton("prevMessage", label = "Click me"))),
br(),
DT::dataTableOutput("data")
))
)
)
----------------------
全局.R
##
## load.libraries()
suppressMessages(library(shiny))
suppressMessages(library(plotly))
suppressMessages(library(tidyr))
suppressMessages(library(data.table))
suppressMessages(library(dplyr))
suppressMessages(library(lubridate))
suppressMessages(library(DT))
##
## Set global env values
## Client
.GlobalEnv$client <- "STLevis"
## Data storage for message
.GlobalEnv$vault <- "message.txt"
if(!file.exists(vault)) fwrite(file=vault, data.frame(depoTime=as.POSIXct(character()),msg=character(),duration.h=character(),remTime=as.POSIXct(character())))
.GlobalEnv$msg_vault_df <- fread(vault)
##
## colors
.GlobalEnv$civ.col1 <- rgb(60/255, 60/255, 59/255)
.GlobalEnv$civ.col2 <- rgb(145/255, 191/255, 39/255)
.GlobalEnv$civ.axis.col <- list(linecolor = toRGB("lightgrey"),
gridcolor = toRGB("darkgrey"),
tickcolor = toRGB("darkgrey"),
tickfont = list(color="white"),
titlefont = list(color="white"))
###################################
## Store the message with its duration
store.message <- function(myMessage,myDuration){
## Open the message vault
msg_vault <- fread(vault)
## Change the column class
msg_vault <- msg_vault %>% mutate(depoTime = as.character(depoTime),
msg = as.character(msg),
duration.h = as.numeric(duration.h),
remTime = as.character(remTime))
## Create the data to save
time.now <- Sys.time()
new_data <- data.frame(depoTime = as.character(time.now),
msg = myMessage,
duration.h = myDuration,
remTime = as.character(time.now + hours(myDuration)))
## Append the new message
new_vault <- rbind(msg_vault,new_data)
## Save it
fwrite(new_vault,file=vault)
}
###################################
## Store the message with its duration
store.message.vault <- function(msg_vault){
## Remove the buttons
msg_vault <- msg_vault %>% select(-Delete)
## Save it
fwrite(msg_vault,file=vault)
}
----------------------
服务器.R
#######################
## Define server logic
shinyServer(function(input, output, session) {
msg_vault <- reactiveFileReader(intervalMillis = 100, session = session, filePath = vault, readFunc = fread)
## ----------------------------
## Listen to the previsualisation button
observeEvent(input$prevMessage, {
## Build the sentence to show to the user
myMessage <- "This is a message"
## Show the sentence
showModal(modalDialog(
title=NULL,
HTML(myMessage),
footer = tagList(actionButton("confirmMessage", "Publier"),
modalButton("Annuler"))
))
})
## ----------------------------
## If the message publication has been confirmed
observeEvent(input$confirmMessage, {
## Store the msg
store.message("this is a message",0)
## Notify the user
showModal(modalDialog("Le message a été publié.",footer=NULL,easyClose = TRUE))
Sys.sleep(3)
removeModal()
})
## ----------------------------
## Add buttons to the table
shinyInput <- function(FUN, len, id, ...) {
inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))
}
inputs
}
## ----------------------------
## Table of messages to display
observe({
## Extract the reactive data
msg_vault_df <- msg_vault()
## Create the table to display
.GlobalEnv$msg_tbl = data.frame(
Delete = shinyInput(actionButton, nrow(msg_vault_df), 'button_', label = "Retirer", onclick = 'Shiny.onInputChange(\"select_button\", this.id)' ),
depoTime = msg_vault_df$depoTime,
msg = msg_vault_df$msg,
duration.h = msg_vault_df$duration.h,
remTime = msg_vault_df$remTime
)
print(msg_tbl)
## Push the table to the UI
output$data <- DT::renderDataTable(
msg_tbl, server = FALSE, escape = FALSE, selection = 'none',options = list(searching = FALSE,info=FALSE,paging=FALSE)
)
})
## ----------------------------
## Wait for the delete buttons
observeEvent(input$select_button, {
## Chosen row to delete
print(input$select_button)
selectedRow <- as.numeric(strsplit(input$select_button, "_")[[1]][2])
print(selectedRow)
## Remove row
myMsg_tbl <- .GlobalEnv$msg_tbl
#print(myMsg_tbl)
myMsg_tbl <- myMsg_tbl[rownames(myMsg_tbl) != selectedRow, ]
## Save the remaining, changing the file will update the table
store.message.vault(myMsg_tbl)
})
})
解决方案
'Shiny.onInputChange(\"select_button\", this.id)'
单击按钮时发送this.id
到。但是,如果您再次单击该按钮,则不会发生任何事情,因为没有更改。input$select_button
this.id
这相当于'Shiny.setInputValue(\"select_button\", this.id)'
. 但是Shiny.setInputValue
有一个选项可以克服这个问题:{priority: 'event'}
选项。
所以你必须做onclick = 'Shiny.setInputValue(\"select_button\", this.id, {priority: \"event\"})'
而不是onclick = 'Shiny.onInputChange(\"select_button\", this.id)'
.
推荐阅读
- javascript - 在调用迭代器之前调用 async.each 最终回调
- javascript - 使用模板变量作为脚本 src 的一部分
- html5-video - html5视频如何处理文本轨道?
- android - 有没有办法将我的文档 ID 与 toObject 映射?
- testing - 使用元运算符进行测试不会打印测试描述
- python - Pytorch cuda get_device_name 和 current_device() 挂起并被杀死?
- javascript - 根据对象的其他属性从对象数组中的对象数组中获取属性的“val”
- sql - 如何在当前日期和下一个日期之间进行日期差异?
- python - 从 Pandas DataFrame 就地排序行
- c++ - C ++试图通过getline函数为函数调用获取用户输入值