Search code examples
javascriptrshinyflexdashboard

Detecting the full load the flex dashboard page


I'm using a rather complex flexdashboard structure with 10 pages and several graphics. The loading time is about 30" and I would like to show on each page the text "Loading data..." until the charts are effectively available.

To reach this, I've inserted in each page a Loading data... and I'm using a Javascript to detect when the page is loaded to set the visibility to hidden for all the .

When the charts are loaded, the text "Loading data..." does not hide.

Here is a reproducible example:

---
title: 'Reprex'
output:
  flexdashboard::flex_dashboard:
    orientation: column
    vertical_layout: fill
runtime: shiny

---

```{r setup, include=FALSE}
tags$head(tags$script(src = "script.js"))

```

Reprex{data-icon="fa-chart-bar"}
===================================== 

<div id="loading">Loading data...</div>


```{r}
renderPlot({
  
  Sys.sleep(5) #it's just to mimic the elaboration time
  plot(cars)
  
  })

```

Here is the content of the javascript file (that sits in the same directory of the RMD file:

$(document).on('shiny:busy', function(event) {
  var cont = document.getElementById("loading");
  cont.style.visibility = "visible";
});

$(document).on('shiny:idle', function(event) {
  var cont = document.getElementById("loading");
  cont.style.visibility = "hidden";
});

Thank you in advance for your kind support.


Solution

  • Do this:

    ---
    title: 'Reprex'
    output:
      flexdashboard::flex_dashboard:
        orientation: column
        vertical_layout: fill
    runtime: shiny
    
    ---
    
    ```{js}
    Shiny.addCustomMessageHandler("loading-done", function(data){
        document.querySelector(`[data-loading="${data.loading_id}"]`).style.display = "none"
    })
    ```
    
    Reprex{data-icon="fa-chart-bar"}
    ===================================== 
    
    <div data-loading="loading-1">Loading data...</div>
    
    
    ```{r}
    renderPlot({
        Sys.sleep(5) #it's just to mimic the elaboration time
        on.exit(session$sendCustomMessage("loading-done", list(loading_id = "loading-1")))
        plot(cars)
    })
    
    ```
    
    1. Change id to custom data-loading property, because flexdashboard adds some prefix to your IDs. Don't want to waste time predicting the pattern.
    2. Make a <div data-loading="loading-1"> ... </div> on each of your page, just remember to change the loading ID. data-loading="loading-X" is paired with list(loading_id = "loading-x") on server.
    3. When the plot finishes, sends a signal to JS to hide the message.

    Alternation

    I would suggest you use other loader libraries so you don't need to write your own JS.

    ---
    title: 'Reprex'
    output:
      flexdashboard::flex_dashboard:
        orientation: column
        vertical_layout: fill
    runtime: shiny
    
    ---
    
    ```{r include=FALSE}
    library(spsComps)
    ```
    
    Reprex{data-icon="fa-chart-bar"}
    ===================================== 
    
    ```{r}
    plotOutput("plot1")
    ```
    
    ```{r}
    loader1 <- addLoader$new(
        target_selector = "plot1",
        footer = h4("Loading data...")
    )
    
    output$plot1 <- renderPlot({
        loader1$show()
        Sys.sleep(5) #it's just to mimic the elaboration time
        on.exit(loader1$hide())
        plot(cars)
    })
    ```
    

    enter image description here

    For this, each page needs its own loader.

    If you want multiple pages, we can use the full_screen loader so all pages can share the same loader.

    ---
    title: 'Reprex'
    output:
      flexdashboard::flex_dashboard:
        orientation: column
        vertical_layout: fill
    runtime: shiny
    
    ---
    
    ```{r include=FALSE}
    library(spsComps)
    ```
    
    Reprex{data-icon="fa-chart-bar"}
    ===================================== 
    
    ```{r}
    plotOutput("plot1")
    ```
    
    ```{r}
    loader1 <- addLoader$new(
        target_selector = "body", isID = FALSE, method = "full_screen",
        footer = h4("Loading data..."), height = "300px"
    )
    
    output$plot1 <- renderPlot({
        loader1$show()
        Sys.sleep(2) #it's just to mimic the elaboration time
        on.exit(loader1$hide())
        plot(cars)
    })
    ```
    
    
    Reprex{data-icon="fa-home"}
    =====================================
    
    ```{r}
    renderUI({
        loader1$show()
        Sys.sleep(2)
        on.exit(loader1$hide())
        div("another page")
    })
    
    ```
    

    enter image description here