R Shiny用reactiveValues()复制可编辑响应表同步改动且不改原数据
实现方案
核心运行逻辑
- 对
dat_joined$data全程仅做读取操作,不写入、不修改,保证可编辑表的源数据不会被编辑操作污染 dat_joined$data每次生成完成后,将其完整值拷贝存入mydf$data,作为不可编辑表的初始数据源- 可编辑表的所有单元格编辑操作,仅在点击
Process按钮后,逐行同步更新到mydf$data中,不触发dat_joined$data的变更流程
原代码的问题点
- 初始化
mydf时使用硬编码的10行5列空矩阵,和dat_joined$data的动态结构不匹配 - 未在
dat_joined$data生成时同步初始副本,导致初始状态下两个表内容不一致 - 未适配DT单元格编辑事件的列索引偏移问题(
rownames = F时DT返回的列索引从0开始,直接操作会导致列匹配错误) - 编辑值回写时未遍历所有编辑记录,仅取最后一次编辑的单元格值,无法同步多轮编辑的内容
修正后可运行代码
library(shiny) library(DT) library(shinyWidgets) library(tidyverse) # 生成主测试数据集 dat_total <- tibble(ID_1 = 1:10, names = letters[1:10], ID_2 = 11:20, names_2 = LETTERS[c(3:5, 1, 2, 6:8, 10, 9)]) shinyApp( ui = fluidPage( title = 'Radio button and a dropdown manue ', sliderInput("n_rows_table", "Number of rows:", min = 0, max = 10, value = 5), actionBttn( inputId = "button_1", label = "Make tables", size = "sm", color = "warning" ), DT::dataTableOutput("mytable"), actionBttn( inputId = "button_2", label = "Process", size = "sm", color = "success"), DT::dataTableOutput("table2") ), server = function(input, output, session) { # 初始化响应值容器 dat_left <- reactiveValues(data=NULL) dat_right <- reactiveValues(data=NULL) dat_joined <- reactiveValues(data=NULL) # 不可编辑表数据源,初始状态为空,待同步 mydf <- reactiveValues(data = NULL) # 点击Make tables后生成基础数据集 dat <- eventReactive(input$button_1, { dat_total[1:input$n_rows_table, ] %>% rowid_to_column() }) # 拆分得到左右两个匹配源数据集 observe({ dat_left$data <- dat() %>% select(rowid, ID_1, names) }) observe({ dat_right$data <- dat() %>% select(rowid, ID_2, names_2, ID_1) }) # 拼接得到可编辑表的源数据(后续不做任何修改) observe({ if (is.null(dat_right$data)) return(NULL) dat_joined$data <- left_join(dat_left$data, dat_right$data, by = "rowid") # 核心操作:dat_joined数据生成后,立刻值拷贝存入mydf作为初始副本 # R中数据框的<-赋值默认是值拷贝,不会和原对象共享内存引用 mydf$data <- dat_joined$data }) # 渲染可编辑表,直接引用dat_joined$data output$mytable <- renderDT({ req(dat_joined$data) datatable(dat_joined$data, rownames = F, editable = "cell") }) # 点击Process按钮时,将所有编辑内容同步到mydf,全程不修改dat_joined$data observeEvent(input$button_2,{ req(input$mytable_cell_edit, mydf$data) info = input$mytable_cell_edit # 遍历所有编辑记录,逐一更新到mydf for (idx in seq_len(nrow(info))) { row_pos <- info$row[idx] # 适配rownames=F的索引偏移:DT返回列索引从0开始,+1匹配数据框列号 col_pos <- info$col[idx] + 1 edit_val <- info$value[idx] # 类型匹配后写入mydf mydf$data[row_pos, col_pos] <- DT::coerceValue(edit_val, mydf$data[row_pos, col_pos]) } }) # 渲染不可编辑表 output[["table2"]] <- renderDT({ req(mydf$data) datatable(mydf$data, editable = F, rownames = F) }) } )
功能验证点
- 点击“Make tables”生成表格后,不可编辑表会自动加载和可编辑表完全一致的初始内容
- 在可编辑表上进行任意单元格修改,
dat_joined$data的原始值不会发生变更 - 完成多轮编辑后点击“Process”按钮,所有修改内容会逐一对齐同步到不可编辑表
- 调整滑块行数重新点击“Make tables”时,两个表会自动重置为新生成数据的初始副本状态
内容的提问来源于stack exchange,提问作者Gh_Shiny
相关产品推荐
相关产品推荐

