3D0G 3D0G - 10 months ago 198
R Question

Multiple action buttons with one event handler in Shiny?

I'd like to have a variable number of identical actionButton()s on a page all handled by one observeEvent() function.

For example, in a variable-length table of tables, I'd like each interior table to have a button that links to more information on that table.

In standard HTML, you do this with a simple form, where you use a hidden input to designate the interior table number, like this:

<form ...>
<input id="table_number" type="hidden" value="1"/>
<input type="submit" value="Examine"/>

When a button is pressed, you can examine the hidden input to see which one it was.

Is there a way to do this in Shiny? The only solution I've come up with is to give each actionButton() it's own inputId. This requires a separate observeEvent() for each button. Those have to be created ahead of time, imposing a maximum number of buttons.

Answer Source

You could use shiny modules for this: you can have variable number of actionButton that are identical. These are defined in the ab_moduleUI part. They are handled by their own observeEvent but it has to be defined only once in the ab_module part.

With lapply any number of actionButton can be created.

Edit: You don't have to specify the number of buttons beforehand: use renderUI to generate UI elements at server side.

For demonstration purposes I added a numericInput to increase/decrease the number of modules to render.

# UI part of the module
ab_moduleUI <- function(id){
  ns <- NS(id)
      actionButton(ns("btn"), paste("ActionButton", id, sep="-")),

# Server part of the module
ab_module <- function(input, output, session){
    output$txt <- renderText("More information shown")

# UI
ui <- fluidPage(
  # lapply(paste0("mod", 1:no_btn), ab_moduleUI)
  numericInput("num", "Number of buttons to show" ,value = 5, min = 3, max = 10),

# Server side
server <- function(input, output, session){
  observeEvent(input$num, {
    output$ui <- renderUI({
      lapply(paste0("mod", 1:input$num), ab_moduleUI)

    lapply(paste0("mod", 1:input$num), function(x) callModule(ab_module, x))


shinyApp(ui, server)

Read more about shiny modules here