daattali / shinyalert

🗯️ Easily create pretty popup messages (modals) in Shiny
https://daattali.com/shiny/shinyalert-demo/
Other
241 stars 26 forks source link

Retain input state in modal #42

Closed tylerlittlefield closed 3 years ago

tylerlittlefield commented 3 years ago

Hi @daattali just checked out the latest and greatest shinyalert after seeing your tweet, congrats 🎉

I am currently using shinyBS::bsModal instead of shinyalert::shinyalert, shiny::modalDialog, etc. because shinyBS::bsModal retains the state of my inputs. If I select a couple things from a selectInput, close the modal and then reopen, those selections are still there. This is a very important feature for my use case as I want to prevent users from having to reselect things. Is it possible to preserve the state of inputs when the modal is closed and then reopened? Below is a minimal example that doesn't preserve state:

library(shiny)

ui <- fluidPage(
  shinyalert::useShinyalert(),
  actionButton("go", "go")
)

server <- function(input, output, session) {
  observeEvent(input$go, {
    shinyalert::shinyalert(
      title = "hello",
      html = TRUE,
      text = tagList(
        selectInput("letters", "letters", letters, multiple = TRUE)
      )
    )
  })
}

shinyApp(ui, server)
daattali commented 3 years ago

That's an interesting question. I suppose the reason this is happening is because the other modal solutions define the modal as UI, and in the server you only show/dismiss the modal, so it's always the same one. But in shinyalert I deliberately made the decision that the modal should be defined in the server, because it felt weird and disconnected for me to define it as UI and then call it at a different place. But I guess you just found a problem with my design!

With shinyalert, every time you call 'shinyalert()' it re-created the the UI, so you can't really retain the previous elements.

To achieve what you want, I think a workaround could be to use a reactiveVal. The following code is untested but I believe something like it should work:

library(shiny)

ui <- fluidPage(
    shinyalert::useShinyalert(),
    actionButton("go", "go")
)

server <- function(input, output, session) {
    letters_selected <- reactiveVal(NULL)
    observeEvent(input$go, {
        shinyalert::shinyalert(
            title = "hello",
            html = TRUE,
            text = tagList(
                selectInput("letters", "letters", letters, multiple = TRUE, selected = letters_selected())
            )
        )
    })

    observe({
        letters_selected(input$letters)
    })
}

shinyApp(ui, server)
tylerlittlefield commented 3 years ago

reactiveValues is something I have tried in the past, but ended up giving up as repopulating the inputs was too much work (reactive selectInputs/sliders, etc.) and hurt performance. I'll close this issue, thanks for the response!

daattali commented 3 years ago

That make sense. If you'd like to add a small section to the README about comparison to other models and the drawback/limitation of shinyalert so that other users would know this limitation, I'd be happy for a PR

tylerlittlefield commented 3 years ago

Sounds good! If I have time, I might submit a PR, would love to contribute to this package.

I will add that one benefit to modals like shinyalert that are defined in server as opposed to UI, is you don’t need to worry about tabs/pages in app. With modals that are defined in UI, that modal will only show up on the tab/page it’s defined, so I’ve had to come up with some strange navigation logic to preserve inputs 😅