【发布时间】:2019-10-22 12:55:44
【问题描述】:
下面的应用程序包含一个模块,每次单击 Add 按钮时都会插入一个 UI 对象。该对象由一个selectInput 和一个Remove 按钮组成,用于移除UI 对象:
如果 DOM 中只剩下一个 selectInput,我想禁用 Remove 按钮。
为此,我会跟踪 1) 使用计数器 rv$ct 插入了多少输入,以及 2) 在 rv$rmvd 中删除了多少。我设置了一个观察者,它监听rv$ct 和length(rv$rmvd) 之间的差异值,并在该观察者内部使用shinyjs::toggleState 来启用Remove 按钮,如果差异大于1。
应用如下:
library(shiny)
library(shinyjs)
# module UI ---------------------------------------------------------------
modUI <- function(id) {
ns = NS(id)
tagList(
actionButton(ns('add'), 'Add'),
fluidRow(div(id = ns('placeholder')))
)
}
# module server -----------------------------------------------------------
modServer <- function(input, output, session) {
ns = session$ns
rv = reactiveValues(ct = 0, rmvd = NULL)
observeEvent(input$add, {
rv$ct = rv$ct + 1
Id = function(id) paste0(id, rv$ct)
insertUI(
selector = paste0('#', ns('placeholder')),
ui = div(
id = Id(ns('inputGroup')),
splitLayout(
cellWidths = '10%',
h4(Id('State ')),
selectInput(Id(ns('state')), 'State:', state.abb),
div(
class = 'rmvBttn',
actionButton(Id(ns('remove')), 'Remove'))
)
)
)
remove_id = Id('remove')
remove_group = Id(ns('inputGroup'))
observeEvent(input[[remove_id]], {
removeUI(selector = paste0('#', remove_group))
rv$rmvd = c(rv$rmvd, str_extract(remove_id, '\\d+$'))
})
})
observe({
diff = rv$ct - length(rv$rmvd)
delay(1000, toggleState(selector = 'div.rmvBttn', condition = diff > 1)) #not working
# Other selectors I have tried that don't work:
# delay(1000, toggleState(selector = paste0('#', ns('placeholder'), 'button'), condition = diff > 1))
# delay(1000, toggleState(selector = 'button[id *= "remove"]', condition = diff > 1))
# Using the id works:
# delay(1000, toggleState(id = 'remove1', condition = diff > 1)) #this works
})
}
# main UI -----------------------------------------------------------------
ui <- fluidPage(
useShinyjs(),
tags$head(tags$style(HTML('.shiny-split-layout > div { overflow: visible; }'))),
modUI('mod')
)
# main server -------------------------------------------------------------
server <- function(input, output, session) {
callModule(modServer, 'mod')
}
# Run app
shinyApp(ui, server)
由于用户可以插入任意数量的输入并随机删除它们,DOM 中最后一个删除按钮的完整 ID 是未知的,因此我使用 toggleState 的 selector 参数而不是 @ 987654337@ 参数。我尝试了以下selectors 的变体,但似乎都没有工作:
button[id *= "remove"]
paste0('#', ns('placeholder'), 'button')
div.rmvBttn
让我感到困惑的是,它们似乎在应用程序的非模块化版本中运行良好(见下文):
library(shiny)
library(shinyjs)
# UI -----------------------------------------------------------------
ui <- fluidPage(
useShinyjs(),
tags$head(tags$style(HTML('.shiny-split-layout > div { overflow: visible; }'))),
tagList(
actionButton('add', 'Add'),
fluidRow(div(id = 'placeholder'))
)
)
# server -------------------------------------------------------------
server <- function(input, output, session) {
rv = reactiveValues(ct = 0, rmvd = NULL)
observeEvent(input$add, {
rv$ct = rv$ct + 1
Id = function(id) paste0(id, rv$ct)
insertUI(
selector = paste0('#placeholder'),
ui = div(
id = Id('inputGroup'),
splitLayout(
cellWidths = '10%',
h4(Id('State ')),
selectInput(Id('state'), 'State:', state.abb),
div(
class = 'rmvBttn',
actionButton(Id('remove'), 'Remove'))
)
)
)
remove_id = Id('remove')
remove_group = Id('inputGroup')
observeEvent(input[[remove_id]], {
removeUI(selector = paste0('#', remove_group))
rv$rmvd = c(rv$rmvd, str_extract(remove_id, '\\d+$'))
})
})
observe({
diff = rv$ct - length(rv$rmvd)
# delay(1000, toggleState(selector = 'div.rmvBttn', condition = diff > 1))
# delay(1000, toggleState(selector = '#placeholder button', condition = diff > 1))
delay(1000, toggleState(selector = 'button[id *= "remove"]', condition = diff > 1))
})
}
# Run app
shinyApp(ui, server)
作为检查,提供完整的 ID 在模块化和非模块化版本中都适用,例如toggleState(id = 'remove2', condition = diff > 1).
【问题讨论】: