20

I have initial loading of data from the DB in the server.R which takes a few seconds. Until this is done, the page displayed is distorted (wrong data in selection box, and weird placing of the boxes, see below). Distorted display

I want to display a different page (or at least different content in my first-displayed tab) until the data is completely loaded.

I thought about doing some kind of conditionalPanel using a condition based on a dedicated global variable (initial_loading_done), but wherever I tried placing the conditionalPanel it didn't work.

This is the structure of my UI.R:

shinyUI(

  dashboardPage(
    dashboardHeader(title = "Title"),
    dashboardSidebar(
       sidebarMenu(
           menuItem("Tab1", tabName = "Tab1",icon = icon("dashboard")),
           menuItem("Tab2", tabName = "Tab2",  icon = icon("bar-chart-o"))
       )
    ),
    dashboardBody(
       includeCSS("custom_css.css"),
       tabItems(
           tabItem(tabName = "Tab1", 
                   fluidRow(<content>),
                   mainPanel(
                      fluidRow(<content>)
                   )
           ),
           tabItem(tabName = "Tab2",
                  fluidRow(<content>),
                  mainPanel(
                      dataTableOutput('my_data_table')  
                  )
           )
       )
    )
 )
)
DeanAttali
  • 25,268
  • 10
  • 92
  • 118
KeshetE
  • 385
  • 2
  • 5
  • 17
  • 1
    see [1](http://stackoverflow.com/questions/17325521/r-shiny-display-loading-message-while-function-is-running) – Batanichek Feb 25 '16 at 12:49

3 Answers3

27

Here's a very simple example using shinyjs package

The idea is to create the loading "page" and the content "page" under different IDs, have the content page initially hidden, and use show() and hide() after the app is ready

library(shiny)
library(shinyjs)

load_data <- function() {
  Sys.sleep(2)
  hide("loading_page")
  show("main_content")
}

ui <- fluidPage(
  useShinyjs(),
  div(
    id = "loading_page",
    h1("Loading...")
  ),
  hidden(
    div(
      id = "main_content",
      "Data loaded, content goes here"
    )
  )
)

server <- function(input, output, session) {
  load_data()
}

shinyApp(ui = ui, server = server)
DeanAttali
  • 25,268
  • 10
  • 92
  • 118
  • 1
    First of all - thanks! The main_content is indeed hidden and displayed after the data being loaded, which is great. But 9 out of 10 times I launch the app, the "loading_page" isn't displayed at all... What am I doing wrong? – KeshetE Feb 28 '16 at 09:00
  • 1
    Looks like wherever I place the first div (with the loading_page), it isn't calculated until the app data is finished loading - I tried removing the show/hide part and it is only displayed after the data is completely loaded. Is this something to do with the fact I'm using shinydashboard? – KeshetE Mar 01 '16 at 07:37
  • 1
    no, it doesn't matter where you place the elements. The loading_page should show up immediately, and the other div should not show up (because it's inside a `hidden()` function), and it should only show up after the `Sys.sleep(2)` finishes running. If you run this example code that I show, it should work 10/10 times. It will work with a shinydashboard as well – DeanAttali Mar 01 '16 at 09:29
  • 1
    For others Googling slow shinyapps.io initial loading times, this also appears to remedy that. – Frans Rodenburg Mar 03 '19 at 13:21
  • Dean's solution indeed works. Is there a way to make the transition smoother, so it fades into the loaded page rather than just disappearing to reveal the loaded one? – Jamie Jan 27 '22 at 16:23
  • The show/hide functions from shinyjs have an `animate` parameter – DeanAttali Feb 21 '22 at 16:15
9

In server I like to use reactiveValues() to store a setupComplete condition. Then, when the data is loaded my setupComplete is set to TRUE.

In the ui we can then assess this setupComplete condition in a conditionalPanel, and only display the content (in my example the three box() widgets).

Here's a working example

## app.R ##
library(shiny)
library(shinydashboard)
library(shinyjs)

ui <- dashboardPage(
    dashboardHeader(),
    dashboardSidebar(),
    dashboardBody(
        actionButton(inputId = "btn_data", label = "Download"),
        conditionalPanel(condition = "output.setupComplete",
            box( title = "box1" ),
            box( title = "box2" ),
            box( title = "boc3" )
        ),
        conditionalPanel(condition = "!output.setupComplete",
                         box( title = "loading"))
    )
)

server <- function(input, output) { 

    rv <- reactiveValues()
    rv$setupComplete <- FALSE

    ## simulate data load
    observe({

        if(input$btn_data){

            df <- data.frame(id = seq(1,200),
                             val = rnorm(200, 0, 1))

            ## Simulate the data load
            Sys.sleep(5)
            ## set my condition to TRUE
            rv$setupComplete <- TRUE
        }

        ## the conditional panel reads this output
        output$setupComplete <- reactive({
            return(rv$setupComplete)
        })
        outputOptions(output, 'setupComplete', suspendWhenHidden=FALSE)

    })
}

shinyApp(ui, server)
SymbolixAU
  • 25,502
  • 4
  • 67
  • 139
  • Thanks, that really helpful! I want to display a "Loading" page until the loading is done, So created another conditionalPanel with the condition "!output.setupComplete". But it cannot evaluate the setupComplete param. What should I change in the server.r in order for it to work? – KeshetE Feb 28 '16 at 07:53
  • I've updated my answer with a second conditional panel that shows a 'loading' box before the data is loaded. If you want to start doing more complex operations I recommend using @daattali 's excellent `shinyjs` package (as referenced in his answer) – SymbolixAU Feb 28 '16 at 08:34
  • Now your example doesn't work for me - stays with "Loading" and doesn't change to the actual content page. When trying in my app, I see the box until the data is loaded - but don't see its content (just an empty box). Pretty sure it has something to do with the location of the conditionalPanel in my code - it is inside a tabItem. – KeshetE Feb 28 '16 at 09:18
0

The code

hidden(
    div(
      id = "main_content",
      "Data loaded, content goes here"
    )

doesn't work with tabsetPanel. But if you move the id to the div level it works beautifully. Thanks to shinyjs author Dean Attali for this tip. https://stackoverflow.com/users/4432127/keshete

  hidden(
        div(id = "mainTabsetPanel",
          tabsetPanel(
....
Community
  • 1
  • 1
JerryN
  • 2,356
  • 1
  • 15
  • 49