在保持位置的情况下更新Shiny DataTable中的行

11

我正在创建一个Shiny应用程序,它在屏幕顶部显示data.frame信息,并在底部显示特定变量的统计信息。用户可以通过与DT :: datatable对象交互来导航data.frame列。

当用户单击变量时,会呈现详细信息,可以进行编辑。我希望更新这些信息并在datatable中反映出来。我的问题是,当我更新表格时,它从最开始重新渲染和显示。 如何在编辑后保留datatable的页码和行选择?

这是一个简单的工作示例,显示了mtcars数据集中的DT :: datatable。我有一些控件可以更新字段。请注意,datatable被重新呈现回到第一页。

library(shiny)

runApp(shinyApp(

  ui = fluidPage(
    title = "minimal-working-example",
    fluidRow(
      column(3, inputPanel(
        selectInput("field", "Field", choices = names(mtcars)),
        numericInput("value", "Value", 0),
        actionButton("submit", "Submit")
      )),

      column(9,
        DT::dataTableOutput("table")
      )
    )
  ),

  server = function(input, output) {

    v <- reactiveValues(mtcars=mtcars)

    observeEvent(input$submit, {
      v$mtcars[input$field] <- input$value
    })

    output$table <- DT::renderDataTable({
      DT::datatable(
        v$mtcars,
        selection = "single",
        options = list(pageLength = 5))
    })
  }
))

会话信息:

Session info --------------------------
 setting  value                       
 version  R version 3.3.0 (2016-05-03)
 system   x86_64, mingw32             
 ui       RStudio (0.99.902)          
 language (EN)                        
 collate  English_United States.1252  
 tz       America/Chicago             
 date     2016-07-11                  

Packages -------------------------------
 package     * version     date       source                        
 DT            0.1.45      2016-02-09 Github (rstudio/DT@a63e9ac)   
 shiny       * 0.13.0.9000 2016-02-08 Github (rstudio/shiny@e871934)

1
这有点棘手,但是可行的。基本上需要做的是以响应式方式保存表格的当前页面,可以使用javascript中的Shiny.onInputChange来完成。然后当您呈现表格时,回调以在之前的页码处打开它。我会在有机会时写出完整的答案。 - Carl
1个回答

12

可以在 R 内部完成此操作,而无需通过 JS 或类似方式进入 datatable 的结构。我们利用从 DT 包中获取的各种表状态信息来呈现新更新的 datatable,就像之前的那个一样。我们使用的所有内容都在DT 文档中描述。

第一项:选择。您可以通过在 datatable 的 selection 参数中添加 selected = ... 来预先选择行。这可以与变量 input$table_rows_selected 结合使用,以保存先前选择的行并在重新呈现时预先选择该精确行。

第二项:分页。datatable 包有一个选项 displayStart,指定在呈现表格时应显示哪一行。文档在此处。因此,如果每页有 5 行,则 displayStart = 9 将从第 3 页开始显示。 (JavaScript 数组从0开始,因此始终要减去1。)这可以与 input$table_rows_current 结合使用,后者是当前可见行号的向量。如果我们存储第一个条目(减 1),则我们知道从哪里开始显示。

以下是完整的代码示例:

library(shiny)

runApp(shinyApp(

  ui = fluidPage(
    title = "minimal-working-example",
    fluidRow(
      column(3, inputPanel(
        selectInput("field", "Field", choices = names(mtcars)),
        numericInput("value", "Value", 0),
        actionButton("submit", "Submit")
      )),

      column(9,
        DT::dataTableOutput("table")
      )
    )
  ),

  server = function(input, output) {

    v <- reactiveValues(mtcars=mtcars)
    previousSelection <- NULL
    previousPage <- NULL

    observeEvent(input$submit, {
      previousSelection <<- input$table_rows_selected
      previousPage <<- input$table_rows_current[1] - 1

      v$mtcars[input$field] <- input$value
    })

    output$table <- DT::renderDataTable({
      DT::datatable(
        v$mtcars,
        selection = list(mode = "single", target = "row", selected = previousSelection),
        options = list(pageLength = 5, displayStart = previousPage))
    })
  }
))

感谢您详细的回复。我在您提供的解决方案中发现了DT API中的特性。我打算给您颁发奖励,因为您回答了我提出的问题。然而,在实施这个解决方案时,我发现在对表格进行排序时会出现错误。您有什么想法来解决这个问题吗? - Zelazny7
1
@Zelazny7 有一个 datatable 选项 order,如果你设置了 datatable 选项 stateSave = TRUE,你可以通过 input$table_state$order 获取当前表格的排序。但是,集合在 displayStart 之后排序,这将导致错误的页面选择。我会使用排序来(临时)对数据集进行排序,并检索出 previousPage 行号将显示在哪个实际页面上(排序后的行号)。然后,我会将此值(模数页长度)作为您的 previousPage 参数。如果排序列是已被覆盖的列,则还需添加异常规则。 - K. Rohde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接