I got an app that when you click on an actionButton
, it should set an input to a certain value; and a click action should be made through shinyjs
. However, only the first thing is happening; and I don't know how to make the second one happen. Or maybe it IS happening; but the data inside the reactive
object is not getting updated.
Heres a minimal reprex:
library(shiny)
ui <- fluidPage(
shinyjs::useShinyjs(),
selectInput("selector", label = "Carb selector", choices = unique(mtcars$carb)),
actionButton("generate", "OK!"),
tableOutput("results"),
actionButton("reset", "RESET"),
)
server <- function(input, output, session) {
data <- reactive({
req(input$generate)
isolate(
mtcars %>% filter(carb == input$selector)
)
})
output$results <- renderTable(data())
observeEvent(input$reset, {
updateSelectInput(session,
inputId = "selector",
label = "Carb selector updated",
choices = unique(mtcars$carb),
selected = 1)
shinyjs::click("generate")#This does not seem trigger when you hit reset!
})
}
shinyApp(ui, server)
The architecture of your code is a bit questionable, but I will point out why it is behaving this way.
To understand this, let's first break the observer on input$reset
into two:
observeEvent(input$reset, {
updateSelectInput(
session,
inputId = "selector",
label = "Carb selector updated",
choices = unique(mtcars$carb),
selected = 1
)
})
observeEvent(input$reset, {
shinyjs::click("generate") # This does not seem trigger when you hit reset!
})
After that, let's have a look at the documentation of updateSelectInput
:
The input updater functions send a message to the client, telling it to change the settings of an input object. The messages are collected and sent after all the observers (including outputs) have finished running.
And there lies the answer. In other words, shinyjs::click("generate")
will always execute before update*Input()
effects kick in.
Basically, this is the flow:
1. User clicks 'reset' btn
2. That leads to a click on 'generate' btn
3. The `data()` reactive is re-evaluated
4. All observers are re-evaluated
5. `update*Input()` sends updates to the client
We can use number 4 above to our advantage by having a reactiveVal()
that always has the most current value of 'selector' and is a step ahead of update*Input()
.
I have made a few changes to your code to make it more explicit and straight to the point.
library(shiny)
library(dplyr)
ui <- fluidPage(
shinyjs::useShinyjs(),
selectInput("selector", label = "Carb selector", choices = unique(mtcars$carb)),
actionButton("generate", "OK!"),
tableOutput("results"),
actionButton("reset", "RESET"),
)
server <- function(input, output, session) {
rv_selector <- reactiveVal()
observeEvent(input$selector, rv_selector(input$selector))
data <- reactive({
mtcars %>% filter(carb == rv_selector())
}) |> bindEvent(input$generate)
output$results <- renderTable(data())
observeEvent(input$reset, {
updateSelectInput(
session,
inputId = "selector",
label = "Carb selector updated",
choices = unique(mtcars$carb),
selected = 1
)
rv_selector(1) # Ensures server is updated before the client
shinyjs::click("generate")
})
}
shinyApp(ui, server)