Kay
Kay

Reputation: 2332

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.

Upvotes: 0

Views: 507

Answers (1)

edsandorf
edsandorf

Reputation: 767

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.

Upvotes: 2

Related Questions