rshinyshinyjsshinyalert

How to design a notification system in Shiny to evaluate the input value?


I would like to design a notification in Shiny to evaluate if the input values is equal to a specific number. The following code example is my attempt. This is a simple app checking if the input value is equal to 1000. If not, a notification will show. I used shinyalert to create the notification and the delay function from the shinyjs package to avoid the situation that execution is too sensitive.

This app works but not perfectly. The problem is when the user types in a number. Sometimes it may execute the notification more than once. I believe it is related to how fast the user type in a number. For example, if I type in 5678 into the numericInput very fast, one notification will show, but if I type in 56 and then follow up with 78 very quickly (within a second). The notification will show twice. The app probably evaluates if 56 equals to 1000, then moves on test if 5678 equals to 1000.

If possible, I hope I can show the notification only once. I thought adding the delay function will help, but it did not, or perhaps I did not use the delay function in the right way. Please let me know if you have any feedback.

library(shiny)
library(shinyjs)
library(shinyalert)

ui <- fluidPage(
  
  useShinyjs(),
  useShinyalert(),
  
  titlePanel("Check if the input text is 1000."),
  
  numericInput(inputId = "value_in", value = 0, label = "Input value")
  
)

server <- function(input, output, session){
  
  observeEvent(input$value_in, {
    req(input$value_in)
    delay(
      ms = 2000,
      if (input$value_in != 1000){
        shinyalert(text = "The input value is not 1000", 
                   type = "warning")
      }
    )
  }, ignoreInit = TRUE)
  
}

shinyApp(ui, server)

Solution

  • Debounce is the way to go. Below is some more about how it works, and then a minimal working example.

    Debouncing means that every invalidation from r will be held for the specified time window. If r invalidates again within that time window, then the timer starts over again. This means that as long as invalidations continually arrive from r within the time window, the debounced reactive will not invalidate at all. Only after the invalidations stop (or slow down sufficiently) will the downstream invalidation be sent.

    library(shiny)
    library(shinyjs)
    library(shinyalert)
    library(magrittr)
    
    ui <- fluidPage(
        
        useShinyjs(),
        useShinyalert(),
        
        titlePanel("Check if the input text is 1000."),
        
        numericInput(inputId = "value_in", value = 0, label = "Input value")
        
    )
    
    server <- function(input, output, session){
        
        #Create a debounced reactive, only runs when the input has changed and then been idle for 1000 milliseconds. 
        value_in_debounce <- reactive(input$value_in) %>% debounce(1000)
        
        observeEvent(value_in_debounce(), {
    
            print(paste('Triggered at', Sys.time(), 'Value is:', value_in_debounce()))
    
            if (value_in_debounce() != 1000){
                shinyalert(text = "The input value is not 1000", type = "warning")
            }
    
        }, ignoreInit = TRUE)
        
    }
    
    shinyApp(ui, server)