复杂的 R Shiny 输入与数据表的绑定问题

问题描述 投票:0回答:1

我正在尝试做一些有点棘手的事情,我希望有人可以帮助我。

我想在数据表中添加

selectInput
。 如果我启动应用程序,我会看到输入
col_1
col_2
.. 与数据表连接良好(您可以切换到 a、b 或 c)

但是 如果我更新数据集(从

iris
mtcars
),输入和数据表之间的连接就会丢失。现在,如果您更改
selectinput
,日志不会显示修改。如何保留链接?

我使用

shiny.bindAll()
shiny.unbindAll()
进行了一些测试,但没有成功。

你有什么想法吗?

请查看该应用程序:

library(shiny)
library(DT)
library(shinyjs)
library(purrr)
    
    ui <- fluidPage(
      selectInput("data","choose data",choices = c("iris","mtcars")),
      DT::DTOutput("tableau"),
      verbatimTextOutput("log")
    )
    
    server <- function(input, output, session) {
      dataset <- reactive({
        switch (input$data,
          "iris" = iris,
          "mtcars" = mtcars
        )
      })
      
      output$tableau <- DT::renderDT({
        col_names<-
          seq_along(dataset()) %>% 
        map(~selectInput(
          inputId = paste0("col_",.x),
          label = NULL, 
          choices = c("a","b","c"))) %>% 
          map(as.character)
        
        DT::datatable(dataset(),
                  options = list(ordering = FALSE, 
                          preDrawCallback = JS("function() {
                                               Shiny.unbindAll(this.api().table().node()); }"),
                         drawCallback = JS("function() { Shiny.bindAll(this.api().table().node());
                         }")
          ),
          colnames = col_names, 
          escape = FALSE         
        )
        
      })
      output$log <- renderPrint({
        lst <- reactiveValuesToList(input)
        lst[order(names(lst))]
      })
      
    }
    
    shinyApp(ui, server)
javascript r shiny dt
1个回答
7
投票

了解您的挑战:

为了确定您面临的挑战,您必须了解两件事。

  1. 如果刷新数据表,它将被“删除”并从 从头开始(这里不是100%确定,我想我在某个地方读过它)。
  2. 请记住,您本质上是在构建 html 页面。

selectInput()
只是 html 代码的包装。如果您在控制台中输入
selectInput("a", "b", "c")
,它将返回:

<div class="form-group shiny-input-container">
  <label class="control-label" for="a">b</label>
  <div>
    <select id="a"><option value="c" selected>c</option></select>
    <script type="application/json" data-for="a" data-nonempty="">{}</script>
  </div>
</div>

请注意,您正在构建

<select id="a">
,一个带有
id="a"
的选择。因此,如果我们假设刷新后 1) 是正确的,您将尝试使用现有 id 构建另一个 html 元素:
<select id="a">
。这不应该起作用:如果多个不同的 HTML 元素是不同的元素,它们可以具有相同的 ID 吗?。 (假设我的假设 1)成立;))

解决您的挑战:

乍一看非常简单:只需确保您使用的 id 在创建的 html 文档中是唯一的。

非常快速且肮脏的方法是更换:

inputId = paste0("col_",.x)

类似:

inputId = paste0("col_", 1:nc, "-", sample(1:9999, nc))

但是之后你就很难使用了。

更远的路:

所以你可以使用某种记忆

  1. 您已经使用过哪些 ID。
  2. 您当前正在使用哪些 ID。

你可以使用

  global <- reactiveValues(oldId = c(), currentId = c())

为此。

过滤掉旧的使用过的 ID 并提取当前 ID 的想法可能是这样的:

    lst <- reactiveValuesToList(input)
    lst <- lst[setdiff(names(lst), global$oldId)]
    inp <- grepl("col_", names(lst))
    names(lst)[inp] <- sapply(sapply(names(lst)[inp], strsplit, "-"), "[", 1)

可重现的示例如下:

library(shiny)
library(DT)
library(shinyjs)
library(purrr)

ui <- fluidPage(
  selectInput("data","choose data",choices = c("iris","mtcars")),
  dataTableOutput("tableau"),
  verbatimTextOutput("log")
)

server <- function(input, output, session) {

  global <- reactiveValues(oldId = c(), currentId = c())

  dataset <- reactive({
    switch (input$data,
            "iris" = iris,
            "mtcars" = mtcars
    )
  })

  output$tableau <- renderDataTable({
    isolate({
      global$oldId <- c(global$oldId, global$currentId)
      nc <- ncol(dataset())
      global$currentId <- paste0("col_", 1:nc, "-", sample(setdiff(1:9999, global$oldId), nc))

      col_names <-
        seq_along(dataset()) %>% 
        map(~selectInput(
          inputId = global$currentId[.x],
          label = NULL, 
          choices = c("a","b","c"))) %>% 
        map(as.character)
    })    
    DT::datatable(dataset(),
                  options = list(ordering = FALSE, 
                                 preDrawCallback = JS("function() {
                                                      Shiny.unbindAll(this.api().table().node()); }"),
                                 drawCallback = JS("function() { Shiny.bindAll(this.api().table().node());
}")
          ),
          colnames = col_names, 
          escape = FALSE         
    )

})
  output$log <- renderPrint({
    lst <- reactiveValuesToList(input)
    lst <- lst[setdiff(names(lst), global$oldId)]
    inp <- grepl("col_", names(lst))
    names(lst)[inp] <- sapply(sapply(names(lst)[inp], strsplit, "-"), "[", 1)
    lst[order(names(lst))]
  })

}

shinyApp(ui, server)
© www.soinside.com 2019 - 2024. All rights reserved.