Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observe modal (easy) closing in Shiny

I'm looking for a way to trigger an event based upon closing of a Shiny modal when the easy-close option is TRUE (so clicking outside the modal closes it). Since there is no ID linked to a modal, I can't seem to catch this event. I tried wrapping the modal in an 'observe' event, but this only triggers the opening but not closing of the modal.

Any ideas... Thanks


example: I want to trigger an event if this modal closes by clicking outside of it, so not the dismiss button. The code below only triggers when opening...

library(shiny)
ui <- fluidPage(
  fluidRow(
    actionButton(inputId = "enterText", label = "Enter name", align = "left"),
    h1(textOutput("myOutput"))
  )
)

server <- function(input, output, session) {
  myText <- reactiveValues(input = "...")
  myModal = modalDialog(h3("Enter a string, then click outside this modal to close and display the text"),
                        textInput(inputId = "myString", label = "Enter a string: "),
                        title = "Input", easyClose = TRUE, footer = modalButton("Dismiss"))
  test = observe(myModal)

  #Open the modal when button clicked
  observeEvent(input$enterText,{
    showModal(myModal)
    })

  #Observe the modal, should fire when it CLOSES by clicking outside the modal (easy-close)
  observeEvent(test, {
    myText$input = input$myString
    print("observed")
  }, ignoreInit = T)

  output$myOutput = renderText(myText$input)

}

shinyApp(ui = ui, server = server)
like image 291
pieterjanvc Avatar asked Jul 21 '17 19:07

pieterjanvc


2 Answers

I want to provide an easier answer for anyone who doesn't require this to work with easyClose = TRUE, as the OP does -- in that case, this is a much easier solution than SBista's link in the comments.

This simply replaces the modal's default "dismiss" button with an action button of your own, which allows you to implement some additional action when you close the modal. This is set via the footer = argument.

In this example, the modal contains checkboxes, which don't take effect until the modal is closed.

library(shiny)
shinyApp(
  ui <- fluidPage(
    fluidRow(
      ## Button to display modal:
      actionButton(inputId = "display_modal",label = "Display modal"),
      ## Print the choices that were made in the modal:
      h1(textOutput("checked_letters"))
    )
  ),

  server <- function(input, output) {
    ## These values allow the actions made in the modal to be delayed until the
    #  modal is closed
    values = reactiveValues(to_print = "",   ## This is the text that will be displayed
                            modal_closed=F)  ## This prevents the values$to_print output from 
                                             #  updating until the modal is closed

    ## Open the modal when button clicked
    observeEvent(input$display_modal,{
      values$modal_closed <- F
      showModal(modalDialog(
        checkboxGroupInput("checkboxes",label = "Select letters",
                           choices = LETTERS[1:7]),
        ## This footer replaces the default "Dismiss" button,
        #  which is 'footer = modalButton("Dismiss")'
        footer = actionButton("dismiss_modal",label = "Dismiss")
        ))
    })

    ## This event is triggered by the actionButton inside the modalDialog
    #  It closes the modal, and by setting values$modal_closed <- T, it
    #  triggers values$to_print to update.
    observeEvent(input$dismiss_modal,{
      values$modal_closed <- T
      removeModal()
    })
    ## values$to_print is only updated once the modal is closed.
    observe({
      if(values$modal_closed){
        values$to_print <- paste(input$checkboxes)
      }
    })
    ## Forward the values$to_print to the UI
    output$checked_letters = renderText({values$to_print})
  }
)
like image 101
sssheridan Avatar answered Nov 10 '22 00:11

sssheridan


Similar to the first answer, my approach only works with easyClose = FALSE. Instead of using an actionButton together with an observeEvent we could create a custom function modalActionButton, which is basically copy paste from actionButton and just adding data-dismiss = "modal":

I adapt the example from the first answer below:

library(shiny)

# this is basically copied from actionButton() and just "`data-dismiss` = "modal" 
# from modalButton() is added:
modalActionButton <- function(inputId, label, icon = NULL, width = NULL, ...) {
  
  value <- restoreInput(id = inputId, default = NULL)
  tags$button(id = inputId, type = "button", style = if (!is.null(width)) 
    paste0("width: ", validateCssUnit(width), ";"), type = "button", 
    class = "btn btn-default action-button", `data-dismiss` = "modal", `data-val` = value, 
    list(shiny:::validateIcon(icon), label), ...)
  
}

shinyApp(

    ui <- fluidPage(
    fluidRow(
      ## Button to display modal:
      actionButton(inputId = "display_modal",label = "Display modal"),
      
      ## Print the choices that were made in the modal:
      h1(textOutput("checked_letters"))
    )
  ),
  
  server <- function(input, output) {
    
    values = reactiveValues(to_print = "")   ## This is the text that will be displayed

    observeEvent(input$display_modal, {
      showModal(modalDialog(
        checkboxGroupInput("checkboxes",label = "Select letters",
                           choices = LETTERS[1:7]),

        title = "This is a modal page with a 'modalActionButton' ",
        size = "l",
        easyClose = FALSE,
        # here is the modalActionButton
        footer = modalActionButton("close",
                                   "Close")
      ))
    })

    observeEvent(input$close, {
        values$to_print <- input$checkboxes
      })

    ## Forward the values$to_print to the UI
    output$checked_letters <- renderText({values$to_print})

   }
)
like image 2
TimTeaFan Avatar answered Nov 10 '22 00:11

TimTeaFan