生成动态输入数量的观测器 [英] Generate observers for dynamic number of inputs
本文介绍了生成动态输入数量的观测器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我有一个我认为是非常简单的用例,但我还没有找到解决方案:我想让SHINY生成用户指定数量的输入,为每个输入动态创建一个观察者。
在下面最小的可重现代码中,用户通过在textInput
小部件中键入来指示所需的操作按钮的数量;然后他或她按下"Submit",这将生成操作按钮。
我想要的是让用户能够点击任何操作按钮并生成特定于它的输出(例如,在最小的情况下,只打印按钮的名称):
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,Slight会打印它的名称四次,而其他所有按钮都没有反应。
使用lapply
生成观察者的想法来自https://github.com/rstudio/shiny/issues/167#issuecomment-152598096
推荐答案
在您的示例中,只要一个actionButton只被按下一次,一切都可以正常工作。例如,当我创建3
按钮/观察者时,我会在控制台中打印出正确的ID--每个新生成的actionButton都有一个观察者。√
[1] "add_1"
[1] "add_2"
[1] "add_3"
但是,当我选择3
以外的数字,然后再次按submit
时,您描述的问题开始了。
比方说,我现在要4
actionButton-I输入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)
这篇关于生成动态输入数量的观测器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文