0

I have come across an interesting example of the following R-Shiny code:

missing_files_exist <- check_for_missing_files()
if (missing_files_exist) {
  shinyWidgets::show_alert(
                    title = "Missing Files Detected",
                    text = "It appears file x is missing.",
                    type = "warning"
                )
}
if (nrow(data) == 0) {
                shinyWidgets::sendSweetAlert(
                    title = "No data",
                    text = "Please load data first.",
                    type = "error"
                )
                return(NULL)
}

In this case the check_for_missing_files function is very elaborate, taking around 2 seconds to complete. This leads to the "No data" alert being shown first and, thus, suppressing the "Missing Files" alert. This seems odd to me, since I expected R to move on to the next line only once the first one has finished. I am sure that is what happening in the background but then I struggle to find an explanation for this behavior.

Adding a sleep statement fixes the issue, in case that is of relevance:

missing_files_exist <- check_for_missing_files()
if (missing_files_exist) {
  shinyWidgets::show_alert(
                    title = "Missing Files Detected",
                    text = "It appears file x is missing.",
                    type = "warning"
                )
}
Sys.sleep(2)
if (nrow(data) == 0) {
                shinyWidgets::sendSweetAlert(
                    title = "No data",
                    text = "Please load data first.",
                    type = "error"
                )
                return(NULL)
}

Question: Why does this behavior happen and what can I do to prevent it?

Pycruncher
  • 25
  • 2
  • 3
    So this is part of a shiny app? Is this code wrapped inside an observer or reactive element? The rules about when things run in shiny are very different than R in general. It's easier to help you if you include a simple [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) with sample input that can be used to test and verify possible solutions. – MrFlick Apr 05 '23 at 14:22
  • @MrFlick Thank you for your comment, I have posted an answer that uses three simple examples to further investigate the mechanics at play. – Pycruncher Apr 06 '23 at 08:17
  • Normally this won’t happen but it depends entirely on the implementation of `check_for_missing_files`. You’ll need to show us that. – Konrad Rudolph Apr 10 '23 at 13:08

2 Answers2

0
ui <- shiny::fluidPage(
    shiny::mainPanel(
        shiny::actionButton(
            inputId = "button",
            label = "Validate"
        )
    )
)
server <- function(input, output) {
    shiny::observeEvent(
        eventExpr = input$button,
        handlerExpr = {
            shinyWidgets::sendSweetAlert(
                title = "Alert 1",
                type = "error"
            )

            shinyWidgets::sendSweetAlert(
                    title = "Alert 2",
                    type = "error"
            )
        }
    )
}
shiny::shinyApp(ui = ui, server = server)

Only Alert 2 is shown.

ui <- shiny::fluidPage(
    shiny::mainPanel(
        shiny::actionButton(
            inputId = "button",
            label = "Validate"
        )
    )
)
server <- function(input, output) {
    shiny::observeEvent(
        eventExpr = input$button,
        handlerExpr = {
            shinyWidgets::sendSweetAlert(
                title = "Alert 1",
                type = "error"
            )
            Sys.sleep(1)
            shinyWidgets::sendSweetAlert(
                title = "Alert 2",
                type = "error"
            )
        }
    )
}
shiny::shinyApp(ui = ui, server = server)

First Alert 1 is shown, after 1 second it gets replaced by Alert 2.

ui <- shiny::fluidPage(
    shiny::mainPanel(
        shiny::actionButton(
            inputId = "button",
            label = "Validate"
        )
    )
)
server <- function(input, output) {
    shiny::observeEvent(
        eventExpr = input$button,
        handlerExpr = {
            if (TRUE) {
                shinyWidgets::sendSweetAlert(
                    title = "Alert 1",
                    type = "error"
                )
                return(NULL)
            }
            shinyWidgets::sendSweetAlert(
                title = "Alert 2",
                type = "error"
            )
        }
    )
}
shiny::shinyApp(ui = ui, server = server)

Only Alert 1 is shown.

 

So, this seems to suggest, that the issue was faced is simply because alerts draw over one another. To get around this behaviour, one should avoid multiple alerts or make the latter alerts conditional on the previous alerts being clicked away, for example by using shinyWidgets::confirmSweetAlert.

Pycruncher
  • 25
  • 2
0

This works:

library(shiny)
library(shinyWidgets)

ui <- fluidPage(
  mainPanel(
    actionButton(
      inputId = "button1",
      label = "ALERT 1"
    ),
    actionButton(
      inputId = "button2",
      label = "ALERT 2"
    )
  )
)
server <- function(input, output, session) {
  
  flag <- reactiveVal(NULL)

  observeEvent(
    input$button1,
    {
      confirmSweetAlert(
        inputId = "alert1",
        title = "Alert 1",
        type = "error"
      )
    }, priority = 3
  )

  observeEvent(
    input$alert1,
    {
      flag(TRUE)
    }, 
    priority = 2
  )
  
  observeEvent(
    input$button2,
    {
      flag(TRUE)
    }
  )
  
  observeEvent(
    flag(),
    {
      flag(NULL)
      sendSweetAlert(
        title = "Alert 2",
        type = "error"
      )
    }, priority = 1
  )
  
}

shinyApp(ui = ui, server = server)
Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225