countdown
countdown copied to clipboard
Timers aren't connected to JS scripts when generated programatically in Shiny
I am trying to write a Shiny app that allows users to create multiple timers with labels (and some other controls). The plan is to allow them to create an arbitrary number of timers, so I was trying to use renderUI
to generate the timers. They render correctly, but they do not respond to controls. Looking at the inspect tools in the browser, it appears that this is because the newly created timers are not registered with the correct triggers for the javascript controls.
If I load a timer using the hidden
function from shinyjs
, it loads correctly. I am going to try this work around, but it makes the controls a fair bit more cumbersome.
If someone can point me to where the triggers are registered, I would be happy to take a stab at a fix here. I just haven't done enough in that area to track down the code that I need to dig into.
Reproducible example:
library(shiny)
library(countdown)
library(shinyjs)
demo_app <- shinyApp(
ui = fluidPage(
textInput(
"timer_labels"
, "Labels for timers (separate with commas)"
, "Timer 1, Timer 2, Timer 3"
, width = "100%"
)
, actionButton("create_label_timers"
, "Create timers for each label")
, uiOutput("timer_holder")
, div(
h3("This one loads automatically")
, countdown(
id = "countdown_auto"
, class = "inline"
)
)
, useShinyjs()
, h3("This one is hidden to start")
, actionButton("show", "Show")
, hidden(
countdown(
id = "countdown_hidden"
, class = "inline"
)
)
)
, server = function(input, output){
output$timer_holder <- renderUI({
if(input$create_label_timers == 0){
return(p("Click a button above to generate timers"))
}
if(input$timer_labels == ""){
return(p("Please enter labels to use the label method"))
}
in_labels <-
strsplit(input$timer_labels, ",")[[1]]
timer_list <- tagList(lapply(1:length(in_labels), function(this_label_n){
div(
h3(in_labels[this_label_n])
, countdown(
id = paste0("countdown_timer_"
, this_label_n)
, class = "inline"
)
)
}))
return(timer_list)
})
observeEvent(input$show, {
show("countdown_hidden")
})
}
)
runApp(demo_app)
Thanks for the report! It makes sense that dynamically-created timers aren't working in Shiny: the timers are only initialized when the page is loaded
https://github.com/gadenbuie/countdown/blob/c810f16c6fa57294844b595ffae4535136d18810/lib/countdown.js#L439-L446
Adding support for timers in renderUI in Shiny is both non-trivial and not super hard. Thanks for offering to do a PR but I'll try to fix this soon.