R Shiny-在Shinyjs :: toggleState中使用JQuery选择器禁用动态创建的按钮

时间:2019-06-07 02:12:30

标签: jquery r shiny shinyjs

下面的应用程序包含一个模块,该模块在每次单击Add按钮时插入一个UI对象。该对象包含一个selectInput和一个Remove按钮,该按钮将删除UI对象:

enter image description here

如果DOM中仅剩一个Remove,我想禁用selectInput按钮。

为此,我跟踪1)使用计数器rv$ct插入了多少输入,以及2)在rv$rmvd内部删除了多少输入。我设置了一个观察者,它监听rv$ctlength(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)

1 个答案:

答案 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)"))
}