如何在R Shiny中实现支持撤销最近4步操作的undo按钮
实现思路
要实现最多撤销最近4次操作的功能,核心是新增一个操作历史栈存储每次修改前的数据集版本,限制栈的最大长度为4,点击撤销按钮时取出最近的版本覆盖当前数据即可。
具体改动点
- 在响应式值中新增
history列表存储历史版本,同时设置最大可撤销步数为4 - 所有修改数据的操作(拆分列、删除行、替换值、删除列)执行前,先将当前数据压入历史栈,超过最大步数时自动丢弃最早的历史版本
- 新增撤销按钮的监听逻辑,点击时从历史栈取出最近版本恢复数据
修改后完整可运行代码
library(shiny) library(reshape2) library(DT) library(tibble) ### 拆分列函数 splitColumn <- function(data, column_name) { newColNames <- c("Unmerged_type1", "Unmerged_type2") newCols <- colsplit(data[[column_name]], " ", newColNames) after_merge <- cbind(data, newCols) after_merge[[column_name]] <- NULL after_merge } ### 插入新列函数 fillvalues <- function(data, values, columName){ df_fill <- data vec <- strsplit(values, ",")[[1]] df_fill <- tibble::add_column(df_fill, newcolumn = vec, .after = columName) df_fill } ### 删除列函数 removecolumn <- function(df, nameofthecolumn){ df[ , -which(names(df) %in% nameofthecolumn)] } ui <- fluidPage( sidebarLayout( sidebarPanel( fileInput("file1", "Choose CSV File", accept = ".csv"), checkboxInput("header", "Header", TRUE), actionButton("Splitcolumn", "SplitColumn"), uiOutput("selectUI"), actionButton("deleteRows", "Delete Rows"), textInput("textbox", label="Input the value to replace:"), actionButton("replacevalues", label = 'Replace values'), actionButton("removecolumn", "Remove Column"), actionButton("Undo", 'Undo') ), mainPanel( DTOutput("table1") ) ) ) server <- function(session, input, output) { # 最大撤销步数设置为4 MAX_UNDO <- 4 rv <- reactiveValues(data = NULL, history = list()) observeEvent(input$file1, { file <- input$file1 ext <- tools::file_ext(file$datapath) req(file) validate(need(ext == "csv", "Please upload a csv file")) rv$data <- read.csv(file$datapath, header = input$header) # 新上传文件后清空历史记录 rv$history <- list() }) output$selectUI<-renderUI({ req(rv$data) selectInput(inputId='selectcolumn', label='select column', choices = names(rv$data)) }) # 拆分列操作:先存历史再修改数据 observeEvent(input$Splitcolumn, { rv$history <- c(rv$history, list(rv$data)) if (length(rv$history) > MAX_UNDO) rv$history <- rv$history[-1] rv$data <- splitColumn(rv$data, input$selectcolumn) }) # 删除行操作:先存历史再修改数据 observeEvent(input$deleteRows,{ if (!is.null(input$table1_rows_selected)) { rv$history <- c(rv$history, list(rv$data)) if (length(rv$history) > MAX_UNDO) rv$history <- rv$history[-1] rv$data <- rv$data[-as.numeric(input$table1_rows_selected),] } }) output$table1 <- renderDT({ rv$data }) # 替换值操作:先存历史再修改数据 observeEvent(input$replacevalues, { rv$history <- c(rv$history, list(rv$data)) if (length(rv$history) > MAX_UNDO) rv$history <- rv$history[-1] rv$data <- fillvalues(rv$data, input$textbox, input$selectcolumn) }) # 删除列操作:先存历史再修改数据 observeEvent(input$removecolumn, { rv$history <- c(rv$history, list(rv$data)) if (length(rv$history) > MAX_UNDO) rv$history <- rv$history[-1] rv$data <- removecolumn(rv$data,input$selectcolumn) }) # 撤销按钮逻辑 observeEvent(input$Undo, { req(length(rv$history) > 0) # 取出最近的历史版本覆盖当前数据 rv$data <- rv$history[[length(rv$history)]] # 删除已恢复的历史版本 rv$history <- rv$history[-length(rv$history)] }) } shinyApp(ui, server)
验证说明
你可以用提供的CSV测试数据验证功能:所有修改操作执行后点击Undo按钮即可回退到上一步,最多支持连续回退4次操作,新上传文件会自动清空之前的操作历史。
内容的提问来源于stack exchange,提问作者Kevin Tracey
相关产品推荐
相关产品推荐

