Reputation: 465
I'm currently working on a shiny App which makes time series data from sensors accessible. To customize the plot of the data, I use a slider (left) which limits the plot by being called in xlim()/scale_x_datetime() in ggplot. The plot is updated via a "Update Plot"-Button, since some recalculations have to be done if settings are changed. This all worked fine, until I called the input of the slider not only in ggplot, but also in the observeEvent() which is triggered by the "update Plot". Since then, the state of the slider resets itself each time I press the "update Plot" button. I guess the code re-runs the renderUI block with the sliders inside which sets the slider to its default again.
At the moment I am using a hacky workaround which stores the current slider setting in a reactiveValue and gives it back to the slider, but this still has some flaws and I am pretty sure the slider normally shouldn't reset when the code is being rerun.
simplified version of the server code:
data <- reactiveValues()
observeEvent(input$update_plot, {
#some code which gathers info
data$one <- data_function(raw_data, input$slider, ...)
})
output$slider <- renderUI({
req(data$one)
#calculate min and max from data$one
#evaluate some conditions to determine the values of slider (datetime or continuous)
if(is.null(input$slider)) {
value <- c(data_time_min, data_time_max)
} else {
value <- input$slider
}
if(input$checkbox_rel_time) { #check what time format should be used
sliderInput("slider", "Zeitraum:", min = data_min_rel, max = data_time_rel, value = value)
} else {
sliderInput("slider", "Zeitraum:", min = data_time_min, max = data_time_max, value = value, timezone = "GMT")
}
}
As you can see, the observeEvent and output$slider depend on each other (output needs "data", observeEvent needs "input$slider"). Maybe this causes a problem? I guess what I need is to prevent that the use of input$slider triggers the renderUI, but isolate() didn't do the trick.
I've already spent a lot of hours trying to solve this, so I would be really greatful for any help :)
Upvotes: 1
Views: 177
Reputation: 465
As per suggestion from ismirsehregal I tried to use updateSliderInput() instead of renderUI. With some extra steps this solved my problem. I took the time to set up a minimal reproducible example of the solution for anyone with the same problem.
To solve the problem, that I want to change the min, max and values when my x data type changes, I used observeEvent({list(input$checkbox)}
which changes a reactiveValue vals$slider_changed to TRUE.
I do still have the problem that an error is shown in the console, when I first use the "update plot", since I initialized the slider with c(0,1) and ggplot expects data of the class POSIXct:
Warning: Error in : Invalid input: time_trans works with objects of class POSIXct only
I could just initialize the slider with a datetime, but this wouldn't work as well when I don't know the start and end date of the data I am going to get.
library(shiny)
ui <- fluidPage(
sliderInput("daterange", "Zeitraum:", min = 0, max=1, value = c(0,1), ticks = F, animate = F, timezone = "GMT"),
checkboxInput("check_time_rel", "relative Time?"),
actionButton("update_plot", "Update Plot"),
plotOutput("plot")
)
server <- function(input, output, session) {
data <- reactiveValues()
vals <- reactiveValues()
vals$slider_changed <- TRUE
data$one <- data.frame(time = as.POSIXct(c("2022-08-12 09:00:08 GMT", "2022-08-12 09:00:18 GMT", "2022-08-12 09:00:28 GMT", "2022-08-12 09:00:38 GMT", "2022-08-12 09:00:48 GMT", "2022-08-12 09:00:58 GMT", "2022-08-12 09:01:08 GMT", "2022-08-12 09:01:18 GMT", "2022-08-12 09:01:28 GMT", "2022-08-12 09:01:38 GMT")),
value = c(0.4201216,0.4350491,0.4183712,0.4353558,0.4405609,0.4368317,0.4544089,0.4650647,0.4785052,0.4869763))
observeEvent(input$update_plot, {
vals$check_time_rel <- input$check_time_rel
if(input$check_time_rel) {
data$one <- data$one %>%
dplyr::mutate(time_rel = as.numeric(time - time[1], units = "secs"))
}
print(data$one)
if(vals$slider_changed) {
time_column <- dplyr::pull(data$one, time)
time_min <- min(as.POSIXct(time_column))
time_max <- max(as.POSIXct(time_column))
time_min_rel <- 0
if (vals$check_time_rel) {
time_max_rel <- round(as.numeric(time_max - time_min, units = "secs"), digits = 2)
value <- c(time_min_rel, time_max_rel)
} else {
value <- c(time_min, time_max)
}
if (vals$check_time_rel) {
updateSliderInput(session, "daterange", min = time_min_rel, max=time_max_rel, value = value, timezone = NULL)
} else {
updateSliderInput(session, "daterange", min = time_min, max=time_max, value = value, timezone = "GMT")
}
}
vals$slider_changed <- FALSE
})
observeEvent({list(input$check_time_rel)}, {
vals$slider_changed <- TRUE
})
output$plot <- renderPlot({
print(input$daterange)
if(input$check_time_rel) {
x = as.name("time_rel")
} else {
x = as.name("time")
}
g <- ggplot2::ggplot(data = data$one, ggplot2::aes_(x = x, y = ~value)) +
ggplot2::geom_line()
if(input$check_time_rel) {
s <- g + ggplot2::scale_x_continuous(limits = input$daterange)
} else {
s <- g + ggplot2::scale_x_datetime(limits = input$daterange)
}
s
})
}
shinyApp(ui, server)
note: you can remove the ggplot2:: and dplyr:: by calling library(ggplot2) and library(dplyr), I use the former variant because I use golem as a framework.
Upvotes: 0