R Shiny: enabling/disabling shinyWidget using shinyjs

38 Views Asked by At

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)


2

There are 2 best solutions below

0
Stéphane Laurent On BEST ANSWER

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)
0
YBS On

Perhaps you could use updateAirDateInput to update the date ranges, and then it gets enabled. Try this

server <- function(input, output, session) {
  
  observeEvent(input$date_preset, {
    if (input$date_preset == "Custom"){
      shinyjs::enable("ui_date_calendar") # working
      shinyjs::enable("ui_testbutton") #working
      updateAirDateInput(session,"date_picker",value=date_range())
    } else {
      updateAirDateInput(session,"date_picker",value=date_range())
      shinyjs::disable("ui_date_calendar") # working
      shinyjs::disable("ui_testbutton") # working
    }
  })
  
  # get date ranges for presets
  date_range <- reactive({
    req(input$date_preset)
    
    if (input$date_preset == "Custom"){
      return(NULL)
    } else if (input$date_preset == "Preset 1"){
      return(c("2024-01-01", "2024-02-01"))
    } else if (input$date_preset == "Preset 2"){
      return(c("2024-02-01", "2024-03-01"))
    } else {
      return(NULL)
    }
    
  })
  
  # generate date picker UI
  output$ui_date_calendar <- renderUI({
    shinyjs::disabled(
    shinyWidgets::airDatepickerInput(
      inputId = "date_picker",
      value = c("2024-01-01", "2024-02-01"),
      range = TRUE,
      placeholder = "Click to pick a date range"
    )
    )
  })
  
  # generate button UI
  output$ui_testbutton <- renderUI(
    shinyjs::disabled(actionButton("testbutton", "TEST"))
  )
  
}