2
votes

I'm creating a shiny app with a selectInput dynamically populated with a reactive function, created via renderUI. The selectInput is in a separate "configuration" tabpanel. I noticed that after running the App the input$id value remains NULL until I click on the tabPanel where the uiOutput() resides. Then after it is rendered, input$id is assigned with the "selected" value.

I tried to use req(input$id) where needed but it did not work. If I try running req(input$id) in the Debug console the error returned is just "Error: ". I also tried with shinydashboard but got the same behaviour.

I created a minimal reproducible example. The textOutput in the tabPanel "A" should display the choice of the selectInput. But until you clic on the second tabPanel, it shows nothing.

library(shiny)

ui <- fluidPage(
  tabsetPanel(
    tabPanel("one", textOutput('text')),
    tabPanel("two",
             uiOutput('select'))
  )
)

server <- function(input, output, session) {
  c <- reactive({
    list("A", "B", "C")
  })

  output$select <- renderUI({
    selectInput(
      inputId = "choice",
      label = "Select input",
      choices = c(),
      selected = 1
    )
  })

  output$text <- renderText(
    input$choice
  )
}

shinyApp(ui, server)

If I directly put a selectInput in the second tabPanel (without renderUI), input$choice is initialised right from the app start

e.g.

library(shiny)

ui <- fluidPage(
  tabsetPanel(
    tabPanel("one", textOutput('text')),
    tabPanel("two",
             selectInput(
               inputId = "choice",
               label = "Select input",
               choices = c("A", "B", "C"),
               selected = 1
             ))
  )
)

server <- function(input, output, session) {

  output$text <- renderText({
    input$choice
  })
}

shinyApp(ui, server)

Is there any way to "initialise" the renderUI element without having to load its tabPanel?

Thanks

1
Add to server: outputOptions(output, "choice", suspendWhenHidden = FALSE) - Ryan Morton
Thanks. This works but only if placed after the assignment of the outputs in the server. I think I prefer this approach because there's less code involved. - kofm

1 Answers

3
votes

That's beacause of lazy loading in shiny.

Have a look at RStudios article on reactivity:

However, you must tell R to run the expresssion for this to happen because R uses a style of execution known as lazy evaluation. In other words, R will not execute an expression until you force it to.

Thus, you have to force renderUI to evaluate. That means the only way that your user has a valid input for your textOutput is to use reactive providing a default value which is updated as soon as input$choice is rendered.

Here I've added e.g. reactiveValues for reactive variable d to your code:

library(shiny)

ui <- fluidPage(
  tabsetPanel(
    tabPanel("one", textOutput('text')),
    tabPanel("two",
             uiOutput('select'))
  )
)

server <- function(input, output, session) {
  c <- reactive({
    list("A", "B", "C")
  })
  d <- reactiveValues(choice = "A")

  output$select <- renderUI({
    selectInput(
      inputId = "choice",
      label = "Select input",
      choices = c(),
      selected = 1
    )
  })

  observe({
    req(input$choice)
    d$choice <- input$choice
  })

  output$text <- renderText(
    d$choice
  )
}

shinyApp(ui, server)