切换DT表格列可见性时如何保留单选按钮值?
问题分析
你遇到的核心问题有两个:
- DT的
columnDefs中targets需要传入列索引(从0开始),但你直接用了列名(如'F'),导致隐藏逻辑失效,同时触发表格DOM异常; - 动态修改列显示状态后,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
相关产品推荐
相关产品推荐

