1

I am using a for loop to create multiple popping up messages in my shiny app (using shinyalert package). I would like messages to stop popping if user had clicked Cancel as an answer to a previous message. Below a sample of code illustrating my example

library(shiny)
library(shinyalert)

ui <- fluidPage(
    actionButton("run", "Run")
)

server <- function(input, output, session) {

observeEvent(input$run, {
    
    vector.elements <- c("A", "B", "C", "D", "E")
    decision <<- TRUE
    
    for (element in vector) {
    
    shinyalert(
        title = "Do you accept following element?",
        text = element,
        showCancelButton =  TRUE,
        callbackR = mycallback)
        
        mycallback <- function(value) {
            decision <<- value
        }
        
        if (decision == FALSE) {
            break
        }
    }
})
}
shinyApp(ui = ui, server = server)

If user clicks on Cancel button as an answer to Do you accept following element? A, I would like next messages not to pop up.

Any hint would be much appreciated!

Mark Perez
  • 177
  • 7

1 Answers1

1

Shiny alert does not seem to be run synchronous to the loop. Put print(decision) in front of the for-loop. It'll show in the console that the loop runs independently from the user clicking on the alert messages. That means: it won't work with a for loop or any other loop. It can only be done using the event mechanisms provided by Shiny.

The solution below creates and manipulates a reactive value RequiredAnswers. Any change to it will trigger the shiny alert to open and ask the user to confirm the first element of the RequiredAnswers vector. In other words, it removes the element that has just been answered with "No".

Each answer to the alert will be caught by observeEvent(input$AnswerAlert, {}). If the response was "cancel" it dismisses the first element of RequiredAnswers thus triggering the next alert. This way we get a loop. If the response was "Ok" it will clear RequiredAnswers and no more alerts will be triggered (because observeEvent(RequiredAnswers(), {}) does not respond to RequiredAnswers == NULL.

Drawback: if the user clicks 'Cancel' quite fast, Shiny does not recognize the event observeEvent(input$AnswerAlert, {}) does not get called. I cannot say for sure what the source of this is. My guess is a bug in Shiny Alert.

Another way would be to do it recursively (see the section "Chaining modals" in the documentation). This way, the lost events may be avoided.

library(shiny)
library(shinyalert)

ui <- fluidPage(
  actionButton("run", "Run"),
  verbatimTextOutput("answer", placeholder = TRUE)
)

server <- function(input, output, session) {

  RecentDecision <- reactiveVal()
  RequiredAnswers <- reactiveVal()
  
  # Responds to the alert being confirmed or dismissed
  observeEvent(input$AnswerAlert, {
    if (input$AnswerAlert) {
      Answer <- RequiredAnswers()[1]
      RecentDecision(Answer)
      print(RecentDecision())
      RequiredAnswers(NULL)
    } else {
      # Remove the first item of RequiredAnswers
      # Clear it completely when the end has been reached
      if (length(RequiredAnswers()) == 1) {
        RequiredAnswers(NULL)
        RecentDecision(NULL)
      }
      else
        RequiredAnswers(RequiredAnswers()[-1])
    }
  })
  
  # Responds to changes, ignores NULL
  observeEvent(RequiredAnswers(), {
    shinyalert(
      title = "Do you accept following element?",
      text = RequiredAnswers()[1],
      showCancelButton =  TRUE,
      inputId = "AnswerAlert"  # use individual id
    )
  })
  
  # Respond to the Run button
  observeEvent(input$run, {
    # Set up the vector of desired answers
    RequiredAnswers(c("A", "B", "C", "D", "E"))
  })
  
  output$answer <- renderText({
    if (!is.null(RecentDecision()))
      RecentDecision()
    else
      "No answer, yet"
  })
}

shinyApp(ui = ui, server = server)

Jan
  • 4,974
  • 3
  • 26
  • 43
  • Thank you so much for the answer! Below I described a problem I am facing now. I would be very grateful if you take a look at it. – Mark Perez Apr 19 '22 at 11:34
  • Glad to be of help. Which "below" are you referring to? If you direct me to your question, maybe I can help. ...by the way, if it's not too much to ask, if an answer solves the problem it is customary to accept it (additional to upvoting it) so that future users see that it helped. – Jan Apr 19 '22 at 16:26
  • I accepted and upvoted the answer, thank you again. I was being asked to post another question as a separate post. [Here it is](https://stackoverflow.com/questions/71936438/how-to-skip-elements-in-chaining-modal-approach-shinyalert-library). I would be very grateful if you could take a look – Mark Perez Apr 20 '22 at 08:29