Search code examples
rshinyshinydashboard

Use functions or loops to create shiny UI elements


I am creating a shiny app and realized I am repeating a particular UI element so I am wondering if there is a way to wrap this in a function and supply parameters to make it work in different cases. In my server file, I have

output$loss <- renderUI({
    req(input$got)
    if(input$got %in% Years) return(numericInput('got_snow', label = 'John Snow', value = NA))
    if(!input$got %in% Years) return(fluidRow(column(3)))
})

and in the ui file, I have:

splitLayout(
  cellWidths = c("30%","70%"),
  selectInput('got', label = 'Select age', choices = c('',Years) , selected = NULL),
  uiOutput("loss")
)

Since I find myself using these several times and only changing a few things in both the UI and server files, I wanted to wrap these in a function and use them as and when I please. I tried this for the server file

ui_renderer <- function(in_put, label, id){
  renderUI({
    req(input[[in_put]])
    if(input[[in_put]] %in% Years) return(numericInput(id, label = label, value = NA))
    if(!input[[in_put]] %in% Years) return(fluidRow(column(3)))
  })

}
output$p_li <- ui_renderer(input='li', "Enter age", id="c_li")

and in my ui file, I put

uiOutput('c_li')

but it's not working. Any help is greatly appreciated.


Solution

  • I was unable to test your code since there was no minimal working example. I don't know if this is a typo in your example, but your are trying to render c_li, but your output is called p_li. Not sure how wrapping a render object in a standard function works, but I have done something similar using reactive values instead.

    This is a minimal example using some of your terminology. It is not a working example, but an outline of the idea to my proposed solution.

    # Set up the UI ----
    ui <- fluidPage(
      uiOutput("loss")
    )
    
    # Set up the server side ----
    server <- function(input, output, session) {
    
      # Let us define reactive values for the function inputs
      func <- reactiveValues(
        value <- "got",
        label <- "select age",
        id <- "xyz"
      )
    
      # Set up an observer for when any of the reactive values change
      observeEvent({
        func$value
        func$label
        func$id
      }, {
        # Render a new UI any time a reactive value changes
        output[["loss"]] <- renderUI(
          if (input[[func$value]] %in% years) {
            numericInput(func$id, label = func$label, value = NA)
          } else {
            fluidRow(
              column(3)
            )
          }
        )
      })
    }
    
    # Combine into an app ----
    shinyApp(ui = ui, server = server)
    
    

    The general idea is to define a set of reactive values and set up an observer that will render the UI every time one or more of the reactive values change. You can assign a new value to any of the reactive values using direct assignment, e.g. current$value <- "object_two". Making that change will update the UI using Shiny's reactive pattern, which means you only need to change one value to update the UI.