2016-08-15 44 views
6

我有我认为是一个非常简单的用户案例,我一直无法找到解决方案:我希望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

+0

这只是一张纸条,只有列表中的过去“lapply”的最后一个项目产生观察员的问题是闪亮之间的兼容性和R版本我已经安装了。使用最新版本的R,我的示例代码的行为就像下面的答案所表明的那样,并且提供的解决方案可以工作。 –

回答

4

在你的榜样一切产生观察家正常工作这么长时间的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)