如何在提交后使用基于 Reactive 的过滤可见数据更新 Shiny Datatable?

How to update Shiny Datatable after Submit with Filtered Visible Data based on Reactive?

这是我向 SO 提出的第一个问题,我会尽量简短明了。

我正在修改一个基本的 Shiny CRUD 版本,该版本由 Dean Attali(感谢您)提供,用于研究目的。为此,我生成了一个玩具 MRE(见下文),展示了我试图实现的主要行为。

  1. 用户筛选显示基于反应的数据(refreshData() based on input$item_used)
  2. 用户可以添加或删除新条目(saveData()、deleteData())
  3. 用户可以单击行并通过 DT::dataTableOutput.
  4. 动态查看反应性变化

我的问题如下:如果用户添加了一个新条目,并且在下拉列表中的两个选项之一上过滤了可见的 DT 数据 selection,那么 活动设备标识符视图不更新。假设用户向 'B' 项添加了一个新条目,所有且只有 'B' 项在 table 中可见(通过下拉列表过滤)——以便查看新条目,她必须 select 'A' 项,然后 select 'B' 项,到 'refresh' 中的 table以查看新条目。

我知道这里有一个基于反应性的简单解决方案,但我似乎无法识别它。非常感谢您的集体帮助!

library(shiny)
library(shinyjs)

# Define the fields we want to save from the form
fields <- c("name", "item_used", "notes")
data <- data.frame(name = c("Luthien","Aredhel","Beren","Turin"),
                   item_used = c("A","B","A","B"),
                   notes = c("fixed","not broken","almost fixed", "beyond repair"),
                   stringsAsFactors = FALSE)

# Shiny app with 3 fields that the user can submit data for
shinyApp(
  ui = fluidPage(sidebarLayout(
    sidebarPanel(width = 3,

                 selectInput("select_used", "Item Used?", choices = c("", "A","B")),
                 tags$hr(),
                  lapply(1:length(fields), function(x) textInput(fields[x], fields[x])),
                  actionButton("submit", "Submit"),
                 actionButton("delete", "Delete")
  ),
  mainPanel(DT::dataTableOutput("maindata", width = 300),
            tags$hr()
             ))),
  server = function(input, output, session) {

    # Whenever a field is filled, aggregate all form data
    formData <- reactive({
      data <- sapply(fields, function(x) input[[x]])
      data
    })

    # When the Submit button is clicked, save the form data
    observeEvent(input$submit, {
      saveData(formData())
    })

    # Show the previous responses
    # (update with current response when Submit is clicked)
    output$maindata <- DT::renderDataTable({
      input$delete
      input$submit
      refreshData()
    })

    observeEvent(input$maindata_rows_selected, {
      dat <- refreshData()[input$maindata_rows_selected,]
      for(i in 1:length(dat)){
        updateTextInput(session, fields[i], value = unname(dat[i]))
      }

    })

    observeEvent(input$delete,{
      deleteData()
    })

    deleteData <- function(){
      delrow <- row.names(refreshData()[input$maindata_rows_selected,])
      data <- loadData()[-c(as.numeric(delrow)),]
      data <<- data

    }

    loadData <- function(){
      data <- data
      return(data)
    }    

    saveData <- function(data) {
      data <- rbind(loadData(), formData())
      data <<- data
    }

    refreshData <- reactive({
      data <- loadData()
      if(input$select_used == ""){
        data
      } else {
        data[data$item_used == input$select_used, ]
      }
    })
  }
)

我已经回答了我自己的问题,我将 post 放在这里以防其他人发现它有用。解决方案非常简单。

与其将 "refreshed data" 定义为反应式,不如将其定义为函数。所以...

 refreshData <- reactive({...})

...应该...

 refreshData <- function(){...}

无需对原始代码进行其他更改。通过将 refreshData 更改为函数,可以插入新数据(通过输入字段),同时保留基于用户输入的过滤视图(在 DT 中)。