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.
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) }) })
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.
A reactive expression is an R expression that uses widget input and returns a value. The reactive expression will update this value whenever the original widget changes. To create a reactive expression use the reactive function, which takes an R expression surrounded by braces (just like the render* functions).
By default, selectInput() and selectizeInput() use the JavaScript library selectize. js (https://github.com/selectize/selectize.js) instead of the basic select input element. To use the standard HTML select input element, use selectInput() with selectize=FALSE .
The isolate function lets you read a reactive value or expression without establishing this relationship. The expression given to isolate() is evaluated in the calling environment. This means that if you assign a variable inside the isolate() , its value will be visible outside of the isolate() .
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()) }) } ))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With