4

I have a loading spinner in shiny which is implemented similarly to this answer:

conditionalPanel(condition="$('html').hasClass('shiny-busy')",
                 tags$div("Loading...",id="loadmessage")
)



runApp(list(
  ui = pageWithSidebar(
      headerPanel("Test"),
         sidebarPanel(
           tags$head(tags$style(type="text/css", "
             #loadmessage {
               position: fixed;
               top: 0px;
               left: 0px;
               width: 100%;
               padding: 5px 0px 5px 0px;
               text-align: center;
               font-weight: bold;
               font-size: 100%;
               color: #000000;
               background-color: #CCFF66;
               z-index: 105;
             }
          ")),
           numericInput('n', 'Number of obs', 100),
           conditionalPanel(condition="$('html').hasClass('shiny-busy')",
                            tags$div("Loading...",id="loadmessage"))
         ),
         mainPanel(plotOutput('plot'))
  ),
  server = function(input, output) {
    output$plot <- renderPlot({ Sys.sleep(2); hist(runif(input$n)) })
  }
))

The issue I'm having is that the loader comes up all the time, even when shiny is busy for just a fraction of a second. This leads to the app blinking in and out all the time. Is there a way to basically set a delay on the conditional panel so that the spinner only comes up after the page is busy for a second?

Community
  • 1
  • 1
Shorpy
  • 1,549
  • 13
  • 28

3 Answers3

6

If I understood you right, the task is to show specific class and "busy" message after certain delay of "busyness". The spinner has to be shown only for significant busy times (maybe longer than a second).

This could be easily achieved with debounce concept. It has implementation in many libraries, here's the lodash debounce implementation, for example.

I will not provide the code snippets, it's up to how to integrate in your code, but will provide the pseudo code, so you understand how to use it:

// flag of busyness
var isBusy = false;

// ...
// operation in progress, start the counting
isBusy = true;
_.debounce(showSpinner, 1000, {
   'trailing': true             // we need to trigger only when 1 seconds interval passed from last iteration
}));

// ... when done
hideSpinner();

// will be debounced after 1 second interval, and if still busy - the spinner will be shown
var showSpinner = function() {
    if (isBusy) {
        $('selector').addClass('shiny-busy');
    }
}

var hideSpinner = function() {
   isBusy = false;        // our external variable is used
   if ($('selector').hasClass('shiny-busy')) {
       $('selector').removeClass('shiny-busy');
   }
}

The pseudo-code is just to illustrate the concept, but hopefully it will explain you how to use it.

Farside
  • 9,923
  • 4
  • 47
  • 60
2

I came across this package:shinysky. (here's the github)

It has a busyIndicator element and you can set how long it should wait before appearing, you can just add busyIndicator(wait=1000) to your ui.R.

You can also look at the code of the function by running busyIndicator in R, it's basically some js code using setTimeout.

NicE
  • 21,165
  • 3
  • 51
  • 68
0

I am a little confused what I am looking at, but in general, it looks like you are relying on $('html').hasClass('shiny-busy') to determine when to show it, so whatever is applying the "shiny-busy" class, just delay it by a second or whatever.

Macainian
  • 362
  • 2
  • 10
  • Yup, that's what's happening. I'm looking for an idea of how to make it only show the spinner for signifiant busy times (maybe longer than a second) – Shorpy Feb 23 '16 at 17:13
  • But that is exactly what I am talking about. "significant" just means wait a little before showing it so if it finishes loading before the threshold, the spinner won't show, but if the loading passes the threshold, then the spinner will show. If you still need help, please paste the code that applies the "shiny-busy" class into your question and I will take a look. – Macainian Feb 23 '16 at 20:30
  • Sorry I might be misunderstanding. The `shiny-busy` class comes from Shiny I think, so I don't actually have the code (or at lease would prefer to not try to alter the `shiny-busy` class. – Shorpy Feb 24 '16 at 17:24
  • Well I suppose what I mean is that you are doing something to make it "busy" right? But how could Shiny know that it is "busy" without you explicitly doing something to call a "busy" signal? – Macainian Feb 24 '16 at 22:45
  • @Macainian, find out my answer above. Thank you for the discussion, it helped to understand what's needed. – Farside Feb 29 '16 at 13:27