r - 使用 R Shiny 中的操作按钮将行从一个 DT 移动到其他 DT

更新

我正在尝试使用 shinyDT 制作应用,similar to the accepted answer from Shree here .你,我想添加以下内容:

  1. 扩展 Shree 的解决方案,以便左侧(源)的 DT 中的项目可以移动到右侧和后面的多个表并且是可扩展的,以便我可以决定我想在右边放多少张 table 。也就是说,左侧表格中的不同项目可以放在右侧的不同表格中。
  2. 此外,在右侧的每个表格旁边都有双箭头按钮,以便可以通过单击双箭头按钮来添加或删除表格中的所有项目,而不仅仅是用于移动所选变量的单箭头按钮, like here , 但仍然能够决定是否显示它们。
  3. 右侧的表格即使在空的时候也可见。

有人可以帮忙吗?

最佳答案

如前所述shiny modules是解决这个问题的优雅方式。你必须传入一些 reactives 来接收行,你必须返回一些 reactives 来发送行/告诉主表它应该删除它刚刚发送的行。

一个完整的示例如下所示:

library(shiny)
library(DT)

receiver_ui <- function(id, class) {
   ns <- NS(id)
   fluidRow(
      column(width = 1,
             actionButton(ns("add"), 
                          label = NULL,
                          icon("angle-right")),
             actionButton(ns("add_all"), 
                          label = NULL,
                          icon("angle-double-right")),
             actionButton(ns("remove"),
                          label = NULL,
                          icon("angle-left")),
             actionButton(ns("remove_all"),
                          label = NULL,
                          icon("angle-double-left"))),
      column(width = 11,
             dataTableOutput(ns("sink_table"))),
      class = class
   )
}

receiver_server <- function(input, output, session, selected_rows, full_page, blueprint) {
   ## data_exch contains 2 data.frames:
   ## send: the data.frame which should be sent back to the source
   ## receive: the data which should be added to this display
   data_exch <- reactiveValues(send    = blueprint,
                               receive = blueprint)
   
   ## trigger_delete is used to signal the source to delete the rows whihc just were sent
   trigger_delete <- reactiveValues(trigger = NULL, all = FALSE)
   
   ## render the table and remove .original_order, which is used to keep always the same order
   output$sink_table <- renderDataTable({
      dat <- data_exch$receive
      dat$.original_order <- NULL
      dat
   })
   
   ## helper function to move selected rows from this display back 
   ## to the source via data_exch
   shift_rows <- function(selector) {
      data_exch$send <- data_exch$receive[selector, , drop = FALSE]
      data_exch$receive <- data_exch$receive[-selector, , drop = FALSE]
   }
   
   ## helper function to add the relevant rows
   add_rows <- function(all) {
      rel_rows <- if(all) req(full_page()) else req(selected_rows())
      data_exch$receive <- rbind(data_exch$receive, rel_rows)
      data_exch$receive <- data_exch$receive[order(data_exch$receive$.original_order), ]
      ## trigger delete, such that the rows are deleted from the source
      old_value <- trigger_delete$trigger
      trigger_delete$trigger <- ifelse(is.null(old_value), 0, old_value) + 1
      trigger_delete$all <- all
   }
   
   observeEvent(input$add, {
      add_rows(FALSE)
   })
   
   observeEvent(input$add_all, {
      add_rows(TRUE)
   })
   
   observeEvent(input$remove, {
      shift_rows(req(input$sink_table_rows_selected))
   })
   
   observeEvent(input$remove_all, {
      shift_rows(req(input$sink_table_rows_current))
   })
   
   ## return the send reactive to signal the main app which rows to add back
   ## and the delete trigger to remove rows
   list(send   = reactive(data_exch$send),
        delete = trigger_delete)
}


ui <- fluidPage(
   tags$head(tags$style(HTML(".odd {background: #DDEBF7;}",
                             ".even {background: #BDD7EE;}",
                             ".btn-default {min-width:38.25px;}",
                             ".row {padding-top: 15px;}"))),
   fluidRow(
      actionButton("add", "Add Table") 
   ),
   fluidRow(
      column(width = 6, dataTableOutput("source_table")),
      column(width = 6, div(id = "container")),
   )
)

server <- function(input, output, session) {
   orig_data <- mtcars
   orig_data$.original_order <- seq(1, NROW(orig_data), 1)
   my_data <- reactiveVal(orig_data)
   
   handlers <- reactiveVal(list())
   
   selected_rows <- reactive({
      my_data()[req(input$source_table_rows_selected), , drop = FALSE]
   })
   
   all_rows <- reactive({
      my_data()[req(input$source_table_rows_current), , drop = FALSE]
   })
   
   observeEvent(input$add, {
      old_handles <- handlers()
      n <- length(old_handles) + 1
      uid <- paste0("row", n)
      insertUI("#container", ui = receiver_ui(uid, ifelse(n %% 2, "odd", "even")))
      new_handle <- callModule(
         receiver_server,
         uid,
         selected_rows = selected_rows,
         full_page = all_rows,
         ## select 0 rows data.frame to get the structure
         blueprint = orig_data[0, ])
      
      observeEvent(new_handle$delete$trigger, {
         if (new_handle$delete$all) {
            selection <- req(input$source_table_rows_current)
         } else {
            selection <- req(input$source_table_rows_selected)
         }
         my_data(my_data()[-selection, , drop = FALSE])
      })
      
      observe({
         req(NROW(new_handle$send()) > 0)
         dat <- rbind(isolate(my_data()), new_handle$send())
         my_data(dat[order(dat$.original_order), ])
      })
      handlers(c(old_handles, setNames(list(new_handle), uid)))
   })
   
   output$source_table <- renderDataTable({
      dat <- my_data()
      dat$.original_order <- NULL
      dat
   })
}


shinyApp(ui, server)

解释

一个模块包含 UI 和服务器,并且由于命名空间技术,名称只需要在一个模块内是唯一的(并且每个模块以后也必须有一个唯一的名称)。该模块可以通过传递给 callModulereactives 与主应用程序通信(请注意,我仍在使用旧功能,因为我尚未更新我的 Shiny 库), 或者从服务器函数返回。

在主应用程序中,我们有一个按钮,它动态插入 UI 并调用 callModule 来激活逻辑。 observers 也在同一调用中生成,以使服务器逻辑正常工作。

https://stackoverflow.com/questions/62782414/

相关文章:

r - 在 R 中将日期偏移一个月

pdf - 从 S3 读取 pdf 对象

php - 试图从 laravel 中 Eloquent 产品评论中获取评论者的姓名

javascript - 如何使用 react 模态显示没有背景效果的模态?

android - CameraX - 在纵向模式下锁定 Activity 时仅旋转预览

amazon-web-services - terraform 可以复制 s3 存储桶的内容吗?

oracle - 基于字符和数字的 PL/SQL Regex 搜索

python - 将 Pandas 数据框字符串拆分为单独的行

ios - Flutter NSException : Configuration fails. 可

javascript - 如何使用 typescript 导出 Mongoose 模式?