shinycssloaders
is not working after Shiny rendering event is done. In this case datatable.js is asynchronous. After the table skeleton is rendered, it is fetching the data and waits for the data to be load then updates the table. Shiny rendering is done on the first part, so shinycssloaders
cannot cover the second part.
To do this, we need to use advanced custom loaders, in this case, my example uses addLoader
from spsComps, which adds loaders from the server-side and you can decide when to add it and when to hide it.
The way I know when to apply the loader is based on datatable events. I added some js to tell Shiny server these events happened, e.g. table starts to load, page change triggers, etc. You can add more if you want. Then, from server, I show the loader. When the table data is loaded, I tell Shiny server again, then I hide the loader.
library(shiny)
library(DT)
library(spsComps)
shinyApp(
ui = fluidPage(
fluidRow(column(12,DT::DTOutput('tbl'))),
tags$script(
"
function loaderStart() {
Shiny.setInputValue('loader_state', 1)
}
function loaderEnd() {
Shiny.setInputValue('loader_state', 0)
}
$('#tbl').on('draw.dt', function () {
loaderEnd();
});
$('#tbl').on('page.dt', function () {
loaderStart();
});
$('#tbl').on('preInit.dt', function () {
loaderStart();
});
$('#tbl').on('order.dt', function () {
loaderStart();
});
"
)
),
server = function(input, output) {
tbl_loader <- addLoader$new("tbl")
output$tbl = renderDT(
data.frame(a = 1:10000000, b = 1:10000000)
)
observeEvent(input$loader_state, {
req(!is.null(input$loader_state))
if(input$loader_state == 1) {
tbl_loader$recreate(
type = "facebook",
footer = tags$span("Loading data ...", style = "display: block")
)$show()
}
else tbl_loader$hide()
})
}
)
Read more about addLoader
, and try some demos