I am using renderUI to create a "DisplayName" input and I have a reactive function called "ReactiveTest" that uses input$DisplayName. If you run the code below you will see that the "DisplayName" drop down list calls the NameAndID() which in turn calls GetDisplayName() in the global.r file. That call populates the "DisplayName" dropdown list.
BUT when ReactiveTest() tries to access input$DisplayName it says that input$DisplayName is NULL then there seems to be a second call to ReactiveTest() and THEN it sees the input$DisplayName
so when you run the code you will see the timing looks like this:
[1] "Department dropdown list is being returned now"
[1] "Department dropdown list is being returned now"
Listening on http://155.0.0.1:555
[1] "DISPLAY NAME dropdown list is being returned now"
[1] "Now ReactiveTest() tries to access input$DisplayName"
[1] "ReactiveTest() says input$DisplayName Name is NULL" --> why is it NULL? if 2 lines above the dropdown was populated???
[1] "Now ReactiveTest() tries to access input$DisplayName"--> Where is this coming from? Shouldn't there only be 1 call to ReactiveTest()
[1] "ReactiveTest() says input$DisplayName Name is Bob"
I have 3 questions:
(1) Why does ReactiveTest() not see the input$DisplayName after the DisplayName dropdown is called?
(2) Why is there a second call to ReactiveTest()?
(3) How can I make ReactiveTest() see whats in the "DisplayName" dropdown the first call? In my real code I have to test for is.null(input$DisplayName)== TRUE and then not run some code but isn't there a better way?
Here is my code which you can run:
library(shiny)
runApp(list(
ui = basicPage(
sidebarPanel(
selectInput("Department", "Select a department",
choices = as.character(GetDepartments()$Department),
selected = as.character(GetDepartments()$Department[1])),
uiOutput("DisplayName")
),
mainPanel(textOutput("Text") )
),
server = function(input, output, session) {
NameAndID<- reactive({
GetDisplayName(input$Department)
})
output$DisplayName<-renderUI({
Department <- input$Department
selectInput("DisplayName", 'DisplayName:', choices = as.character(NameAndID()$DisplayName), selected =as.character(NameAndID()$DisplayName[1] ))
})
ReactiveTest<- reactive({
print("Now ReactiveTest() tries to access input$DisplayName")
if(is.null(input$DisplayName) == TRUE)
{
print("ReactiveTest() says input$DisplayName Name is NULL")
return("NULL")
}else
{
print(paste0("ReactiveTest() says input$DisplayName Name is ", input$DisplayName))
return(input$DisplayName)
}
})
output$Text <- renderText({
#myData <- GetDisplayName(input$Department)
#code <- as.character(myData[myData$DisplayName == input$DisplayName,2])
return(ReactiveTest())
})
}
))
global.r
GetDepartments<- function(){
df<- data.frame(Department= c("Dept A", "Dept B"), id = c(1,2))
print("Department dropdown list is being returned now")
return(df)
}
GetDisplayName<- function(Dept){
if(Dept == "Dept A")
{
df<- data.frame(DisplayName= c("Bob", "Fred"), id = c(4,6))
print("DISPLAY NAME dropdown list is being returned now")
return(df)
}else
{
df<- data.frame(DisplayName= c("George", "Mary"), id = c(10,20))
print("DISPLAY NAME dropdown list is being returned now")
return(df)
}
}
Your questions are essentially all asking the same thing, why is the function being called twice?
As I mentioned in your previous question, reactive expressions will only run again if they realize something has changed in the input. When you initialize a shiny app with a reactive ui, the values will not be initially loaded (i.e. null values). This has come up before with other shiny questions, such as this one on google groups and a similar one on SO, and the general consensus is that you should have some sort of check in your code (would love to be proven wrong). So your instinct to add a is.null check is correct. This is therefore a nice opportunity to use the validate function. Although this is initialization problem is somewhat annoying, the time taken during initialization should be insignificant.
Here is an example using validate. This however makes it very easy to provider a user friendly error message if somehow a null value is passed and anything passed the validate is not run. More straightforward, in the reactive expression, the initialization only prints and then leaves the function. This type of validation is recommended and documentation can be found here
runApp(list(
ui = basicPage(
sidebarPanel(
selectInput("Department", "Select a department",
choices = as.character(GetDepartments()$Department),
selected = as.character(GetDepartments()$Department[1])),
uiOutput("DisplayName")
),
mainPanel(textOutput("Text") )
),
server = function(input, output, session) {
NameAndID<- reactive({
GetDisplayName(input$Department)
})
output$DisplayName<-renderUI({
Department <- input$Department
selectInput("DisplayName", 'DisplayName:', choices = as.character(NameAndID()$DisplayName), selected =as.character(NameAndID()$DisplayName[1] ))
})
ReactiveTest<- reactive({
print("Now ReactiveTest() tries to access input$DisplayName")
validate(
need(!is.null(input$DisplayName), "ReactiveTest() says input$DisplayName Name is NULL")
)
print(paste0("ReactiveTest() says input$DisplayName Name is ", input$DisplayName))
return(input$DisplayName)
})
output$Text <- renderText({
return(ReactiveTest())
})
}
))
Related
I have a shiny app where you can upload some raw data and build a summarizedExperiment (let's call it RAW) or you can upload an already processed summarizedExperiment (let's call it FINAL). Now I need to save in a reactive object RAW or FINAL. I tried something like this:
sumexp_all = reactive({
if(!is.null(RAW())){
list(sumexp_data = RAW()$sumexp_data, sumexp_data_mean = RAW()$sumexp_data_mean, replicates = RAW()$replicates)
}else if(!is.null(input$finalsumexpinput)){
readRDS(file = input$finalsumexpinput$datapath)
}else{
NULL
}
})
But in this case it works only if I have RAW (if I don't have RAW (so should be NULL since there are many req() in the pipeline) and I want to upload FINAL, nothing happens). If reverse the if conditions (before FINAL and then RAW) I can upload the FINAL. What's wrong? Is it correct to evaluate a reactive with is.null()?
I tried something else like this:
sumexp_all = reactive({
checkerror <- tryCatch(RAW(), error = function(e) "empty")
if(checkerror != "empty"){
list(sumexp_data = RAW()$sumexp_data, sumexp_data_mean = RAW()$sumexp_data_mean, replicates = RAW()$replicates)
}else if(!is.null(input$finalsumexpinput)){
readRDS(file = input$finalsumexpinput$datapath)
}else{
NULL
}
})
But the shiny app crashes and returns this error:
Warning: Error in if: argument is of length zero
And that's weird because if I print checkerror it correctly returns "empty" if's empty.
UPDATE:
I made some testing in order to find where is the problem, and the problem is with the RAW() reactive. Basically I tried this code to find the problem:
checkraw = reactive({
if(is.null(RAW())){
"raw is null"
}else{
"raw is not null"
}
})
checkinp = reactive({
if(is.null(input$finalsumexpinput$datapath)){
"input non loaded"
}else{
"input loaded"
}
})
output$printcheck= renderPrint({
paste(checkraw(), ",", checkinp())
})
So what I expect from this code is that if RAW() doesn't exist and I uplaod the rds file, it' printed "raw is null, input loaded", but what happens is that nothing is displayed.
If RAW() exists then it's correctly printed "raw is not null, input not loaded"
Then I tried to print only checkinp() (removing the checkraw() reactive) and then checkinp() is correctly printed.
So it seems to me that the problem is in the evaluation of the RAW() reactive. Do you think that the problem could be that RAW() depends on others reactive variables (so there is a req() inside it).
EDIT:
Here is a reproducible example.
ui <- shinyUI(pageWithSidebar(
headerPanel("check"),
sidebarPanel(
actionButton("Button", "enable raw"),
textInput("text", "write something", "something..."),
br(),
),
mainPanel(
verbatimTextOutput("printcheck"),
verbatimTextOutput("checkfin")
)
))
server <- shinyServer(function(input, output) {
stepa = eventReactive(input$Button, {
rnorm(100)
})
stepb = reactive({
req(stepa())
stepa() * 100
})
output$printcheck = renderPrint({
is.null(stepb())
})
fin = reactive({
if(!is.null(stepb())){
stepb()
}else{input$text}
})
output$checkfin = renderPrint({
fin()
})
})
shinyApp(ui=ui,server=server)
As you can see output$checkfin is printed ONLY if stepb() is not null even if in this case should be printed input$text
I posted the problem on the shiny github page. For those interested, here's the answer.
https://github.com/rstudio/shiny/issues/3533
I am trying to implement user feedback for an app I'm working on using the shinyvalidate package. Some of the feedback I want to communicate to the user is whether a value they have selected is within a specific range or not. The range of interest depends on another input they have made (I have included a simplified repex of the code).
Thus, whether the precondition of interest is met depends on a reactive value. I first define a function that checks whether a provided value is within a specified range. Then I call this function within add_rule() using a reactive as an argument to the function. This results in an error, which states that I cannot access the reactive using add_rule. This is supposedly the case because the InputValidator object is not a reactive consumer.
Error message:
Warning: Error in Can't access reactive value 'num1' outside of reactive consumer.
i Do you need to wrap inside reactive() or observer()?
55: <Anonymous>
Error: Can't access reactive value 'num1' outside of reactive consumer.
i Do you need to wrap inside reactive() or observer()?
However, if I use an unnamed function within add_rule(), I can specify a range that depends on a reactive and I no longer get the error message. The unnamed and named functions I use are identical and I don't understand why I get an error message using a named function but I do not get the error message when using the named function.
Here is my code using the named function:
library(shiny)
library(shinyvalidate)
checkRange <- function(value, value2){
if(value < -2 * value2 || value > 2 * value2 ){
paste0("Please specify a number that is within the range: ", -2 * value2, ":", 2 * value2, " - Tank you!")
}
}
ui <- fluidPage(
fluidRow(
numericInput("num1",
"Please specify your first number",
value = NULL),
numericInput("num2",
"Please specify a different number",
value = NULL),
verbatimTextOutput("selectedNums")
)
)
server <- function(input, output, session){
iv <- InputValidator$new()
iv$add_rule("num1", sv_required())
iv$add_rule("num2", sv_required())
iv$add_rule("num2", checkRange, value2 = input$num1)
iv$enable()
output$selectedNums <- renderPrint({
req(iv$is_valid())
paste0("The first number = ", input$num1, " and the second number = ", input$num2)
})
}
app <- shinyApp(ui, server)
runApp(app)
And here is the code using an anonymous function (UI and server code are largely identical except one call to iv$add_rule()):
library(shiny)
library(shinyvalidate)
ui <- fluidPage(
fluidRow(
numericInput("num1",
"Please specify your first number",
value = NULL),
numericInput("num2",
"Please specify a different number",
value = NULL),
verbatimTextOutput("selectedNums")
)
)
server <- function(input, output, session){
iv <- InputValidator$new()
iv$add_rule("num1", sv_required())
iv$add_rule("num2", sv_required())
iv$add_rule("num2", function(value){
if(value < - 2 * input$num1 || value > 2 * input$num2){
paste0("Please specify a number that is within the range: ", -2 * input$num1, ":", 2 * input$num1, " - Tank you!")
}
})
iv$enable()
output$selectedNums <- renderPrint({
req(iv$is_valid())
paste0("The first number = ", input$num1, " and the second number = ", input$num2)
})
}
app <- shinyApp(ui, server)
runApp(app)
I would prefer to use the named function since I would like to reuse the code multiple times. Could anyone help me out as to why I get an error message with the named function but not with the unnamed one?
You could do:
iv$add_rule("num2", function(value){checkRange(value, input$num1)})
How one should check whether or not the validate(need()) conditions are fulfilled in a reactive expression?
Trying to get access to a reactive expression when this one does not fulfill the conditions lead to a silent error and stops the function/observer whatever stored the initial call. While this makes perfect sense from an UI perspective, how to handle it from a function perspective and prevent a premature stop?
Below an example (based on the genuine Shiny Validate tutorial) which replicates this behavior: until you select a value in the selectInput(), you will not see the console message "This message will only be printed when data() validate/need are fulfilled." when clicking the button because the observeEvent() is stopped. In the meantime, you may still be eager to do something, like writing an empty file or triggering something at this point, etc.
require(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),
mainPanel(
actionButton(inputId = "actbtn", label = "Print in console reactive value")
)
)
)
server <- function(input, output) {
data <- reactive({
validate(
need(input$data != "", "Please select a data set"),
need(input$data %in% c("mtcars", "faithful", "iris"),
"Unrecognized data set"),
need(input$data, "Input is an empty string"),
need(!is.null(input$data),
"Input is not an empty string, it is NULL")
)
get(input$data, 'package:datasets')
})
observeEvent(input$actbtn, {
print(data())
print("This message will only be printed when data() validate/need are fulfilled.")
})
}
shinyApp(ui, server)
I tried to check the "state" of the reactive expression prior to actually calling it, without success for now. I can of course adapt my app (for instance by doing a validation-like by myself or just re-performing the conditions prior to calling the reactive expression) but I am quite confident that there is a smarter way to handle this context.
When using req or validate/need, the chain of reactivity is disrupted if not completely valid. This means that anything that depends on the reactive block will also not fire (due to the data block). I don't know of a way to "peek" into the reactive block's validation, since that would involve evaluating the expression and catching the shiny-specific signals.
Typically, it is sufficient in a shiny app that the actbtn observer will not fire due to data not validating. However, if you need to know in one expression whether another is fire-able, you can set up a third reactive block that always returns a known element (i.e., no validate in it).
library(shiny) # don't use require without checking its return value
# https://stackoverflow.com/a/51263513/3358272
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),
mainPanel(
actionButton(inputId = "actbtn", label = "Print in console reactive value")
)
)
)
server <- function(input, output) {
isgood <- reactive({
if (is.null(input$data) || !nzchar(input$data)) {
"Please select a data set"
} else if (!input$data %in% c("mtcars", "faithful", "iris")) {
"Unrecognized data set"
} else character(0)
})
data <- reactive({
validate(need(!length(isgood()), isgood()))
get(input$data, 'package:datasets')
})
observeEvent(input$actbtn, {
validate(need(!length(isgood()), isgood()))
print("This message will only be printed when data() validate/need are fulfilled.")
})
}
shinyApp(ui, server)
An alternative is to disable the button (cannot be clicked) until data() is valid. This can be done with shinyjs, which also requires a change to the ui component.
library(shiny)
library(shinyjs)
ui <- fluidPage(
shinyjs::useShinyjs(), # <-- add this
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),
mainPanel(
actionButton(inputId = "actbtn", label = "Print in console reactive value")
)
)
)
server <- function(input, output) {
observeEvent(TRUE, {
message("quux")
shinyjs::disable("actbtn")
}, once = TRUE)
data <- reactive({
validate(
need(input$data != "", "Please select a data set"),
need(input$data %in% c("mtcars", "faithful", "iris"),
"Unrecognized data set"),
need(input$data, "Input is an empty string"),
need(!is.null(input$data),
"Input is not an empty string, it is NULL")
)
get(input$data, 'package:datasets')
})
observeEvent(data(), {
shinyjs::toggleState("actbtn", condition = !is.null(data()))
})
observeEvent(input$actbtn, {
print("This message will only be printed when data() validate/need are fulfilled.")
})
}
shinyApp(ui, server)
Due to the choices you have available in your UI, the toggleState observer will only ever toggle the disabled state once, after that it should always be valid. Because of that, there are ways to possibly reduce it. For instance, once could add once=TRUE as in the first observeEvent I added, in which case once the observer fires once, it is removed and never fires again. In your real-world use, if you believe that data() might toggle back to an unusable state (e.g., 0 rows), then you would likely want to keep the default of once=FALSE and add more logic on nrow(data()) to your data validation.
Typically in a web interface if you have a dropdown populated from a database that display's some text and you want to use that selected text in the dropdown and pass it back to a database. But a lot of times you want to pass an ID instead of the actual text displayed.
In my example below I have a global.R file that returns the data for the dropdowns. This simulates data returned from a database. For each dropdown there is a text field that is displayed in the dropdowns and an "id" field that is not displayed BUT I have to somehow access the "id" fields of the dropdowns. How is this done in Shiny?... Because the selectInputs don't allow you to store the ids so you can access them like input$DisplayName$id
In the example below I just want to print the "id" of the "DisplayName" selectInput so if "Mary" is in the input$DisplayName then "20" should be printed in the RenderText call.
Here is code to run:
require(shiny)
runApp(list(
ui = basicPage(
sidebarPanel(
selectInput("Department", "Select a department", choices = as.character(GetDepartments()$Department), selected = as.character(GetDepartments()$Department[1])),
uiOutput("DisplayName")
),
mainPanel(textOutput("Text") )
),
server = function(input, output, session) {
output$DisplayName<-renderUI({
Department <- input$Department
print(Department)
selectInput("DisplayName", 'DisplayName:', choices = as.character(GetDisplayName(Department)$DisplayName), selected =as.character(GetDisplayName(Department)$DisplayName[1] ))
})
output$Text <- renderText({
# Here I want to simulate accessing the "id" field of the input$DisplayName
#in my app I need to pass the id to a database query
#If Mary is in input$DisplayName how can I access her id of "20"?
print("in render text")
return( ??? How do I access the id = 20???)
})
}
))
Here is the global.r file that simulates code that returns stuff from a database
GetDepartments<- function(){
df<- data.frame(Department= c("Dept A", "Dept B"), id = c(1,2))
return(df)
}
GetDisplayName<- function(Dept){
if(Dept == "Dept A")
{
df<- data.frame(DisplayName= c("Bob", "Fred"), id = c(4,6))
return(df)
}else
{
df<- data.frame(DisplayName= c("George", "Mary"), id = c(10,20))
return(df)
}
}
This is very similar to your other question here. As #nrussel suggests, this is just a simple subsetting problem. Just pull up your department and index on the name. Here is a working example.
EDIT*** - make dataset reactive to avoid repetition.
As per the documentation:
Reactive expressions are a smarter than regular R functions. They cache results and only update when they become obsolete. The first time that you run a reactive expression, the expression will save its result in your computer’s memory. The next time you call the reactive expression, it can return this saved result without doing any computation (which will make your app faster). The reactive expression will use this new copy until it too becomes out of date.
runApp(list(
ui = basicPage(
sidebarPanel(
selectInput("Department", "Select a department",
choices = as.character(GetDepartments()$Department),
selected = as.character(GetDepartments()$Department[1])),
uiOutput("DisplayName")
),
mainPanel(textOutput("Text") )
),
server = function(input, output, session) {
myData <- reactive({
GetDisplayName(input$Department)
})
output$DisplayName<-renderUI({
Department <- input$Department
print(Department)
myData <- myData()
selectInput("DisplayName", 'DisplayName:', choices = as.character(myData$DisplayName),
selected =as.character(myData$DisplayName[1] ))
})
output$Text <- renderText({
print("in render text")
myData <- myData()
code <- as.character(myData[myData$DisplayName == input$DisplayName,2])
return(code)
})
}
))
Originally I create this shiny interface that takes in a parameter "company id" and "date", but here we have a problem: most people dont know the companies we work with by their id, only their name, i.e. (McDonalds, Radioshack).
So I want to ideally create a search function like this
My current idea is to pass in a table including a list of all our partner companies and their ids to global.R. Then pass in the textInput as the search variables and perform the search on server side. However, I get lost on how to pass searchResults back into the UI on a selectInput panel?
My current code:
ui.R
library(shiny)
shinyUI(pageWithSidebar(
sidebarPanel(
textInput("nameSearch", "Or, Search for company name", 'McDonald'),
selectInput("partnerName", "Select your choice", list( "searchResults" ),
br(),
submitButton("Update View"),
br(),
),
server.R
shinyServer(function(input, output) {
#subTable
searchResult<- reactive({
subset(partners, grepl(input$nameSearch, partners$name))
})
output$searchResults <- renderTable({
searchResult[,1]
})
global.R
partners<- read.csv("partnersList.csv", fill=TRUE)
partnersList is just in this format
name id
------------------
McDonalds 1
Wendy's 2
Bestbuy 3
You need to make the UI reactive. I haven't tested this (miss data for it too) but should work I think. In server.R add:
output$selectUI <- renderUI({
selectInput("partnerName", "Select your choice", searchResult()[,1] ),
})
And in ui.R replace the selectInput with:
htmlOutput("selectUI")
In Shiny version 0.8 (where I have tested it), in server.R add the following:
shinyServer(function(input, output, session) {
observe({
# This will change the value of input$partnerName to searchResult()[,1]
updateTextInput(session, "partnerName",
label = "Select your choice",
value = searchResult()[,1])
})
})
Now the function within shinyServer has additional argument session.
You can skip the label if you don't need to change it.
You don't need to change anything in ui.R.
Reply from Rstudio's JC:
Sure, just use a textInput for the search string, and use
renderUI/uiOutput to make a dynamic selectInput. Then the rest of
your code can depend on the selectInput's value. (Make sure to check
for NULL or whatever when reading the selectInput value, because it
will start out with no value.)
If I understand the question correcly, all you need to do is display to the user company names and have company ids passed to the server.
I suppose this feature was not available back in the days (and you had to work around as suggested above) but nowadays the argument choices in selectInput function accepts named lists exactly for this need. Hopefully example below clarifies the situation.
library(shiny)
library(tidyverse)
companies <- tribble(
~name, ~id,
"McDonalds", 1,
"Wendy's", 2,
"Bestbuy", 3
)
ui <- fluidPage(
uiOutput("select_company_ui"),
textOutput("value")
)
server <- function(input, output) {
output$select_company_ui <- renderUI({
selectInput("select_company", "Select company", choices = deframe(companies))
})
output$value <- renderText(paste0("Value of the company filter is ", input$select_company))
}
# Run the application
shinyApp(ui = ui, server = server)
Here you can see the resulting app:
Side note: I use function deframe from package tibble to turn a tibble into a named list just for convenience, you could do without it by writing
choices = c("McDonalds" = 1, "Wendy's" = 2, "Bestbuy" = 3)