Shiny中DT数据表selectInput更新后保持selectize样式的方法
DT数据表更新后selectize样式丢失的修复方案
问题根源
initComplete 回调仅在数据表首次渲染时执行一次,负责给初始的selectInput添加selectize样式和搜索功能。但通过replaceData()更新数据表后,新生成的selectInput节点不会触发initComplete,因此没有被初始化selectize,导致样式丢失。
修复步骤
- 将selectize初始化逻辑移至
drawCallback:drawCallback会在每次数据表重绘(包括更新后)时执行,确保新生成的selectInput都能被处理。 - 添加旧实例销毁逻辑:避免重复初始化selectize导致的DOM异常或功能冲突。
- 调整执行顺序:先初始化selectize,再绑定Shiny事件,保证绑定的是处理后的元素。
修改后的完整代码
library(shiny) library(DT) # 生成selectize初始化JS:销毁旧实例+初始化新实例 selectize_ids <- function(ids) { tmp <- paste0(" let $el = $('#", ids, "');") tmp <- paste0(tmp, " if($el.data('selectize')) $el[0].selectize.destroy();") tmp <- paste0(tmp, " $el.selectize();") c( "function(settings){", tmp, "}" ) } shinyApp( ui = fluidPage( div(style = "display: none;", selectInput(inputId = "dummy", label = NULL, choices = 1:2)), actionButton(inputId = "my_button", label = "Click Me"), DT::dataTableOutput(outputId = "table") ), server = function(input, output, session) { input_ids <- c("A_1", "A_2") df_reactive <- reactive({ data.frame( A = sapply(input_ids, function(id) as.character(selectInput( inputId = id, label = NULL, choices = paste0(c("a", "b", "c"), input$my_button) ))) ) }) output$table <- DT::renderDataTable({ DT::datatable( data = isolate(df_reactive()), selection = "none", rownames = F, escape = F, options = list( # 保留initComplete确保首次加载正常 initComplete = JS(selectize_ids(input_ids)), preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'), drawCallback = JS('function() { ', JS(selectize_ids(input_ids)), ' Shiny.bindAll(this.api().table().node()); } ') ) ) }) proxy <- DT::dataTableProxy(outputId = "table") observe({ DT::replaceData(proxy = proxy, data = df_reactive(), rownames = F) }) } )
更灵活的通用方案(适配动态变化的selectInput)
如果selectInput的id会随用户输入动态增减,无需手动维护id列表,直接选中数据表内所有select元素进行处理:
library(shiny) library(DT) # 通用selectize初始化:处理数据表内所有select元素 selectize_all <- function() { c( "function(settings){", " $(settings.nTable).find('select').each(function(){", " let $el = $(this);", " if($el.data('selectize')) $el[0].selectize.destroy();", " $el.selectize();", " });", "}" ) } shinyApp( ui = fluidPage( div(style = "display: none;", selectInput(inputId = "dummy", label = NULL, choices = 1:2)), actionButton(inputId = "my_button", label = "Click Me"), DT::dataTableOutput(outputId = "table") ), server = function(input, output, session) { # 模拟动态变化的input_ids input_ids <- reactive(paste0("A_", 1:(input$my_button + 1))) df_reactive <- reactive({ data.frame( A = sapply(input_ids(), function(id) as.character(selectInput( inputId = id, label = NULL, choices = paste0(c("a", "b", "c"), input$my_button) ))) ) }) output$table <- DT::renderDataTable({ DT::datatable( data = isolate(df_reactive()), selection = "none", rownames = F, escape = F, options = list( initComplete = JS(selectize_all()), preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'), drawCallback = JS('function() { ', JS(selectize_all()), ' Shiny.bindAll(this.api().table().node()); } ') ) ) }) proxy <- DT::dataTableProxy(outputId = "table") observe({ DT::replaceData(proxy = proxy, data = df_reactive(), rownames = F) }) } )
内容的提问来源于stack exchange,提问作者Ben Ernest
相关产品推荐
相关产品推荐

