【问题标题】:Generate observers for dynamic number of inputs为动态输入数量生成观察者
【发布时间】:2016-12-21 09:52:06
【问题描述】:

我有一个我认为非常简单的用户案例,但我无法找到解决方案:我希望 Shiny 生成用户指定数量的输入,并为每个输入动态创建一个观察者。

在下面的最小可重现代码中,用户通过输入textInput 小部件来指示所需的操作按钮的数量;然后他或她按下“提交”,这会生成操作按钮。

我希望用户能够单击任何操作按钮并生成特定于它的输出(例如,对于最小的情况,只需打印按钮的名称):

library("shiny")

ui <- fluidPage(textInput("numButtons", "Number of buttons to generate"), 
                actionButton("go", "Submit"), uiOutput("ui"))

server <- function(input, output) {

        makeObservers <- reactive({

                lapply(1:(as.numeric(input$numButtons)), function (x) {

                        observeEvent(input[[paste0("add_", x)]], {

                                print(paste0("add_", x))

                        })

                }) 
        })

        observeEvent(input$go, {

                output$ui <- renderUI({

                        num <- as.numeric(isolate(input$numButtons))

                        rows <- lapply(1:num, function (x) {

                                actionButton(inputId = paste0("add_", x), 
                                         label = paste0("add_", x))

                        })

                        do.call(fluidRow, rows)

                })

                makeObservers()

        })


}

shinyApp(ui, server)

上面代码的问题是不知何故创建了几个观察者,但他们都只将列表中的最后一项作为输入,传递给lapply。因此,如果我生成四个操作按钮,然后单击操作按钮 #4,Shiny 会打印四次其名称,而所有其他按钮都没有反应。

使用lapply生成观察者的想法来自https://github.com/rstudio/shiny/issues/167#issuecomment-152598096

【问题讨论】:

  • 这只是一个注意,仅为列表中的最后一项生成观察者到“lapply”的问题是 Shiny 和我安装的 R 版本之间的兼容性之一。使用最新版本的 R,我的示例代码的行为就像下面的答案所说的那样,并且提供的解决方案有效。

标签: r shiny


【解决方案1】:

在您的示例中,只要仅按下一次 actionButton,一切正常。例如,当我创建 3 按钮/观察者时,我会在控制台中打印正确的 ID - 每个新生成的 actionButton 都有一个观察者。 √

[1] "add_1"
[1] "add_2"
[1] "add_3"

但是,当我选择3以外的号码然后再次按submit时,您描述的问题就开始了。

说,我现在想要4 actionButtons - 我输入4 并按下submit。之后,我按一次每个新生成的按钮,然后得到以下输出:

[1] "add_1"
[1] "add_1"
[1] "add_2"
[1] "add_2"
[1] "add_3"
[1] "add_3"
[1] "add_4"

通过单击submit 按钮,我再次为三个第一个按钮创建了观察者——我为前三个按钮创建了两个观察者,而对于新的第四个按钮只有一个观察者。

我们可以不断地玩这个游戏,并为每个按钮吸引越来越多的观察者。当我们创建的按钮数量比以前少时,情况非常相似。


对此的解决方案是跟踪已定义的操作按钮,然后仅为新按钮生成观察者。在下面的示例中,我描述了如何做到这一点。它可能不是最好的编程,但它应该可以很好地展示这个想法。

完整示例:

library("shiny")

ui <- fluidPage(
  numericInput("numButtons", "Number of buttons to generate",
                min = 1, max = 100, value = NULL),  
  actionButton("go", "Submit"), 
  uiOutput("ui")
)

server <- function(input, output) {

  # Keep track of which observer has been already created
  vals <- reactiveValues(x = NULL, y = NULL)

  makeObservers <- eventReactive(input$go, {

    IDs <- seq_len(input$numButtons)

    # For the first time you press the actionButton, create 
    # observers and save the sequence of integers which gives
    # you unique identifiers of created observers
    if (is.null(vals$x)) { 
      res <- lapply(IDs, function (x) {
        observeEvent(input[[paste0("add_", x)]], {
          print(paste0("add_", x))
        })
      })
      vals$x <- 1
      vals$y <- IDs
    print("else1")

    # When you press the actionButton for the second time you want to only create
    # observers that are not defined yet
    #

    # If all new IDs are are the same as the previous IDs return NULLL
    } else if (all(IDs %in% vals$y)) {
        print("else2: No new IDs/observers")
        return(NULL)

    # Otherwise just create observers that are not yet defined and overwrite 
    # reactive values 
    } else {
        new_ind <- !(IDs %in% vals$y)
        print(paste0("else3: # of new observers = ", length(IDs[new_ind])))
        res <- lapply(IDs[new_ind], function (x) {
          observeEvent(input[[paste0("add_", x)]], {
            print(paste0("add_", x))
          })
        })
        # update reactive values
        vals$y <- IDs
    }
    res
  })


  observeEvent(input$go, {

    output$ui <- renderUI({

      num <- as.numeric(isolate(input$numButtons))

      rows <- lapply(1:num, function (x) {

        actionButton(inputId = paste0("add_", x),
                     label = paste0("add_", x))

      })

      do.call(fluidRow, rows)

    })
    makeObservers()
  })

}
shinyApp(ui, server)

【讨论】:

    猜你喜欢
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 2012-07-17
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多