daattali / shinycssloaders

⌛ Add loading animations to a Shiny output while it's recalculating
https://daattali.com/shiny/shinycssloaders-demo/
Other
395 stars 45 forks source link

Manually start/stop the loader for an output element #56

Closed Prashanthreddya closed 1 year ago

Prashanthreddya commented 3 years ago

Hi,

Is it possible to manually start or stop the loader for an output element using shinycssloaders ? I tried doing this by assigning the corresponding output element with NULL.

output$element <- NULL

This works in the usual scenario. But I am using multiprocessing to fetch data for each element in parallel by using the future module, that is causing the loaders to go off sync when the elements are being rendered.

Recording of how the component & loader come up while running it sequentially (this is the expected behaviour): sequential

Recording of how the component & loader come up while using multiprocessing: parallel

daattali commented 3 years ago

No, that's not possible with shinycssloaders. This package is explicitly designed to be used in the opposite case - to automatically show when an output is recalculating. If you want to do it manually, you'll need to do it with custom css/javascript (you can use these two examples to help you 1 2

avsdev-cw commented 3 years ago

I keep bumping into this myself and keep coming across this issue, so I'm going to leave a memorandum here:

Combine shinycssloaders with shinyjs and manually control the loading:

# To hide:
shinyjs::runjs(paste0("$('#", session$ns("SOME_ID_HERE"), "').trigger('shiny:outputinvalidated')"))
# To show:
shinyjs::runjs(paste0("$('#", session$ns("SOME_ID_HERE"), "').trigger('shiny:value')"))

Yes, this triggers some internal events which only shiny should be generating, but I've yet to come across any issues with any libraries I am using that triggering events in this manner cause issues with.

daattali commented 3 years ago

A lot of packages and custom applications are using these shiny events, it should be fairly safe 👍

daattali commented 3 years ago

Since this is a commonly requested feature, I'm re-opening the issue in case someone wants to contribute a PR.

I'd be happy to accept a PR if (a) the code is minimal and clean, and (b) the API for it is straightforward such that the following would work:

  shinyApp(
    ui = fluidPage(
      actionButton("recalculate", "Recalculate"),
      actionButton("show", "Show spinner"),
      actionButton("hide", "Hide spinner"),
      withSpinner(plotOutput("plot"))
    ),
    server = function(input, output) {
      output$plot <- renderPlot({
        # This is the normal shinycssloaders behaviour
        input$recalculate
        Sys.sleep(1.5)
        plot(runif(10))
      })

      # This is the new functionality
      observeEvent(input$show, { 
        shinycssloaders::showSpinner("plot")
      })
      observeEvent(input$hide, { 
        shinycssloaders::hideSpinner("plot")
      })
    }
  )
bitowaqr commented 3 years ago

It would be great to have a js solution for this. The Sys.sleep(1.5) causes a bit of a delay for everyone who is using the app if you are running it on a shiny open source server.

daattali commented 3 years ago

The sys.sleep was not a solution, it's to create a test. There is no current solution, but yes it would need to be done in javascript.

daattali commented 1 year ago

@Prashanthreddya @avsdev-cw @bitowaqr I've added this feature, it's not on CRAN yet but please install the latest github version and test it.

You can trigger a spinner with showSpinner(id) and stop it with hideSpinner(id). The id must correspond to a shiny output that was wrapped in withSpinner().

I also added a bonus feature here: showSpinner() has an optional argument expr, which if you provide it then the spinner will automatically stop when the given R expression completes.

Please let me know after testing it.

avsdev-cw commented 1 year ago

Seems to be working well for me