4

I want to create a dynamic UI in Shiny, where each time a button is clicked, a new UI element is created with several input fields. I was hoping that I could do this using reactiveValues, however the ui code can't access them, so I can't tell it how many elements to show.

Here's a reproducible example with just a single UI field created on each click - it works for the first two clicks of the button, but since the lapply in the ui section is coded to a fixed value (3 in this example), after that the new ones stop being displayed. I know I could set the ui value at a higher number, but what I'd like is for it to be reactive. (In the full version I'd like to have nested elements within each of these that work the same way, and buttons to remove each field as well.)

server <- function(input, output) {

  rv <- reactiveValues(numFields = 1)
  #
  # start with one input box
  #
  output$textUI1 <- renderUI(textInput("textInput1", "Input #1"))
  #
  # each time the button is clicked, increase the reactive value
  #
  observeEvent(input$addField, rv$numFields <- rv$numFields + 1)
  #
  # render any additional UI input fields according to value of rv$numFields
  #
  observe({
    if(rv$numFields > 1)
    {
      lapply(2:rv$numFields, function(i) {
        output[[paste0("textUI", i)]] <- renderUI({
          textInput(paste0("textInput", i), paste0("Input #", i))
        })
      })
    }
  })
}

ui <- fluidPage(sidebarLayout(
  sidebarPanel(
    actionButton("addField", "Add text input box")
  ),

  mainPanel(
    # UI output
    lapply(1:3, function(i) { # instead of 3 I want something like rv$numFields here
      uiOutput(paste0("textUI", i))
    })
  )
))

shinyApp(ui, server)
user3757897
  • 316
  • 2
  • 13

1 Answers1

4

Instead of passing the variable from server to ui why don't you create the whole dynamic ui inside your server. Something like this:

library (shiny)

server <- function(input, output) {

  rv <- reactiveValues(numFields = 1)
  #
  # start with one input box
  #
   output$textUI <- renderUI(textInput("textInput1", "Input #1"))
  #
  # each time the button is clicked, increase the reactive value and add a new text input

  observeEvent(input$addField,{
    rv$numFields <- rv$numFields + 1
      output$textUI <- renderUI({
        lapply(1:rv$numFields, function(i) {textInput(paste0("textInput", i), paste0("Input #", i))
      })
        })

  })
}

ui <- fluidPage(sidebarLayout(
  sidebarPanel(
    actionButton("addField", "Add text input box")
  ),

  mainPanel(
      uiOutput("textUI")
  )
))

shinyApp(ui, server) 
SBista
  • 7,479
  • 1
  • 27
  • 58
  • 1
    That definitely solves the problem of adding new fields - how would I prevent already-entered text input from being removed though each time I add a new one? – user3757897 Sep 27 '17 at 13:13
  • You could refer to [this](https://stackoverflow.com/questions/41233242/add-remove-input-fields-dynamically-by-a-button-in-shiny-and-keep-values/41460121#41460121) link – SBista Sep 27 '17 at 13:23