You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R Shiny中DT表格复选框点击更新按钮后无法同步更新求助

解决Shiny DT复选框状态重置问题

问题根源在于表格更新时,DT的JS回调没有根据新数据的select1值同步重置复选框状态,而是保留了DOM中旧的选中状态。以下是修正后的完整代码,重点调整了preDrawCallback和drawCallback的逻辑:

library(shiny)
library(DT)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("species", "选择物种", choices = unique(iris$Species)),
      actionButton("update", "更新表格")
    ),
    mainPanel(
      fluidRow(
        column(6, DTOutput("left_table")),
        column(6, DTOutput("right_table"))
      )
    )
  )
)

server <- function(input, output, session) {
  # 原始数据
  raw_data <- reactiveVal(iris)
  
  # 左侧表格数据(带复选框)
  left_data <- reactive({
    req(input$update)
    isolate({
      df <- subset(raw_data(), Species == input$species)
      df$select1 <- FALSE
      df
    })
  })
  
  # 右侧表格数据(同步选中状态)
  right_data <- reactive({
    req(left_data())
    df <- left_data()
    # 获取左侧表格的复选框选中状态
    selected_rows <- input$left_table_rows_selected
    if (!is.null(selected_rows)) {
      df$select1[selected_rows] <- TRUE
    } else {
      df$select1 <- FALSE
    }
    df
  })
  
  output$left_table <- renderDT({
    req(left_data())
    datatable(
      left_data(),
      selection = "multiple",
      options = list(
        preDrawCallback = JS('function(settings) {
          // 移除之前绑定的事件,避免重复触发
          $(settings.nTable).off("click", "input[type=checkbox]");
        }'),
        drawCallback = JS('function(settings) {
          var api = this.api();
          var rows = api.rows({page: "current"}).nodes();
          // 遍历行,根据select1值设置复选框状态
          rows.each(function(index) {
            var rowData = api.row(index).data();
            var checkbox = $(this).find("input[type=checkbox]");
            checkbox.prop("checked", rowData.select1);
          });
          // 绑定复选框点击事件,同步选中行到Shiny输入
          $(settings.nTable).on("click", "input[type=checkbox]", function() {
            var $row = $(this).closest("tr");
            var rowIdx = api.row($row).index();
            var isChecked = $(this).prop("checked");
            // 更新Shiny的选中行输入
            var selected = api.rows({selected: true}).indexes().toArray();
            if (isChecked) {
              if (!selected.includes(rowIdx)) {
                selected.push(rowIdx);
              }
            } else {
              selected = selected.filter(function(idx) { return idx !== rowIdx; });
            }
            Shiny.setInputValue("left_table_rows_selected", selected);
          });
        }')
      ),
      # 生成复选框列
      callback = JS('table.column(0).nodes().to$().css({width: "50px"});')
    ) %>%
      formatStyle("select1", target = "row", backgroundColor = styleEqual(c(TRUE, FALSE), c("#ccebc5", "#ffffff")))
  })
  
  output$right_table <- renderDT({
    req(right_data())
    datatable(
      right_data(),
      options = list(dom = "t"),
      selection = "none"
    ) %>%
      formatStyle("select1", target = "row", backgroundColor = styleEqual(c(TRUE, FALSE), c("#ccebc5", "#ffffff")))
  })
}

shinyApp(ui, server)

关键修正点:

  • preDrawCallback:在每次绘制表格前移除旧的复选框点击事件绑定,避免重复触发逻辑导致状态混乱。
  • drawCallback:表格绘制完成后,遍历所有行,根据当前行select1列的布尔值强制设置复选框的checked状态,确保新数据加载时状态完全同步。
  • 选中状态同步:点击复选框时,手动管理left_table_rows_selected的Shiny输入值,确保与复选框状态一致,进而同步到右侧表格。

这样修改后,当你切换物种并点击更新按钮时,右侧表格的复选框状态会随新数据的select1值(初始为FALSE)重置,不再保留之前的选中状态。

内容的提问来源于stack exchange,提问作者TobKel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 23:42:39