下面的应用程序包含一个模块,该模块在每次单击Add
按钮时插入一个UI对象。该对象包含一个selectInput
和一个Remove
按钮,该按钮将删除UI对象:
如果DOM中仅剩一个Remove
,我想禁用selectInput
按钮。
为此,我跟踪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是未知的,因此我使用的是selector
的{{1}}参数而不是toggleState
参数。我尝试了以下id
的变体,但似乎都没有用:
selectors
button[id *= "remove"]
paste0('#', ns('placeholder'), 'button')
让我感到困惑的是,它们似乎在该应用程序的非模块化版本中可以正常工作(见下文):
div.rmvBttn
作为检查,提供完整ID既适用于模块化版本,也适用于非模块化版本,例如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)
。
答案 0 :(得分:1)
为按钮设置类别:
actionButton(Id(ns('remove')), 'Remove', class = 'rmvBttn')
(不适用于包含按钮的div)。
这很奇怪
toggleState(selector = '.rmvBttn', condition = diff > 1)
不起作用。除此之外,您可以执行以下操作:
if(diff <= 1){
delay(1000, runjs("$('.rmvBttn').attr('disabled', true)"))
}else{
delay(1000, runjs("$('.rmvBttn').attr('disabled', false)"))
}