inputshinyshinyjsdisableshinywidgets

R Shiny: enabling/disabling shinyWidget using shinyjs


I have a dropdown with two date presets and one "Custom" preset.

The "custom" preset should enable the user to use the date picker input to select a custom date range. If any other preset is selected, the date picker input should be disabled.

I start by disabling the element with shinyjs::disabled() as suggested here. Then I run shinyjs::disable() or shinyjs::enable(), depending on what preset is selected in the dropdown.

This is working as intended if I use disable/enable on a button, but for some reason the same does not work for the shinyWidget. It remains disabled when I select "Custom", even though shinyjs::enable() is triggered.

What am I missing?

Example:

library(shiny)
library(shinyWidgets)
library(shinyjs)

ui <- fluidPage(
  shinyjs::useShinyjs(),

  ## pick a date preset
  shinyWidgets::pickerInput(
    inputId = "date_preset",
    choices = c("Preset 1", "Preset 2", "Custom"),
    selected = "Preset 1"
  ),

  ## custom date picker widget
  uiOutput("ui_date_calendar"),

  ## test button
  uiOutput("ui_testbutton"),
)


server <- function(input, output, session) {

  # get date ranges for presets
  date_range <- reactive({

    shinyjs::disable("ui_date_calendar") # working
    shinyjs::disable("ui_testbutton") # working

    if (input$date_preset == "Custom"){
      shinyjs::enable("ui_date_calendar") #NOT working
      shinyjs::enable("ui_testbutton") #working
      return(NULL)
    }

    if (input$date_preset == "Preset 1"){
      return(c("2024-01-01", "2024-02-01"))
    }

    if (input$date_preset == "Preset 2"){
      return(c("2024-02-01", "2024-03-01"))
    }

  })


  # generate date picker UI
  output$ui_date_calendar <- renderUI(
    shinyjs::disabled(shinyWidgets::airDatepickerInput(
      inputId = "date_picker",
      value = date_range(),
      range = TRUE,
      placeholder = "Click to pick a date range"
    ))
  )

  # generate button UI
  output$ui_testbutton <- renderUI(
    shinyjs::disabled(actionButton("testbutton", "TEST"))
  )


}


shinyApp(ui = ui, server = server)



Solution

  • I didn't experiment, but it looks strange to me to execute shinyjs::disable/enable on the uiOutput; I would rather execute it on the widget contained in the uiOutput, e.g. shinyjs::enable("testbutton") instead of shinyjs::enable("ui_testbutton").

    Now there's a problem here:

      output$ui_date_calendar <- renderUI(
        shinyjs::disabled(shinyWidgets::airDatepickerInput(
          inputId = "date_picker",
          value = date_range(),
          range = TRUE,
          placeholder = "Click to pick a date range"
        ))
      )
    

    This renderUI always renders a disabled date picker. So, if you enable it but if date_range() changes, it is re-rendered in the disabled state. A solution to initially disable it is:

      # generate date picker UI
      output$ui_date_calendar <- renderUI(
        shinyWidgets::airDatepickerInput(
          inputId = "date_picker",
          value = date_range(),
          range = TRUE,
          placeholder = "Click to pick a date range"
        )
      )
      
      # initially disable the date picker
      observeEvent(input$date_picker, {
        shinyjs::disable("date_picker")
      }, once = TRUE) # destroy this observer after first time it is executed
    

    Finally here is an app in which everything works as expected:

    library(shiny)
    library(shinyWidgets)
    library(shinyjs)
    
    ui <- fluidPage(
      shinyjs::useShinyjs(),
      
      ## pick a date preset
      shinyWidgets::pickerInput(
        inputId = "date_preset",
        choices = c("Preset 1", "Preset 2", "Custom"),
        selected = "Preset 1"
      ),
      
      ## custom date picker widget
      uiOutput("ui_date_calendar"),
      
      ## test button
      uiOutput("ui_testbutton")
    )
    
    
    server <- function(input, output, session) {
      
      date_range <- reactiveVal()
      
      observeEvent(input$date_preset, {
        if (input$date_preset == "Custom"){
          shinyjs::enable("date_picker") 
          shinyjs::enable("testbutton")
          date_range(NULL)
        } else if (input$date_preset == "Preset 1"){
          date_range(c("2024-01-01", "2024-02-01"))
        } else if (input$date_preset == "Preset 2"){
          date_range(c("2024-02-01", "2024-03-01"))
        }
      })
    
      # generate date picker UI
      output$ui_date_calendar <- renderUI(
        shinyWidgets::airDatepickerInput(
          inputId = "date_picker",
          value = date_range(),
          range = TRUE,
          placeholder = "Click to pick a date range"
        )
      )
      
      # initially disable the date picker
      observeEvent(input$date_picker, {
        shinyjs::disable("date_picker")
      }, once = TRUE) # destroy this observer after first time it is executed
      
      # generate button UI
      output$ui_testbutton <- renderUI(
        shinyjs::disabled(actionButton("testbutton", "TEST"))
      )
      
    }
    
    shinyApp(ui = ui, server = server)