R shiny passing reactive to selectInput choices
r, shiny, shiny-reactivity
Solution
You need to use `renderUI` on the server side for dynamic UIs. Here is a minimal example. Note that the second drop-down menu is reactive and adjusts to the dataset you choose in the first one. The code should be self-explanatory if you have dealt with shiny before.
runApp(list(
ui = bootstrapPage(
selectInput('dataset', 'Choose Dataset', c('mtcars', 'iris')),
uiOutput('columns')
),
server = function(input, output){
output$columns = renderUI({
mydata = get(input$dataset)
selectInput('columns2', 'Columns', names(mydata))
})
}
))
EDIT. Another Solution using `updateSelectInput`
runApp(list(
ui = bootstrapPage(
selectInput('dataset', 'Choose Dataset', c('mtcars', 'iris')),
selectInput('columns', 'Columns', "")
),
server = function(input, output, session){
outVar = reactive({
mydata = get(input$dataset)
names(mydata)
})
observe({
updateSelectInput(session, "columns",
choices = outVar()
)})
}
))
EDIT2: Modified Example using `parse`. In this app, the text formula entered is used to dynamically populate the dropdown menu below with the list of variables.
library(shiny)
runApp(list(
ui = bootstrapPage(
textInput("text", "Enter Formula", "a=b+c"),
uiOutput('variables')
),
server = function(input, output){
outVar <- reactive({
vars <- all.vars(parse(text = input$text))
vars <- as.list(vars)
return(vars)
})
output$variables = renderUI({
selectInput('variables2', 'Variables', outVar())
})
}
))
Problem
In a shiny app (by RStudio), on the server side, I have a reactive that returns a list of variables by parsing the content of a `textInput`. The list of variables is then used in `selectInput` and/or `updateSelectInput`. I can't make it work. Any suggestions? I have made two attempts. The first approach is to use the reactive `outVar` directly into `selectInput`. The second approach is to use the reactive `outVar` in `updateSelectInput`. Neither works. server.R ``` shinyServer( function(input, output, session) { outVar <- reactive({ vars <- all.vars(parse(text=input$inBody)) vars <- as.list(vars) return(vars) }) output$inBody <- renderUI({ textInput(inputId = "inBody", label = h4("Enter a function:"), value = "a+b+c") }) output$inVar <- renderUI({ ## works but the choices are non-reactive selectInput(inputId = "inVar", label = h4("Select variables:"), choices = list("a","b")) }) observe({ ## doesn't work choices <- outVar() updateSelectInput(session = session, inputId = "inVar", choices = choices) }) }) ``` ui.R ``` shinyUI( basicPage( uiOutput("inBody"), uiOutput("inVar") ) ) ``` A short while ago, I posted the same question at shiny-discuss, but it has generated little interest, so I'm asking again, with apologies, https://groups.google.com/forum/#!topic/shiny-discuss/e0MgmMskfWo Edit 1 @Ramnath has kindly posted a solution that appears to work, denoted Edit 2 by him. But that solution does not address the problem because the `textinput` is on the `ui` side instead of on the `server` side as it is in my problem. If I move the `textinput` of Ramnath's second edit to the `server` side, the problem crops up again, namely: nothing shows and RStudio crashes. I found that wrapping `input$text` in `as.character` makes the problem disappear. Edit 2 In further discussion, Ramnath has shown me that the problem arises when the server attempts to apply the dynamic function `outVar` before its arguments have been returned by `textinput`. The solution is to first check whether `is.null(input$inBody)` exists. Checking for existence of arguments is a crucial aspect of building a shiny app, so why did I not think of it? Well, I did, but I must have done something wrong! Considering the amount of time I spent on the problem, it's a bitter experience. I show after the code how to check for existence. Below is Ramnath's code with `textinput` moved to the `server` side. It crashes RStudio so don't try it at home. (I have used his notation) ``` library(shiny) runApp(list( ui = bootstrapPage( uiOutput('textbox'), ## moving Ramnath's textinput to the server side uiOutput('variables') ), server = function(input, output){ outVar <- reactive({ vars <- all.vars(parse(text = input$text)) ## existence check needed here to prevent a crash vars <- as.list(vars) return(vars) }) output$textbox = renderUI({ textInput("text", "Enter Formula", "a=b+c") }) output$variables = renderUI({ selectInput('variables2', 'Variables', outVar()) }) } )) ``` The way I usually check for existence is like this: ``` if (is.null(input$text) || is.na(input$text)){ return() } else { vars <- all.vars(parse(text = input$text)) return(vars) } ``` Ramnath's code is shorter: ``` if (!is.null(mytext)){ mytext = input$text vars <- all.vars(parse(text = mytext)) return(vars) } ``` Both seem to work, but I'll be doing it Ramnath's way from now on: maybe an unbalanced bracket in my construct had earlier prevented me to make the check work? Ramnath's check is more direct. Lastly, I'd like to note a couple of things about my various attempts to debug. In my debugging quest, I discovered that there is an option to "rank" the priority of "outputs" on the server side, which I explored in an attempt to solve my problem, but didn't work since the problem was elsewhere. Still, it's interesting to know and seems not very well known at this time: ``` outputOptions(output, "textbox", priority = 1) outputOptions(output, "variables", priority = 2) ``` In that quest, I also tried `try`: ``` try(vars <- all.vars(parse(text = input$text))) ``` That was pretty close, but still did not fix it. The first solution I stumbled upon was: ``` vars <- all.vars(parse(text = as.character(input$text))) ``` I suppose it would be interesting to know why it worked: is it because it slows things down enough? is it because `as.character` "waits" for `input$text` to be non-null? Whatever the case may be, I am extremely grateful to Ramnath for his effort, patience and guidance.