I want to wrap Shiny module in kind of "try catch" to prevent it from crashing in any unexpected way. Module represents one of shinydashboard
tabs, so the app is still usable after one of components fails.
I'm aware of possibility of using req
function not to stop the application after single error (proposed e.g. in this post) but the goal here is to achieve quick win by reducing user cost of errors for quite complex app with a substantial technical debt.
Expected behaviour: after module crash modal is shown but user can still use the app.
Reproducible example:
library(shiny)
counterButton <- function(id, label = "Counter") {
ns <- NS(id)
tagList(
actionButton(ns("button"), label = label),
verbatimTextOutput(ns("out"))
)
}
counter <- function(input, output, session) {
count <- reactiveVal(0)
observeEvent(input$button, {
count(count() + 1)
stop("Test observeEvent error")
})
output$out <- renderText({
count()
})
count
}
ui <- fluidPage(
counterButton("counter1", "Counter #1")
)
server <- function(input, output, session) {
tryCatch({
callModule(counter, "counter1")
}, error = function(err) {
showNotification(paste("Error: ", err$message), type = "error")}
)}
shinyApp(ui, server)
I've found a partial solution (see here) and it indeed works but it involves changes in application modules code, which is not always possible for me to do:
tryObserveEvent <- function(eventExpr, handlerExpr, ...) {
eventExpr <- substitute(eventExpr)
handlerExpr <- substitute(handlerExpr)
env <- parent.frame()
shiny::observeEvent(tryCatch(
eval(eventExpr, env),
error = function(e) {
showNotification(paste("Error: ", e$message), type = "error")
}
),
{
tryCatch(
eval(handlerExpr, env),
error = function(e) {
showNotification(paste("Error: ", e$message), type = "error")
}
)
}, ...)
}