Render a plot only when the user selects a button

Viewed 58

I have a somewhat detailed plot that allows a user pretty much total control over what is shown, including which database to use, what the x and y axis are, faceting, and filtering the data based on multiple factor levels. It all works great.

The problem is that right now, every time the user makes a change to ANY of the options, the plot is redrawn. Since the data are quite large, it takes a noticeable amount of time for it to redraw the plot after each change. Since a user might want to make a bunch of changes in quick succession, it's pretty annoying. For example, I have one selection that defaults to showing 10 of 15 possible levels. If the user only want to show one of those levels, they'll delete the other 9. That means the plot redraws itself 10 times, when all we care about is the last one.

What I'd like to do is allow the user to make as many selections as they want, but only redraw the plot when they hit an "update" button. I've tried implementing this, but I can't get it to work. I know wrapping a plot in an observeEvent is bad practice, but I tried it and it doesn't work, as any input$ forces it to update anyways. If I wrap just the data filtering, that data will get filtered correctly (tested with str()), but never actually updates in the plot. Plus that doesn't allow the user to change the axis.

How does one create a customizable plot that only updates when the user says so?

EDIT: As basic of reproduceable code as I can manage that shows the problem:

library(shiny)
library(tidyverse)

ui <- sidebarLayout (
   sidebarPanel (
      selectizeInput (
         inputId= "userXAxis",
         label= "X Axis",
         choices= colnames(mpg[c(1,4,11)]),
         selected= "year"
      ),
      selectizeInput (
         inputId= "userYAxis",
         label= "Y Axis",
         choices= colnames(mpg[c(3,5,8,9)]),
         selected= "cty"
      ),
      actionButton(
         inputId = "updateButton",
         label = "Update"
      )
   ),
   mainPanel (
      plotOutput("graph1")
   )
)

server <- function(input, output) {
   observeEvent(input$updateButton,
   output$graph1 <- renderPlot({

      ggplot() +
      geom_point (
         mapping = aes (
            x = switch (
               input$userXAxis,
               "manufacturer" = mpg$manufacturer,
               "year" = mpg$year,
               "class" = mpg$class
            ),
            y = switch (
               input$userYAxis,
               "displ" = mpg$displ,
               "cyl" = mpg$cyl,
               "cty" = mpg$cty,
               "hwy" = mpg$hwy
            )
         )
      ) +
      labs (
         x = input$userXAxis,
         y = input$userYAxis
      )
   })
   )
}

shinyApp(ui = ui, server = server)

I know the observeEvent is not where it should be, but no matter where I put it, the plot redraws whenever the user selects any change. I want it to update only when the update button is pressed.

1 Answers

A solution using isolate:

server <- function(input, output) {

  scatterplot <- reactive({
    input[["updateButton"]]
    gg <- 
      ggplot() +
      geom_point (
        mapping = aes (
          x = switch (
            isolate(input$userXAxis),
            "manufacturer" = mpg$manufacturer,
            "year" = mpg$year,
            "class" = mpg$class
          ),
          y = switch (
            isolate(input$userYAxis),
            "displ" = mpg$displ,
            "cyl" = mpg$cyl,
            "cty" = mpg$cty,
            "hwy" = mpg$hwy
          )
        )
      ) +
      labs (
        x = isolate(input$userXAxis),
        y = isolate(input$userYAxis)
      )
    gg
  })

  output[["graph1"]] <- renderPlot({
    scatterplot()
  })

}

shinyApp(ui = ui, server = server)
Related