【问题标题】:Complex R Shiny input binding issue with datatable数据表的复杂 R Shiny 输入绑定问题
【发布时间】:2018-12-06 11:39:27
【问题描述】:

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

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

但是 如果我更新数据集(从irismtcars),输入和数据表之间的连接就会丢失。现在,如果您更改 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 datatables shiny


    【解决方案1】:

    了解您的挑战:

    为了确定您手头的挑战,您必须知道两件事。

    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>
    

    请注意,您正在构建&lt;select id="a"&gt;,这是一个带有id="a" 的选择。因此,如果我们假设 1) 在刷新后是正确的,您将尝试使用现有 id 构建另一个 html 元素:&lt;select id="a"&gt;。这不应该工作:Can multiple different HTML elements have the same ID if they're different elements?。 (假设我的假设 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)
    

    【讨论】:

    • 好的,最后一部分我可以解释更多,但它的睡眠时间是一个小时后;)如果需要,让我在接下来的几天回来添加一些解释,...
    • 感谢您的建议,以及这些非常清晰的解释。事实上,我已经在创建的输入的名称上使用了rnorm 技巧。这是一个快速找到其极限的技巧。例如,您入侵了日志系统以模拟正确的行为。保持这些技巧迫使我绕过我想要构建的所有东西:(
    • "。事实上,我已经在创建的输入的名称上使用了 rnorm 技巧。"。我们在问题中看不到这一点,..(?)。恐怕它只适用于解决方法,..过去有一些类似的问题,..无论如何祝你好运,....
    猜你喜欢
    • 2015-01-05
    • 2014-05-18
    • 1970-01-01
    • 1970-01-01
    • 2021-01-15
    • 1970-01-01
    • 2012-06-30
    • 2018-07-08
    • 1970-01-01
    相关资源
    最近更新 更多