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)
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)