rshinycallbackmodal-dialogshinyalert

RShiny shinyalert callback not updating variables?


Over the last couple weeks I've been experimenting using shinyalert() to explore the potential of using inputs within an alert and trigger a chain shinyalert() if inputs are not left blank. There a couple of things that are high priority in my mind:

1: I need to cancel the chained modal if the cancel button is selected. I believe I have this part figured out.

2: If any inputs within the shinyalert are left blank, I also need to cancel the chained modal.

The below code seems to function on first use but it quickly becomes evident that it keeps variables in memory (not sure if this is correct terminology) for some reason that I cannot figure out:

library(shiny)
library(shinyalert)

ui <- fluidPage(
  useShinyalert(),
  actionButton("run", "Run", class = "btn-success")
)
server <- function(input, output, session) {
  
  functest = function(num) {
    ans = is.na(num)
    list(ans = ans)
  }
  
  observeEvent(input$run,
               shinyalert(
                 title = "What is your name?",
                 html = T,
                 text = tagList(textInput("name", "What's your name?", "Jack"),
                                numericInput("num", "Number Here", value = NA)),
                 callbackR = function(x) { 
                   if(x != FALSE & !is.na(input$num)) {
                     shinyalert(html = T,
                                text = tagList(
                                  div(paste(input$name, "'s favorite color is Black", sep = "")),
                                  div(paste("Is input$num NA? ", functest(input$num)$ans, sep = ""))
                                ),
                                showCancelButton = T,
                     )
                   }
                   else {
                     closeAlert()
                   }
                 },
                 closeOnEsc = T,
                 showConfirmButton = TRUE,
                 closeOnClickOutside = T,
                 showCancelButton = TRUE,
               )
  )
}
shinyApp(ui = ui, server = server)

If the cancel button is hit, the chained modal will not show up as expect. And seemingly, even if the num input is left blank and the confirm button is pressed, the chained modal will also not show up (yay!). However, when a number is entered, strange things happen after the initial run:

If I change the name to 'Todd' and input a number, the chained modal will display as expected. However, once run again and the default name of 'Jack' is left and no number is entered, it will still call the chained modal and it will display the output from the previous run such that it still says Todd and it will say that the num input is not NA instead of saying 'Jack' and that the num input is NA. Can anyone help explain why this happens?

What confuses me about this further is that it seems as though anything in the callback cannot be directly accessed via typical shiny nomenclature. Below is an example where the chained modal and the original modal both have the exact same uiOutput call. The uiOutput in the initial modal works and the uiOutput in the chained modal does not.

library(shiny)
library(shinyalert)

ui <- fluidPage(
  useShinyalert(),
  actionButton("run", "Run", class = "btn-success")
)
server <- function(input, output, session) {
  observeEvent(input$run,
               shinyalert(
                 title = "What is your name?",
                 html = T,
                 text = tagList(textInput("name", "What's your name?", "Dean"), 
                                div(uiOutput("test"))),
                 callbackR = function(x) { if(x != FALSE) shinyalert(html = T,
                                                                     text = tagList(
                                                                       div(uiOutput("test")))
                                                                     )
                                                                     },
                 closeOnEsc = T,
                 showConfirmButton = TRUE,
                 closeOnClickOutside = T,
                 showCancelButton = TRUE,
               )
               )
  
  output$test = renderUI({
    paste(input$name, "'s favorite color is black", sep = "")
  })
}
shinyApp(ui = ui, server = server)

This is the reason for the functest() function in the first code. If I pass an input to a function, it seems it can be displayed. But using renderUI() or something similar does not work for some reason. I can't help but feel like the two issues are related...

At any rate, I'm sure what I'm confused about is something elementary but I'd really appreciate any input anyone can give. I'd really prefer to use shinyalert() for consistency across the apps I've been building but if something similar can be accomplished via the default modals then that would work too.

Thanks for reading!


Solution

  • Thanks very much to Stéphane Laurent for commenting the existing bug thread, I never would've found that! Evidently my issues are just a bug with shinyalert() and can be fixed by delaying evaluation for ~380 ms. It seems Dave is pretty busy so I'm not sure if/when it will get fixed but if you are encountering similar issues in the future, I'd recommend checking https://github.com/daattali/shinyalert/issues/46 in case this workaround no longer becomes necessary.

    One can do this with delay() in the shinyjs library. While a less than ideal long term solution, it solves my qualms perfectly. Not only does it allow me to use typical shiny nomenclature (using a uiOutput in the callback), but it also fixes the issue I had with Shiny keeping old values. Below is an example of the first set of code adjusted to work (very similar for the second):

    library(shiny)
    library(shinyalert)
    library(shinyjs)
    
    ui <- fluidPage(
      useShinyalert(),
      useShinyjs(),
      actionButton("run", "Run", class = "btn-success")
    )
    
    server <- function(input, output, session) {
      
      observeEvent(input$run,
                   shinyalert(
                     title = "What is your name?",
                     html = T,
                     text = tagList(textInput("name", "What's your name?", "Jack"),
                                    numericInput("num", "Number Here", value = NA)),
                     callbackR = function(x) { 
                       if(x != FALSE & !is.na(input$num)) {
                         delay(400,
                               shinyalert(html = T,
                                          text = tagList(
                                            div(uiOutput("text")),
                                            div(uiOutput("isna"))
                                          )
                               )
                         )
                       }
                         else {
                           closeAlert()
                         }
                     },
                     closeOnEsc = T,
                     showConfirmButton = TRUE,
                     closeOnClickOutside = T
                   )
      )
      
      output$text = renderUI({
        paste(input$name, "'s favorite color is black", sep = "")
      })
      
      output$isna = renderUI({
        paste("Is input$num NA? ", is.na(input$num), sep = "")
      })
    }
    
    shinyApp(ui = ui, server = server)
    

    Thanks again, I hope this is helpful to others!