R Shiny - 在 shinyjs::toggleState 中使用 JQuery 选择器禁用动态创建的按钮
R Shiny - Using JQuery selectors in shinyjs::toggleState to disable dynamically created buttons
下面的应用程序包含一个模块,每次单击 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 未知,因此我使用 selector
参数toggleState
而不是 id
参数。我尝试了以下 selectors
、none 的变体,其中 none 似乎有效:
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)
.
为按钮设置 class:
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)"))
}
下面的应用程序包含一个模块,每次单击 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 未知,因此我使用 selector
参数toggleState
而不是 id
参数。我尝试了以下 selectors
、none 的变体,其中 none 似乎有效:
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)
.
为按钮设置 class:
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)"))
}