I am trying to use futures to have a "loading" icon appear. This is the code I have
library(shiny)
library(promises)
library(future)
plan(multiprocess)
disksUI <- function(id) {
ns <- NS(id)
fluidRow(
box(
uiOutput(ns("loading")),
dataTableOutput(ns("filelist")),
width=12
)
)
}
disksServer <- function(input, output, session) {
state <- reactiveValues(onLoading=FALSE)
observe({
if (state$onLoading) {
output$loading <- renderUI("Loading")
} else {
output$loading <- renderUI("Done")
}
})
filelist <- reactive(
{
state$onLoading <- TRUE
future({
Sys.sleep(3)
state$onLoading <- FALSE
}
)
}
)
output$filelist <- renderDataTable({
filelist()
})
}
However, the result is not what I expect. What I expect is
the string Loading appears immediately
after three seconds, the string Loading is replaced with Done
What happens is
Nothing is written for three seconds.
After three seconds, the Loading string appears.
I posted my answer here first. However, adding it also here for future readers:
Here is a working example:
library(shiny)
library(shinydashboard)
library(promises)
library(future)
library(shinyjs)
plan(multiprocess)
server <- function(input, output, session) {
output$loading <- renderUI("Idling")
myFilelist <- reactiveVal(NULL)
observeEvent(input$getBtn, {
disable("getBtn")
output$loading <- renderUI("Loading")
myFuture <- future({
Sys.sleep(3)
data.frame(list.files(getwd()))
})
then(myFuture, onFulfilled = function(value) {
enable("getBtn")
output$loading <- renderUI("Done")
myFilelist(value)
},
onRejected = NULL)
return(NULL)
})
output$filelist <- renderDataTable({
myFilelist()
})
}
ui <- fluidPage(
useShinyjs(),
fluidRow(
actionButton("getBtn", "Get file list"),
box(
uiOutput("loading"),
dataTableOutput("filelist"),
width=12
)
)
)
shinyApp(ui, server)
Please note the return(NULL) in the observeEvent() - this is hiding the future from its own session - allowing intra-session responsiveness. However, now we have to deal with potential race conditions, as Joe Cheng already mentioned to you here. In this simple example we can disable the trigger button to avoid users having the possibility of creating new futures while others are still beeing processed.
For further details please read this.
Related
I have an app where a large dataset is read in before the app starts. The app has separate ui and server files. So the UI is visible straightaway and the div for output plot remains empty. It sort of hangs for about 2-3 seconds as the data is read in. And then the plot is displayed. The rest of the app is fast enough and requires no progress bars. I would like to show some progress/indication that the data is being read in rather than just "freezing" for few seconds.
Here is a dummy example. The data is only read in once before the app loads. The data is used in ui as well as server.
library(shiny)
# read big file
#saveRDS(diamonds,"diamonds.Rds")
x <- readRDS("diamonds.Rds")
ui = fluidPage(
titlePanel("Progress bar test"),
selectInput("in_opts","Select",choices=colnames(x),selected=1),
verbatimTextOutput("out_txt")
)
server=function(input,output,session) {
output$out_txt <- renderPrint({
Sys.sleep(3)
head(x)
})
}
shinyApp(ui,server)
I have tried using shinycssloaders. It generally works. It works well in this dummy example. But, it doesn't work for the "reading in file" part since that is outside the withSpinner() function.
library(shiny)
library(shinycssloaders)
# read big file
#saveRDS(diamonds,"diamonds.Rds")
x <- readRDS("diamonds.Rds")
ui = fluidPage(
titlePanel("Progress bar test"),
selectInput("in_opts","Select",choices=colnames(x),selected=1),
shinycssloaders::withSpinner(verbatimTextOutput("out_txt"))
)
server=function(input,output,session) {
output$out_txt <- renderPrint({
Sys.sleep(3)
head(x)
})
}
shinyApp(ui,server)
Is there a way to display progress/indicator for the readRDS() step?
As mentioned in the comments we can run the long computation in a separate process. This can e.g. be done via library(future).
Once the future_promise returns its result, it is assigned to a global reactiveVal - therefore all shiny sessions started later don't have to wait.
library(shiny)
library(promises)
library(future)
library(datasets)
library(shinycssloaders)
plan(multisession)
globalrv <- reactiveVal(NULL)
future_promise({
Sys.sleep(10) # your long running function
iris
}) %...>%
globalrv() %...!% # assign result to globalrv
(function(e) {
globalrv(NULL) # error handling needed?
warning(e)
})
ui = fluidPage(
titlePanel("Progress bar test"),
conditionalPanel("output.trigger == null", shinycssloaders::withSpinner(uiOutput("dummy"))),
conditionalPanel("output.trigger != null", verbatimTextOutput("out_txt"))
)
server = function(input, output, session) {
output$trigger <- eventReactive(globalrv(), {globalrv()})
outputOptions(output, "trigger", suspendWhenHidden = FALSE)
output$out_txt <- renderPrint({
req(globalrv())
head(globalrv())
})
}
shinyApp(ui,server)
Using renderUI instead:
library(shiny)
library(promises)
library(future)
library(datasets)
library(shinycssloaders)
plan(multisession)
globalrv <- reactiveVal(NULL)
future_promise({
Sys.sleep(10) # your long running function
iris
}) %...>%
globalrv() %...!% # assign result to globalrv
(function(e) {
globalrv(NULL) # error handling needed?
warning(e)
})
ui = fluidPage(
titlePanel("Progress bar test"),
uiOutput("spinner"),
verbatimTextOutput("out_txt")
)
server = function(input, output, session) {
output$spinner <- renderUI({
if(is.null(globalrv())){
shinycssloaders::withSpinner(uiOutput("dummy"))
} else {
NULL
}
})
output$out_txt <- renderPrint({
req(globalrv())
head(globalrv())
})
}
shinyApp(ui,server)
Is there a way to avoid the flickering between rendering of the plots using shiny recalculate ?
The plots become grey-on-invalidation mechanism (flashing) the moment they are invalidated (which in our case means when values$a has changed), so their current display is not up to date, but they also have not finished recalculating for the new value of values$a. We can speed up recalculation (by parallelizing code), but never completely get rid of it. So removing this mechanism actually removes information from the end user, as you now don't have any way to know if the plot is still up to date or if it's recalculating for the next value... I understand this may be annoying, but I just want to make sure you understand why it's happening.
Appreciate your time for reading this and will be glad if you could suggest some solution.
library("shiny")
library("parallel")
library("pryr")
ui <- basicPage(
plotOutput('plot1')
,plotOutput('plot2')
,plotOutput('plot3')
,plotOutput('plot4')
,plotOutput('plot5')
,plotOutput('plot6')
,plotOutput('plot7')
,plotOutput('plot8')
,plotOutput('plot9')
,plotOutput('plot10')
,plotOutput('plot11')
,plotOutput('plot12')
,plotOutput('plot13')
,plotOutput('plot14')
,plotOutput('plot15')
,plotOutput('plot16')
,plotOutput('plot17')
,plotOutput('plot18')
,plotOutput('plot19')
,plotOutput('plot20')
,plotOutput('plot21')
,plotOutput('plot22')
,plotOutput('plot23')
,plotOutput('plot24')
,plotOutput('plot25')
,plotOutput('plot26')
,plotOutput('plot27')
,plotOutput('plot28')
,plotOutput('plot29')
,plotOutput('plot30')
)
server <- function(input, output) {
values <- reactiveValues(a=1)
observe({
invalidateLater(5000)
doPlot<-rnorm(1)
values$a <- doPlot
print(mem_used())
})
observeEvent(values$a,{
mclapply(1:30,function(i){
output[[paste0("plot",i)]] <- renderPlot({plot(rnorm(50),main=i)})
})
})
}
shinyApp(ui,server)
##################################
library("shiny")
library("parallel")
library("pryr")
ui <- basicPage(
plotOutput('plot1')
,plotOutput('plot2')
,plotOutput('plot3')
,plotOutput('plot4')
,plotOutput('plot5')
,plotOutput('plot6')
,plotOutput('plot7')
,plotOutput('plot8')
,plotOutput('plot9')
,plotOutput('plot10')
,plotOutput('plot11')
,plotOutput('plot12')
,plotOutput('plot13')
,plotOutput('plot14')
,plotOutput('plot15')
,plotOutput('plot16')
,plotOutput('plot17')
,plotOutput('plot18')
,plotOutput('plot19')
,plotOutput('plot20')
,plotOutput('plot21')
,plotOutput('plot22')
,plotOutput('plot23')
,plotOutput('plot24')
,plotOutput('plot25')
,plotOutput('plot26')
,plotOutput('plot27')
,plotOutput('plot28')
,plotOutput('plot29')
,plotOutput('plot30')
)
server <- function(input, output) {
values <- reactiveValues(a=1)
observe({
invalidateLater(5000)
doPlot<-rnorm(1)
values$a <- doPlot
print(mem_used())
})
mclapply(1:30,function(i){
output[[paste0("plot",i)]] <<- renderPlot({values$a
plot(rnorm(50),main=i)
})
})
}
shinyApp(ui,server)
You can change the recalculating opacity via css, e.g. add the following to your UI code:
tags$style(type="text/css",
".recalculating {opacity: 1.0;}"
)
Using your example:
library("shiny")
library("parallel")
library("pryr")
ui <- basicPage(
tags$style(type="text/css",
".recalculating {opacity: 1.0;}"
),
plotOutput('plot1')
,plotOutput('plot2')
,plotOutput('plot3')
,plotOutput('plot4')
,plotOutput('plot5')
,plotOutput('plot6')
,plotOutput('plot7')
,plotOutput('plot8')
,plotOutput('plot9')
,plotOutput('plot10')
,plotOutput('plot11')
,plotOutput('plot12')
,plotOutput('plot13')
,plotOutput('plot14')
,plotOutput('plot15')
,plotOutput('plot16')
,plotOutput('plot17')
,plotOutput('plot18')
,plotOutput('plot19')
,plotOutput('plot20')
,plotOutput('plot21')
,plotOutput('plot22')
,plotOutput('plot23')
,plotOutput('plot24')
,plotOutput('plot25')
,plotOutput('plot26')
,plotOutput('plot27')
,plotOutput('plot28')
,plotOutput('plot29')
,plotOutput('plot30')
)
server <- function(input, output) {
values <- reactiveValues(a=1)
observe({
invalidateLater(5000)
doPlot<-rnorm(1)
values$a <- doPlot
print(mem_used())
})
observeEvent(values$a,{
mclapply(1:30,function(i){
output[[paste0("plot",i)]] <- renderPlot({plot(rnorm(50),main=i)})
})
})
}
shinyApp(ui,server)
How to display a blank UI (alternatively destroy module UI), if the module server-function fails, without moving all the UI-code to the server function?
Simple reproducible example:
library(shiny)
my_module_ui <- function(id) {
ns <- NS(id)
tags$div(
tags$h1("Don't show me if my_module_server fails!"),
plotOutput(ns("my_plot"))
)
}
my_module_server <- function(input, output, session) {
tryCatch({
my_data <- cars * "A" # fail for demo
# my_data <- cars
output$my_plot <- renderPlot({
cars2 <- my_data + rnorm(nrow(my_data))
plot(cars2)
})
}, error=function(cond) {
message("Destroy UI here!")
})
}
ui <- fluidPage(
my_module_ui("my_id")
)
server <- function(input, output, session) {
callModule(my_module_server, "my_id")
}
shinyApp(ui, server)
My current solution is to have nothing but a uiOutput() in my_module_ui and render the entire ui in the server function. I want to prevent this, since large modules get very messy if all UI-components are placed within the module server-function.
In addition I would preferably also like to avoid returning values from callModule() that destroy the UI and do this from within the server-function instead.
Thanks!
How about you assign a value to the session object and evaluate this value before you create the UI (from server side via renderUI().
1) Move rendering of UI to server side
Use renderUI(my_module_ui("my_id")) on server side and uiOutput("module") on ui side.
2) To detect whether your server module was successful assign a value to the session object
my_module_server <- function(input, output, session) {
tryCatch({
...
session$userData$mod_server <- TRUE
}, error = function(cond) {
session$userData$mod_server <- NULL
})
}
3) Use this value to make the call of your module ui conditional
output$module <- renderUI({
callModule(my_module_server, "my_id")
if(!is.null(session$userData$mod_server)) my_module_ui("my_id")
})
Reproducible example:
library(shiny)
my_module_ui <- function(id) {
ns <- NS(id)
tags$div(
tags$h1("Don't show me if my_module_server fails!"),
plotOutput(ns("my_plot"))
)
}
my_module_server <- function(input, output, session) {
tryCatch({
my_data <- cars * "A" # fail for demo
# my_data <- cars
output$my_plot <- renderPlot({
cars2 <- my_data + rnorm(nrow(my_data))
plot(cars2)
})
session$userData$mod_server <- TRUE
}, error = function(cond) {
session$userData$mod_server <- NULL
})
}
ui <- fluidPage(
uiOutput("module")
)
server <- function(input, output, session) {
output$module <- renderUI({
callModule(my_module_server, "my_id")
if(!is.null(session$userData$mod_server)) my_module_ui("my_id")
})
}
shinyApp(ui, server)
With a little code reordering, and the use of the amazing shinyjs package this can be done.
Note that I added an input to simulate errors and not errors, to see how the UI dissapears. Also all is done in the server part of the module. I hope this will help you. The code has inline comments explaining the steps.
library(shiny)
library(shinyjs)
my_module_ui <- function(id) {
ns <- NS(id)
tagList(
# input added to be able to throw errors and see the ui dissapear
selectInput(
ns('trigger'), 'Error trigger',
choices = list('no error' = c(2,1), 'error' = c('A', 'B')),
selected = 2
),
tags$div(
# div with id, to select it with shinyjs and hide it if necessary
id = ns('hideable_div'),
tags$h1("Don't show me if my_module_server fails!"),
plotOutput(ns("my_plot"))
)
)
}
my_module_server <- function(input, output, session) {
# get all the things prone to error in a reactive call, that way you capture the final
# result or a NULL reactive when an error occurs
foo <- reactive({
tryCatch({
if (input$trigger %in% c(2,1)) {
trigger <- as.numeric(input$trigger)
} else {
trigger <- input$trigger
}
cars * trigger
}, error=function(cond) {
message("Destroy UI here!")
})
})
# obseveEvent based on the error reactive, to check if hide or not the UI
observeEvent(foo(), {
# hide checking if foo is null, using shinyjs
if (is.null(foo())) {
shinyjs::hide('hideable_div')
} else {
shinyjs::show('hideable_div')
}
}, ignoreNULL = FALSE, ignoreInit = FALSE)
# outputs, with validation of the error reactive. That way code after validate is not
# executed but the app does not get blocked (gray)
output$my_plot <- renderPlot({
shiny::validate(
shiny::need(foo(), 'no data')
)
cars2 <- foo() + rnorm(nrow(foo()))
plot(cars2)
})
}
ui <- fluidPage(
# really important for shinyjs tu work!!!!!!!
shinyjs::useShinyjs(),
my_module_ui("my_id")
)
server <- function(input, output, session) {
callModule(my_module_server, "my_id")
}
shinyApp(ui, server)
I am having a shiny app that is heavily relying on modules. Basically it's a big navbarPage with different menus and tabs where every tab is wrapped as a module. Below I provided a minimal example with 2 tabs, but in reality there are more than 20.
# module 1 ------------------------------------------------
moduleOneUI <- function(id) {
ns = NS(id)
tagList(
h2("module1"),
textOutput(ns("text"))
)
}
moduleOne <- function(input, output, session) {
output$text <- renderText({"one yo"})
}
# module 2 ------------------------------------------------
moduleTwoUI <- function(id) {
ns = NS(id)
tagList(
h2("module2"),
textOutput(ns("text"))
)
}
moduleTwo <- function(input, output, session) {
output$text <- renderText({"two yo"})
}
# main app ------------------------------------------------
ui <- navbarPage(
"dashboard",
navbarMenu(
"#1",
tabPanel(
"mod1",
uiOutput("module_one")
)
),
navbarMenu(
"#2",
tabPanel(
"mod2",
uiOutput("module_two")
)
)
)
server <- function(input, output, session) {
output$module_one <- renderUI({
moduleOneUI("module_one")
})
callModule(moduleOne, "module_one")
output$module_two <- renderUI({
moduleTwoUI("module_two")
})
callModule(moduleTwo, "module_two")
}
shinyApp(ui, server)
As you might see the server function gets very large with this hard coded server function. So I tried to create it in a more dynamic way using a loop:
modules <- list(
module_one = c(ui = moduleOneUI, server = moduleOne),
module_two = c(ui = moduleTwoUI, server = moduleTwo)
)
server <- function(input, output, session) {
for (mod_id in names(modules)) {
module <- modules[[mod_id]]
ui_func <- module$ui
server_func <- module$server
output[[mod_name]] <- renderUI({
ui_func(mod_id)
})
callModule(server_func, mod_id)
}
}
However, this approach fails to work as expected. Now I see the moduleTwo code rendered in my module_one tab:
Does any1 know why this is happening and how I can fix it? I really need a dynamic approach to render all those modules.
The two renderUI expressions aren't evaluated until after the loop completes and mod_id = "module_two".
To get around this, you can create a local scope for each loop iteration:
for (mod_id in names(modules)) {
local({
mod_id <- mod_id
module <- modules[[mod_id]]
ui_func <- module$ui
server_func <- module$server
output[[mod_id]] <- renderUI({
ui_func(mod_id)
})
callModule(server_func, mod_id)
})
}
I seem to have found a solution for that problem. If I define a function that calls the module everything works as expected.
create_tab <- function(mod_id, output) {
module <- modules[[mod_id]]
ui_func <- module$ui
server_func <- module$server
output[[mod_id]] <- renderUI({
ui_func(mod_id)
})
callModule(server_func, mod_id)
}
server <- function(input, output, session) {
lapply(names(modules), create_tab, output = output)
}
However, I have no idea why this is working and the other approach isn't. I assume it has something to do with scoping in R.
I am using renderUI to optionally present a Table or Plot based on user selection of the visualization option. I am also using Shiny modules to present the same thing on multiple tabs. While I have gotten Shiny modules to work wonderfully in another app, I am struggling to get it to work with renderUI.
Here is a minimal piece of code that I came up with that shows the problem where nothing gets displayed on either tabs:
myUI <- function(id) {
ns <- NS(id)
fluidRow(
uiOutput(ns('myFinalText'))
)
}
ui <- fluidPage(
tabBox(id = 'myBox', width = 12,
tabPanel('Tab1',
fluidRow(
myUI('tab1')
)),
tabPanel('Tab2',
fluidRow(
myUI('tab2')
))
)
)
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput('myText')
})
}
server <- function(input, output, session) {
callModule(myTextFunc, 'tab1', session = session, 'Hello Tab1')
callModule(myTextFunc, 'tab2', session = session, 'Hello Tab2')
}
shinyApp(ui = ui, server = server)
Any thoughts on what else I should be doing to make this work?
Replacing the Shiny module UI function and server functions as follows makes it work fine.
myUI <- function(id) {
ns <- NS(id)
fluidRow(
textOutput(ns('myFinalText'))
)
}
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderText({
text
})
}
You can get the namespace from the session object. Change myTextFunc in the initial app like this:
myTextFunc <- function(input, output, session, text) {
ns <- session$ns
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput(ns('myText'))
})
}
You shouldn't call output$ function from another output$ function - it's against Shiny design patterns.
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput(ns('myText'))
})
If you want to know, why it is very bad practice, watch Joe Cheng tutorial about 'Effective reactive programming' from this site: https://www.rstudio.com/resources/webinars/shiny-developer-conference/.
You should use rather reactiveValues or reactive expressions instead. What exactly you should use is dependent from what do you want to achieve, so it's hard to say without detailed example, but according to Joe Cheng everything can be accomplished without nesting outputs or observers.
Sorry for answering my own question...but for others looking for a similar solution, this may be of help.
Here is how I solved for the need to inherit Shiny module namespace on the server side to dynamically render UI. IF there is a better way to solve, please comment or post.
tab1NS <- NS('tab1')
tab2NS <- NS('tab2')
myUI <- function(ns) {
tagList(
fluidRow(
radioButtons(ns('type'), 'Select Visual:',
choices = c('Table' = 'table',
'Plot' = 'plot'))
),
fluidRow(
uiOutput(ns('myCars'))
)
)
}
ui <- fluidPage(
tabBox(id = 'myBox', width = 12,
tabPanel('Tab1',
fluidRow(
myUI(tab1NS)
)),
tabPanel('Tab2',
fluidRow(
myUI(tab2NS)
))
)
)
myTextFunc <- function(input, output, session, cars, ns) {
getMyCars <- reactive({
if (input$type == 'table') {
output$table <- renderDataTable({datatable(cars)})
dataTableOutput(ns('table'))
} else{
output$plot <- renderPlot({
plot(cars$wt, cars$mpg)
})
plotOutput(ns('plot'))
}
})
output$myCars <- renderUI({
getMyCars()
})
}
server <- function(input, output, session) {
callModule(myTextFunc, 'tab1', session = session,
mtcars[mtcars$am == 1, ], tab1NS)
callModule(myTextFunc, 'tab2', session = session,
mtcars[mtcars$am == 0, ], tab2NS)
}
shinyApp(ui = ui, server = server)
Replacing your functions with this renderUI equivalent also works:
myUI <- function(id) {
ns <- NS(id)
fluidRow(
uiOutput(ns('myFinalText'))
)
}
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderUI({
text
})
}
Although this obviously does not capture the complexity of what you are really doing. There's something not right about using output$... and textOutput within the renderUI like that. I don't think that is necessary - you don't actually have to use the textOutput function to include text in your output.
EDIT: It occurs to me that the problem has to do with namespaces and modules. When you do output$myText <- renderText(text), the result ends up in the namespace of tab1 or tab2. For example, try changing your textOutput to
textOutput('tab1-myText')
and watch what happens. I think this is why having output$.. variables in your renderUI is problematic. You can access inputs via callModule and that should take care of any namespace issues.