QuillStem
QuillStem

Reputation: 73

How do I make a Shiny reset button that resets input values then updates data?

I have an app that has a lot of data behind it, and a suite of filters that a user can apply to slice and dice that data and get a series of outputs.

Because of the size of the dataset, each time filters are selected there is an understandable delay while R processes the newly filtered data. So, I implemented an 'update model' button that applies all of the changes at once (rather than individually as the filters are selected, because all the lags add up).

I also have a reset button that allows all of the filters to be completely reset at once. Ideally, the behaviour of this button would be to first update all of the input values to their defaults, and then to update the data, so that all of the outputs are updated with the default values (i.e. the equivalent of manually selecting the defaults and then pressing the 'update' button). Both buttons increment a reactive value that is observed and triggers the data update, the only difference being that when the reset button is pressed the input values are reset first (in theory), and then the observed reactive value is incremented.

However when the reset button is pressed, the values are reset, but the reactive outputs are not updated. The update button then has to be pressed (or the reset button again) to get the outputs to update. What appears to be happening is that values are reset after the data update has been triggered, and so the newly reset values do not get passed through.

I have attempted experimenting with priority values in the observeEvent functions, and with moving the UI to renderUI functions inside server. I have also used bindEvent to attempt to make sure the data update happens after the reset.

EDIT: I cannot use session$reload() which was sensibly suggested by jpdugo17 because the app has multiple pages and I only want to reset the filters on the current page.

The original app is not practical to share, but I have scripted a strawman of the problem using the default Shiny with the same logic (or lack thereof!), although it lacks a navbar structure with multiple pages like the real app has:

app.R

library(shiny)

ui <- fluidPage(
    titlePanel("Old Faithful Geyser Data"),
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30),
            actionButton("reset","RESET"),
            actionButton("update","UPDATE")
        ),
        mainPanel(
           plotOutput("distPlot")
        )
    )
)

server <- function(input, output,session) {
# Create values to use control the reset/update function
dat <- reactiveValues(bins=30,push_update=0)

# When reset button is pressed, reset the slider value and send a 'push update' signal
observeEvent(input$reset,{
    updateSliderInput(session=session,"bins",value=30)
    dat$push_update=dat$push_update+1
})

# When update button is pressed, send a 'push update' signal
observeEvent(input$update,{
    dat$push_update=dat$push_update+1
})

# When a 'push update' is received update the value used in the model  
observeEvent(dat$push_update,{
    dat$bins <- input$bins
})

output$distPlot <- renderPlot({
    x    <- faithful[, 2]
    bins <- seq(min(x), max(x), length.out = dat$bins + 1)

    hist(x, breaks = bins, col = 'darkgray', border = 'white')


 })
    
}

shinyApp(ui = ui, server = server)

I hope there is a way to implement including both the reset and update buttons. Any help will be greatly appreciated. I'm aware there are a number of other questions involving reset buttons, but I can't see any that fit this problem.

Upvotes: 7

Views: 1834

Answers (1)

jpdugo17
jpdugo17

Reputation: 7106

actionButton() function has an internal value that increments by 1 each time it is pressed. For resetting the app, i can think of using session$reload() if there are no other tabs you need to conserve. If only a portion of the ui needs to reset, then you can call renderUI inside observeEvent.

Finally, for the update button we can use the input values but isolate them so the only thing that triggers an update is clicking the button and not changing in this case the slider.

library(shiny)

tab1_ui <- tagList(
  sidebarLayout(
    sidebarPanel(
      sliderInput("bins",
                  "Number of bins:",
                  min = 1,
                  max = 50,
                  value = 30),
      actionButton('reset', 'RESET'),
      actionButton("update","UPDATE"),
    ),
    mainPanel(
      plotOutput("distPlot")
    ) )
)

ui <- navbarPage(
  title = "Old Faithful Geyser Data",
  tabPanel('Tab 1',
           uiOutput('tab_1_ui')),
  tabPanel('Tab 2',
           selectInput('iris_nms', 'Select Column', names(iris)))
)

server <- function(input, output,session) {
  
  #show the ui at the start of the app.
  output$tab_1_ui <- renderUI({
    tab1_ui
  })
  
  # When reset button is pressed, reset the slider value and send a 'push update' signal
  observeEvent(input$reset,{
    output$tab_1_ui <- renderUI({
      tab1_ui
    })
  })
  
  # When update button is pressed, send a 'push update' signal
  observe({
    input$update
    input$reset
    output$distPlot <- renderPlot({
      x    <- faithful[, 2]
      bins <- seq(min(x), max(x), length.out = isolate(input$bins[1]))
      hist(x, breaks = bins, col = 'darkgray', border = 'white')
    })
    
  })
  
  
}

shinyApp(ui = ui, server = server)

Upvotes: 6

Related Questions