R Shiny多选项拖拽移动功能实现求助:扩展shinyjqui现有方案
Great question! I’ve tackled this exact multi-selection + drag-and-drop scenario with shinyjqui before, and there are a couple of ways to make it work. Let me walk you through a practical solution that adds checkbox-based multi-selection with batch movement, while keeping the original drag-and-drop functionality intact.
Approach Overview
The default orderInput doesn’t support multi-select drag out of the box, but we can extend it by:
- Adding checkboxes to each item in the containers
- Using reactive values to track items in each list
- Adding buttons to trigger batch movement of selected items
- Keeping the original drag-and-drop functionality for single-item interactions
Modified Code Implementation
library(shiny) library(shinyjqui) ui <- fluidPage( fluidRow( column( width = 12, uiOutput("OrderInputRender") ) ) ) server<- function(input, output, session){ # Reactive values to track items in each container (persists state) rv <- reactiveValues( available = colnames(mtcars), selected = character(0) ) output$OrderInputRender <- renderUI({ fluidRow( column(width = 5, # Available columns with checkboxes for multi-selection orderInput( "All_Columns", width = "100%", label = "Available columns (check to select)", items = lapply(rv$available, function(col) { tags$div( class = "item", tags$input(type = "checkbox", class = "item-checkbox", value = col), tags$span(col) ) }), style="margin:5px 0 0 0%; overflow: auto; background-color:#DCDCDC; border: 0; padding: 10px; height:360px;", connect = "Channel_Column" ), # Button to batch move selected items to the right actionButton("move_right", "Move Selected →", class = "btn-primary btn-block mt-2") ), column(width = 5, # Selected columns with checkboxes orderInput( "Channel_Column", width = "100%", label = "Selected columns (check to select)", items = lapply(rv$selected, function(col) { tags$div( class = "item", tags$input(type = "checkbox", class = "item-checkbox", value = col), tags$span(col) ) }), style="margin:5px 0 0 0%; overflow: auto; background-color:#DCDCDC; border: 0; padding: 10px; height:360px;", connect = "All_Columns" ), # Button to batch move selected items to the left actionButton("move_left", "← Move Selected", class = "btn-primary btn-block mt-2") ), column(width = 2, # Helper display to show current selections h4("Selected Items"), verbatimTextOutput("current_selections") ) ) }) # JavaScript to fetch checked checkbox values when buttons are clicked tags$script(HTML(" // Listen for message from Shiny to get checked items Shiny.addCustomMessageHandler('get_checked', function(data) { var checkedValues = []; $(data.container + ' .item-checkbox:checked').each(function() { checkedValues.push($(this).val()); }); // Send checked values back to Shiny as input Shiny.setInputValue('checked_items', checkedValues); }); ")) # Helper function to retrieve checked items get_selected_items <- function() { session$sendCustomMessage("get_checked", list(container = "#All_Columns, #Channel_Column")) isolate(input$checked_items) } # Handle moving selected items from available to selected observeEvent(input$move_right, { selected <- get_selected_items() # Filter to only items in the available list to_move <- intersect(selected, rv$available) if(length(to_move) > 0){ rv$available <- setdiff(rv$available, to_move) rv$selected <- c(rv$selected, to_move) # Uncheck checkboxes after moving session$sendCustomMessage("uncheck_all", list()) } }) # Handle moving selected items from selected to available observeEvent(input$move_left, { selected <- get_selected_items() # Filter to only items in the selected list to_move <- intersect(selected, rv$selected) if(length(to_move) > 0){ rv$selected <- setdiff(rv$selected, to_move) rv$available <- c(rv$available, to_move) # Uncheck checkboxes after moving session$sendCustomMessage("uncheck_all", list()) } }) # JavaScript to uncheck all checkboxes after movement tags$script(HTML(" Shiny.addCustomMessageHandler('uncheck_all', function() { $('.item-checkbox').prop('checked', false); Shiny.setInputValue('checked_items', []); }); ")) # Display current selected items for feedback output$current_selections <- renderPrint({ input$checked_items }) } shinyApp(ui, server)
Key Features Explained
- Reactive State Tracking: Uses
reactiveValuesto keep track of items in each container, ensuring the UI updates dynamically when items are moved. - Checkbox Multi-Selection: Each item has a checkbox, making it easy for users to select multiple items at once.
- Batch Movement Buttons: Buttons trigger JavaScript to collect selected items, then update the lists in Shiny.
- Dual Functionality: Users can still drag single items between containers (thanks to the
connectparameter) or use batch selection for bulk moves. - Clean State Management: Automatically unchecks checkboxes after moving items to avoid confusion.
Alternative: Native Multi-Drag with Ctrl/Cmd
If you prefer holding Ctrl/Cmd to select multiple items and drag them together, you can customize the jQuery UI sortable options passed to orderInput. Add this to your orderInput call:
options = list( cancel = ".item-checkbox", # Prevent checkbox from interfering with drag selected = ".ui-selected", start = I("function(event, ui) { var selected = $(this).find('.ui-selected').not(ui.item); ui.item.data('items', selected); }"), stop = I("function(event, ui) { var items = ui.item.data('items'); if(items) { items.appendTo(ui.item.parent()); } }") )
Then add CSS to highlight selected items:
.ui-selected { background-color: #a8d1ff; }
This lets users select multiple items with Ctrl/Cmd and drag them together, though it’s a bit more complex than the checkbox approach.
内容的提问来源于stack exchange,提问作者kawsleo

