Village.Idyot
Village.Idyot

Reputation: 2119

How to reactively and repeatedly render the same type of object with an action button in R shiny?

The code at the bottom is taken from an example in https://shiny.rstudio.com/articles/modules.html though I de-modularized it so I can understand something more basic. With this code, each click of the action button renders a counter which counts the number of clicks. Fine.

Instead of counting the number of clicks in the same output of verbatimTextOutput() as the code currently works, I'd like each click to be represented as a new output of verbatimTextOutput(). See illustration below which shows what I'm trying to derive, assuming the user clicks the action button 3 times. I don't know how many times the user will click the action button so there's no way to pre-set or hard-code the number of outputs and assign output names such as output$out1, output$output2, etc. Is there a way to reactively generate the outputs names, as a I naively attempted in the below code with output$"paste(out,count())" and verbatimTextOutput("paste(out,count())") (without the quote marks, I only put them in so the example code would work)? If this is possible this could be a way to achieve the results I am seeking.

Illustration:

enter image description here

Code:

library(shiny)

newOutput <- function(x,y){verbatimTextOutput("paste(out,count())")}

ui <- fluidPage(uiOutput("uiButton"))

server <- function(input,output,session){
  count <- reactiveVal(0)
  observeEvent(input$button, {count(count() + 1)})
  output$"paste(out,count())" <- renderText({count()})
  count
  
  output$uiButton <-
    renderUI(
      tagList(
        actionButton("button", label = "Click me"),
        newOutput()
      )
    )
}

shinyApp(ui, server)

Upvotes: 1

Views: 205

Answers (2)

ismirsehregal
ismirsehregal

Reputation: 33530

This is an alternative approach using insertUI.

Compared to @stefan's renderUI based solution it has the advantage, that the UI elements are rendered only once. Using renderUI every element is re-rendered on button click, accordingly things will slow down depending on the number of elements.

library(shiny)

ui <- fluidPage(
  actionButton("add", "Add UI")
)

server <- function(input, output, session) {
  observeEvent(input$add, {
    output_name <- paste0("out_", input$add)
    output[[output_name]] <- renderText({
      isolate(input$add)
    })
    insertUI(
      selector = ifelse(input$add == 0L, "#add", paste0("#", "out_", input$add-1)),
      where = "afterEnd",
      ui = verbatimTextOutput(output_name)
    )
  }, ignoreNULL = FALSE)
}

shinyApp(ui, server)

Also check ?removeUI.

result

Upvotes: 3

stefan
stefan

Reputation: 125418

Adapting this example to dynamically create graphs to your example you could do:

library(shiny)
library(purrr)

newOutput <- function(x) {
  verbatimTextOutput(x)
}

ui <- fluidPage(
  actionButton("button", label = "Click me"),
  uiOutput("uiText")
)

server <- function(input, output, session) {
  count <- reactiveVal(0)
  observeEvent(input$button, {
    count(count() + 1)
    i <- count()
    output_name <- paste("out", i)
    output[[output_name]] <- renderText({
      i
    })
  })

  output$uiText <- renderUI({
    out_list <- map(seq_len(count()), ~ {
      tagList(
        newOutput(paste("out", .x))
      )
    })
    tagList(out_list)
  })
}

shinyApp(ui, server)

enter image description here

Upvotes: 1

Related Questions