Shiny中sortable的rank_list无法在shinyalert/模态框交互
Shiny sortable rank_list在弹窗中无法拖拽的解决方法
问题原因
sortable包的rank_list组件依赖页面加载时执行的JavaScript代码完成拖拽功能初始化。当你把提前生成的rank_list对象放入shinyalert弹窗时,弹窗是动态添加到DOM中的,初始化脚本不会自动针对这些新元素运行,导致拖拽和选中功能失效。而selectInput这类原生Shiny组件会自动处理动态渲染的交互逻辑,因此不受影响。
解决方案
方案1:使用Shiny原生modalDialog替代shinyalert
Shiny原生模态框能自动处理动态添加组件的依赖加载与初始化,只需在触发弹窗时动态创建rank_list即可:
library(shiny) library(sortable) labels <- list( "one", "two", "three", htmltools::tags$div(htmltools::em("Complex"), " html tag without a name"), "five" = htmltools::tags$div(htmltools::em("Complex"), " html tag with name: 'five'") ) ui <- fluidPage( rank_list( text = "主页面排序组件", labels = labels, input_id = "rank_list_main", options = sortable_options(multiDrag = TRUE) ), actionButton("showModal", "打开原生模态框") ) server <- function(input, output, session) { observeEvent(input$showModal, { showModal(modalDialog( title = "重新排序这些项", tagList( # 动态创建rank_list,确保初始化脚本正确执行 rank_list( text = "可多选后拖拽分组", labels = labels, input_id = "rank_list_modal", options = sortable_options(multiDrag = TRUE) ), br(), selectInput("modalTextInput", "选择字母", choices = c("a", "b", "C")) ), easyClose = TRUE, footer = modalButton("关闭") )) }) # 监听模态框排序结果 observeEvent(input$rank_list_modal, { print("模态框排序结果:") print(input$rank_list_modal) }) # 监听主页面排序结果 observeEvent(input$rank_list_main, { print("主页面排序结果:") print(input$rank_list_main) }) } shinyApp(ui, server)
方案2:在shinyalert弹窗显示后手动初始化sortable
若坚持使用shinyalert,可通过callbackJS参数在弹窗显示后执行JavaScript代码,手动初始化拖拽功能:
library(shiny) library(sortable) library(shinyalert) labels <- list( "one", "two", "three", htmltools::tags$div(htmltools::em("Complex"), " html tag without a name"), "five" = htmltools::tags$div(htmltools::em("Complex"), " html tag with name: 'five'") ) rank_list_alert <- rank_list( text = "可多选后拖拽分组", labels = labels, input_id = "rank_list_alert", options = sortable_options(multiDrag = TRUE) ) ui <- fluidPage( rank_list( text = "主页面排序组件", labels = labels, input_id = "rank_list_main", options = sortable_options(multiDrag = TRUE) ), actionButton("showAlert", "打开shinyalert弹窗") ) server <- function(input, output, session) { observeEvent(input$showAlert, { shinyalert( title = "重新排序这些项", html = TRUE, closeOnClickOutside = TRUE, showConfirmButton = TRUE, text = tagList( rank_list_alert, br(), selectInput("alertTextInput", "选择字母", choices = c("a", "b", "C")) ), # 弹窗显示后执行初始化代码 callbackJS = 'function() { const sortableContainer = document.querySelector(".shinyalert .rank-list-container"); if (sortableContainer) { Sortable.create(sortableContainer, { multiDrag: true, // 同步排序结果到Shiny输入 onEnd: function(evt) { const items = Array.from(sortableContainer.children).map(el => { return el.getAttribute("data-value") || el.textContent.trim(); }); Shiny.setInputValue("rank_list_alert", items); } }); } }' ) }) # 监听弹窗排序结果 observeEvent(input$rank_list_alert, { print("弹窗排序结果:") print(input$rank_list_alert) }) } shinyApp(ui, server)
注意事项
- 方案1更简洁,推荐优先使用,Shiny原生模态框会自动处理组件的依赖与生命周期。
- 方案2需要手动匹配R端的
sortable_options配置(如multiDrag),并确保排序结果同步到Shiny输入。
内容的提问来源于stack exchange,提问作者shbshk
相关产品推荐
相关产品推荐

