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

切换DT表格列可见性时如何保留单选按钮值?

问题分析

你遇到的核心问题有两个:

  1. DT的columnDefs中targets需要传入列索引(从0开始),但你直接用了列名(如'F'),导致隐藏逻辑失效,同时触发表格DOM异常;
  2. 动态修改列显示状态后,Shiny与表格控件的绑定未正确重建,导致无法获取单选按钮/numericInput的值。
修复方案

以下是修正后的代码,关键修改点已标注:

library(shiny)
library(DT)

shinyApp(
  ui = fluidPage(
    title = 'Radio buttons in a table',
    selectInput("colHide","Column to hide",choices = c('F','G','H'),multiple =TRUE),
    DT::dataTableOutput('foo'),
    verbatimTextOutput('sel')
  ),
  server = function(input, output, session) {
    # table with radio buttons
    m = matrix(
      as.character(1:5), nrow = 12, ncol = 5, byrow = TRUE,
      dimnames = list(month.abb, LETTERS[1:5])
    )
    for (i in seq_len(nrow(m))) {
      m[i, ] = sprintf(
        '<input type="radio" name="%s" value="%s"/>',
        month.abb[i], m[i, ]
      )
    }
    m2<-matrix(
      LETTERS[6:8], nrow = 12, ncol = 3, byrow = TRUE,
      dimnames = list(month.abb, LETTERS[6:8])
    )
    m<-cbind(m,m2)
    
    # 1. 动态生成columnDefs:将列名转换为DT要求的0起始索引
    get_column_defs <- reactive({
      if(is.null(input$colHide) || length(input$colHide) == 0){
        list()
      } else {
        # 匹配列名到索引,DT的targets从0开始
        col_indices <- match(input$colHide, colnames(m)) - 1
        list(list(targets = col_indices, visible = FALSE))
      }
    })
    
    # render table
    output$foo = DT::renderDataTable(
      m, escape = FALSE, 
      selection = 'none', 
      server = FALSE,
      options = list(
        dom = 'Bfrtip', 
        paging = TRUE, 
        pageLength = 6,
        # 2. 保留预绘制和解绑、绘制后绑定的逻辑,确保控件状态正确同步
        preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
        drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } '),
        columnDefs = get_column_defs(),
        ordering = FALSE
      ),
      callback = JS("table.rows().every(function(i, tab, row) {
          var $this = $(this.node());
          // 用行名设置id,避免依赖列内容(防止隐藏列影响)
          $this.attr('id', this.index());
          $this.addClass('shiny-input-radiogroup');
        });")
    )
    
    output$sel = renderPrint({
      # 过滤出单选按钮的输入值
      sapply(month.abb, function(i) input[[i]])
    })
  }
)
关键修改说明
  • 列索引转换:通过match(input$colHide, colnames(m)) - 1将选择的列名转为DT所需的0起始索引,确保列隐藏逻辑正确执行;
  • 反应式生成columnDefs:用reactive函数动态生成列定义,确保选择隐藏列时表格选项实时更新;
  • 简化绑定逻辑:移除callback中重复的Shiny.unbindAll和Shiny.bindAll,保留preDrawCallback和drawCallback的绑定逻辑,避免重复绑定导致的异常;
  • 行ID设置优化:改用行索引设置行ID,避免依赖列内容(防止隐藏列后this.data()[0]获取到错误值);
  • 输入值获取简化:直接遍历month.abb获取单选按钮值,无需复杂的正则匹配。

该方案同样适用于numericInput等其他Shiny输入控件,只需保证控件的name属性唯一且可被正确识别即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 02:20:55