2

I have an application where, during a long computation, I want to display a message. While I love this SO answer, I find that condition to be too broad; I want more fine-grained control over when that (or any) message shows up.

My natural inclination is to build on a previous SO question I asked whose solution I use frequently to show and show/hide things bases on other user choices. I often take it one step further by holding a "switching" variable in reactiveValues for the controlling output reactive to depend on; here's a small example:

# app.R
library(shiny)

ui <- shinyUI(fluidPage(
  titlePanel("Switch test"),
  sidebarLayout(
    sidebarPanel(
      actionButton("action", "show/hide output")
    ),
    mainPanel(
      conditionalPanel(
        condition = "output.show",
        h5("HELLO!")
      )
    )
  )
))

server <- shinyServer(function(input, output) {

  vals <- reactiveValues(
    show = TRUE
  )

  output$show <- reactive(vals$show)
  outputOptions(output, "show", suspendWhenHidden = FALSE)

  observeEvent(input$action, {
    vals$show <- !(vals$show)
  })
})

shinyApp(ui = ui, server = server)

Press the button, message goes away. Press again, message comes back.

Now if I have a long operation triggered by the button, I thought I could switch the vals$show before and after to temporarily hide the message:

  observeEvent(input$action, {
    vals$show <- FALSE
    message(paste("vals$show, before:", vals$show))

    Sys.sleep(5)  # long computation

    vals$show <- TRUE
    message(paste("vals$show, after:", vals$show))
  })

But what actually happens is, while the messages display the expected values, the output$show doesn't seem to update until after the whole block has finished executing. Why is that, shouldn't the change in vals$show trigger output$show to recompute immediately?

Some other solutions I've tried include using <<-, using a plain observe, and using multiple observeEvents (one to flip the switch, and the other to do the computation and then flip the switch back). None of these work!

What's the correct way to implement this functionality? How can I make output$show update when I want it to?

ClaytonJY
  • 1,244
  • 10
  • 21
  • 1
    It seems that even though the values are changed in real time, ui updates are not done until after the whole `observeEvent` is finished. I am guessing that you can either use Javascript directly to read the values and update the interface, or use a combination of `renderUI` and `reactivePoll` (I couldn't make it work yet, but I think it can be done). – Xiongbing Jin Mar 31 '16 at 19:30
  • BTW I think this last answer from your linked question http://stackoverflow.com/a/22076307/4190526 should work, just change the show/hide code to something that displays a message. – Xiongbing Jin Mar 31 '16 at 19:48
  • @warmoverflow The answer in that link is too JS-heavy to be ideal, so I'd love a more native way to accomplish this. I can't get `renderUI` or `reactivePoll` to work separately, and it's unclear how I'd combine them. If you come up with a way, please post the answer! Would also appreciate any explanation as to _why_ the UI doesn't update until the `observeEvent` finishes, as this behavior doesn't seem well documented, and has caught me by surprise. – ClaytonJY Mar 31 '16 at 21:16
  • I just noticed `withProgress` function which is an alternative (not exactly what you want, but very easy to add a status indicator when you have calculation in progress. `observeEvent(input$button, { withProgress(message = "calculation in progress", detail = "This may take a while", value = 0, { incProgress(1) Sys.sleep(5) }) })` – Xiongbing Jin Apr 01 '16 at 02:27
  • @warmoverflow you shouldn't use that function without a deterministic loop or at least constant-time operation. If the computation takes longer than the estimate (manifested as the arguments to `incProgress` and `Sys.sleep`), then the progress will hang at 100 for some time, which is not an acceptable user experience. – ClaytonJY Apr 06 '16 at 17:44

0 Answers0