Search code examples
rvalidationshinyaction-buttonreq

Using validation in an action button


I am a little confused about the difference between req and validate in R shiny. The only real difference I can see is that validate gives a message to the user. I am building an interface and was using a bunch of hidden messages and conditional statements. I would like to condense my code and like the idea of using validate. I only want to show my message when the user tries to click the button and tries to continue to another part of the UI.

I provide a simplified version of the code, the message "Success" will only show when the text input for the id and the agreement button is clicked. If one or both are missing, a conditional panel will display the validation text.

I am concerned that displaying an output inside of an action button destroys the environment and essentially turns it into a reactive environment. I used the req after the validation check so that the success message will not display unless the input is provided for both. Is this the best way to do this? Or is there a more efficient/proper way? My main concern is that when I build up the complexity, I will break the app.

library(shiny)

ui <- fluidPage(
      
      
      textInput(inputId = "id",
                label = 'Please enter your id'
                  ),
      
    
      checkboxInput("agree", label = "I agree", value = FALSE),
      conditionalPanel(condition = "input.id == '' || !input.agree",
                              
                      textOutput('error_msg')
      ),
      
      actionButton("submit_info", "Submit"),
      textOutput('success_msg')
   
  
)

server <- function(input, output) {
  observeEvent(input$submit_info, {
    output$error_msg <- renderText({
      shiny::validate(
        shiny::need(input$id != '', 'You must enter your id above to continue.'
        ),
        shiny::need(input$agree, "You must agree to continue")
      )
      
    })
    
    shiny::req(input$id)
    shiny::req(input$agree)
    output$success_msg <- renderText({"Success"})
  
})
}

shinyApp(ui = ui, server = server)

Solution

  • Update: I have solved this issue. Essentially, I make the conditional panel only show once the button is clicked and moved the validate outside of the observe event. Here is the code:

    library(shiny)
    
    ui <- fluidPage(
          
          
          textInput(inputId = "id",
                    label = 'Please enter your id'
                      ),
          
        
          checkboxInput("agree", label = "I agree", value = FALSE),
          conditionalPanel(condition = "(input.submit_info >= 1) & ((input.id == '') || (!input.agree))",
                                  
                          textOutput('error_msg')
          ),
          
          actionButton("submit_info", "Submit"),
          textOutput('success_msg')
       
      
    )
    
    server <- function(input, output) {
      
      output$error_msg <- renderText({
        shiny::validate(
          shiny::need(input$id != '', 'You must enter your id above to continue.'
          ),
          shiny::need(input$agree, "You must agree to continue")
        )
        
      })
      
      observeEvent(input$submit_info, {
        
        shiny::req(input$id)
        shiny::req(input$agree)
        output$success_msg <- renderText({"Success"})
      
    })
    }
    
    shinyApp(ui = ui, server = server)