1

In my Shiny application, I'd like the click of a button to dynamically populate a list of new buttons. Each of these buttons should act specifically and uniquely.

So far I have been able to get the "main" button to create the list of buttons. I have not been able to figure out how to get each of the individual buttons to act accordingly. For simplicity, let's say that I'd like to print the label of the button. In reality, each button triggers should trigger a a variety of things and set a number of reactive values, but that's outside the scope for now.

Here is a reproducible example. In particular, only the last button receives an event listener. And when it fires, it for some reason fires N times; in this example N = 5. In reality too, the buttons may disappear on another event, but then should show up again if that "main" button was clicked a second time.

library(shiny)

ui <- pageWithSidebar(headerPanel("test"),

                      sidebarPanel(actionButton("build", "Build")),

                      mainPanel(uiOutput("buttons")))

server <- function(input, output) {
    observeEvent(input$build, {
        output$buttons <- renderUI({
            btns <- list()
            for (ix in 1:5) {
                name <- paste("btn_", ix)
                btns <- c(btns,
                          list(actionButton(
                              name, paste("Button", ix)
                          )))
                observeEvent(input[[name]], {
                    print(name)
                })
            }
            btns
        })
    })
}

shinyApp(ui, server)

What do I need to do in order to have an event listener associated with each button?

I have tried a variety of things, including wrapping the observeEvent() in local({}), creating a new button name with the namespace like ns <- NS(name) and ns(name) after, and moving renderUI outside of the parent observeEvent().

I've taken a look at Shiny - Can dynamically generated buttons act as trigger for an event, How to create dynamic number of observeEvent in shiny?, and Dynamic number of actionButtons tied to unique observeEvent, among others, but either I didn't understand them or they weren't quite right.

Bonus points if you can explain what's happening behind the scenes so I can learn more about Shiny reactivity!

Created on 2021-12-01 by the reprex package (v2.0.1)

Alex L
  • 470
  • 6
  • 15

1 Answers1

0

Here is a sample app using purrr from the tidyverse that work very good to create multiple inputs. I also added textOutput's to see that the buttons are being clicked. This app could be further modified to select the amount of buttons to create.

App::

library(shiny)

number_of_buttons <- 5
outputs <- map(paste0('btn_', seq(1:number_of_buttons)), ~textOutput(outputId = .)) 

ui <- pageWithSidebar(headerPanel("test"),
                      
                      sidebarPanel(actionButton("build", "Build")),
                      
                      mainPanel(uiOutput("buttons"),
                                tagList(outputs)))


# server ------------------------------------------------------------------

server <- function(input, output) {
  observeEvent(input$build, {
    
    
    #this creates a list with five buttons to pass into `renderUI`
    buttns_inputs <- 
    paste0('btn_', seq(1:number_of_buttons)) %>% 
    map(~ actionButton(., .))
    
    
    output$buttons <- renderUI({
      tagList(buttns_inputs)
    })
    
    
  })
  
  #for debugging purposes
  
  observe({
   req(input$btn_1)
   names(input) %>%
     str_subset('^btn') %>%
     walk(~ {output[[.]] <<-  renderPrint({input[[.]] %>% print}) }) 
  })

}

shinyApp(ui, server)

enter image description here

Simplified version:

library(shiny)

number_of_buttons <- 5

ui <- pageWithSidebar(
  headerPanel("test"),
  sidebarPanel(actionButton("build", "Build")),
  mainPanel(uiOutput("buttons"))
)


# server ------------------------------------------------------------------

server <- function(input, output) {
  observeEvent(input$build, {
    # this creates a list with five buttons to pass into `renderUI`
    buttns_inputs <-
      paste0("btn_", seq(1:number_of_buttons)) %>%
      map(~ actionButton(., .))

    output$buttons <- renderUI({
      tagList(buttns_inputs)
    })
  })
}

shinyApp(ui, server)
jpdugo17
  • 6,816
  • 2
  • 11
  • 23