Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R shiny passing reactive to selectInput choices

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.

like image 276
PatrickT Avatar asked Jan 30 '14 19:01

PatrickT


People also ask

What is reactive R shiny?

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).

How do you select a shiny input?

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 .

What is isolate shiny R?

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() .


1 Answers

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())     })   } )) 
like image 189
Ramnath Avatar answered Sep 19 '22 13:09

Ramnath