UPDATE: Changed example to a (hopefully) less abstract one
I have a shiny app with two tabs using the mtcars dataset. "Displacement" contains a scatterplot showing disp against mpg, "horsepower" contains a scatterplot showing hp against mpg. Both tabs contain a checkbox group input that allows the user to filter the results based on the number of cylinders. The "Generate" button generates the graph so that it doesn't change immediately as soon as the input is changed.
Each tab has two additional buttons. "Compare" is a switching button that copies the values in the checkbox inputs of one tab to the inputs of the other tab and generates the resulting plot, so you can easily compare how both tabs look for the same input. "Restore Defaults" resets a checkbox input to its initial value.
Both of these buttons work in the same manner. Each button has its own observer that triggers an Update function once the button is pressed. This function first updates the input, then generates the plot by updating a reactive value created for this purpose, then switches to the tab containing that plot.
However, only the Compare buttons work correctly, updating the inputs and generating the graphs with a single click. The Restore Default buttons, on the other hand, only update the inputs. You need to press it twice to update the plots as well.
I have tried to move a Restore Default button from one tab to another and then it does update the plot on the first click, so the behavior changes based on whether or not the affected plot is in the same tab as the button (app is provided below so you can try this for yourself). I am baffled by this behavior, because the observeEvent() functions linking the buttons to the update functions are coded identically. It should not matter in which tab a button is located.
Can anyone explain what causes this and what I should change to make both buttons update the checkbox input and the plot output in a single click?
library(shiny)
library(ggplot2)
ui <- fluidPage(
tabsetPanel(id="App",
tabPanel(
"displacement",
checkboxGroupInput("cyl1", "Number of cylinders", choices=c("4","6","8"), selected="4"),
actionButton("genDisp", "Generate"),
actionButton("switchToHp", "Compare"),
actionButton("resDefDisp", "Restore Default"),
plotOutput("disp")
),
tabPanel(
"horsepower",
checkboxGroupInput("cyl2", "Number of cylinders", choices=c("4","6","8"), selected="8"),
actionButton("genHp", "Generate"),
actionButton("switchToDisp", "Compare"),
actionButton("resDefHp", "Restore Default"),
plotOutput("hp")
)
)
)
server <- function(input, output, session){
# reactive values used to trigger plot generation
generateDisp <- reactiveVal(0)
generateHp <- reactiveVal(0)
# "Generate" Buttons
observeEvent({
input$genDisp
},
{
generateDisp(generateDisp()+1)
})
observeEvent({
input$genHp
},
{
generateHp(generateHp()+1)
})
# "Compare"/"Restore Defaults" buttons
observeEvent({
input$switchToHp
},{
updateHp(cyl=input$cyl1)
})
observeEvent({
input$resDefDisp
},{
updateDisp(cyl="4")
})
observeEvent({
input$switchToDisp
},{
updateDisp(cyl=input$cyl2)
})
observeEvent({
input$resDefHp
},{
updateHp(cyl="8")
})
# Functions updating graphs
updateDisp <- function(cyl=NULL)
{
updateCheckboxGroupInput(session, "cyl1", "Number of cylinders", selected = cyl)
generateDisp(generateDisp()+1)
updateTabsetPanel(session, "App", selected = "displacement")
}
updateHp <- function(cyl=NULL)
{
updateCheckboxGroupInput(session, "cyl2", "Number of cylinders", selected = cyl)
generateHp(generateHp()+1)
updateTabsetPanel(session, "App", selected = "horsepower")
}
# output plots
output$disp <- renderPlot({
generateDisp()
isolate({
data <- filter(mtcars, cyl %in% input$cyl1) %>%
mutate(cyl=as.factor(cyl))
ggplot(data, aes(x=mpg, y=disp, color=cyl)) +
geom_point()
})
})
output$hp <- renderPlot({
generateHp()
isolate({
data <- filter(mtcars, cyl %in% input$cyl2) %>%
mutate(cyl=as.factor(cyl))
ggplot(data, aes(x=mpg, y=hp, color=cyl)) +
geom_point()
})
})
}
shinyApp(ui, server)