How can we ensure the user sees the validation error message only once?
Even in Shiny validation page, they had displayed error message twice:
https://shiny.rstudio.com/articles/validation.html
Also, if I were using different language, the below link might have helped.
Knockout - validation showing same error message twice
Even though **Stackoverflow had similar question here but referring different issue **
Show validate error message only once
It meant something different.
I am referring to "Please select a data set" message displaying twice
library(shiny)
ui <- fluidPage(
titlePanel("Validation App"),
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),
# Show a plot of the generated distribution
mainPanel(
tableOutput("table"),
plotOutput("plot")
)
)
)
server <- function(input, output) {
data <- reactive({
validate(
need(input$data != "", "Please select a data set")
)
get(input$data, 'package:datasets')
})
output$plot <- renderPlot({
hist(data()[, 1], col = 'forestgreen', border = 'white')
})
output$table <- renderTable({
head(data())
})
}
shinyApp(ui,server)
If there is an error, ideally only 1 time, user should be notified if not it might be annoying.
Because you are storing the message "Please select a data set" in the reactive object data() and then calling that object to be displayed twice, once in output$plot and once in output$table.
One way to refactor the app and still have a similar experience is the use a place holder in the input widget and then req() to check if the input value is truthy. If a value is not truthy ("falsey"?) then evaluation is stopped and you won't raise errors from downstream outputs that use data().
library(shiny)
ui <- fluidPage(
titlePanel("Validation App"),
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("Please select a dataset" = "", "mtcars", "faithful", "iris"))
),
# Show a plot of the generated distribution
mainPanel(
tableOutput("table"),
plotOutput("plot")
)
)
)
server <- function(input, output) {
data <- reactive({
req(input$data)
get(input$data, 'package:datasets')
})
output$plot <- renderPlot({
hist(data()[, 1], col = 'forestgreen', border = 'white')
})
output$table <- renderTable({
head(data())
})
}
shinyApp(ui,server)
Another option would be to relocate the validate() logic from the data() block to one of the outputs. That way the message will only be shown once, but you may have to implement another check on the data, which is why I prefer using req for things like this.
Related
First of all, I am so sorry if the main question (the title) of the post is not clearly enough. I didn't how to write a question with my problem.
Well, the thing is that I have two select inputs. The main one: Dataset, which have 2 options: 1) Cars and 2) Iris.
The other select input, it has information from the Cars' dataset and information from the Iris one.
I need to show the information from Cars if I select Cars and the information from Iris if I select Iris.
Now, my code is not able to do that. Simply it shows you the options to choose the datasets but in the second select input only shows the information from Cars.
I don't know how to do it, I have been a lot of posts but I couldn't get what I want.
For example this post Filter one selectInput based on selection from another selectInput? was very similar and I thought that I could do something similar, but he doesn't use a dataset from R...
My code:
library(shiny)
ui <- fluidPage(
titlePanel("Select a dataset"),
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Dataset",
choices = c("Cars" = "Cars", "Iris" = "Iris")),
uiOutput("select_cars"),
uiOutput("select_iris")
),
mainPanel(
verbatimTextOutput("text"),
verbatimTextOutput("text2")
)
)
)
server <- function(input, output) {
cars <- reactive({
data("mtcars")
cars <- rownames(mtcars)
return(cars)
})
iris <- reactive({
data("iris")
iris <- data.frame(unique(iris$Species))
colnames(iris) <- "iris"
return(iris)
})
output$select_cars <- renderUI({
selectInput(inputId = "options_cars", "Select one", choices = cars())
})
output$select_iris <- renderUI({
selectInput(inputId = "options_iris", "Select one iris", choices = iris())
})
output$text <- renderPrint(input$options_cars)
output$text2 <- renderPrint(input$options_iris)
}
#Run the app
shinyApp(ui = ui, server = server)
On the other hand I get an error: object of type ‘closure’ is not subsettable. But I don't know why.
Finally, I apologize if someone has already asked something similar before, I really have been looking all morning and I do not know how to solve it. (I am quite new at Shiny and I am trying to do my best).
Thanks very much in advance,
Regards
I have modified some of your code and added some JS functionality from shinyjs, which you may or may not find useful
You don't really need to create objects all the time if you only going to update the list, so we are going to use updateSelectInput to update the sliders
I used hidden functionality to hide the elements initially so they are invisible to begin with
I created dependency on input$dataset within observeEvent so we can update the sliders and hide and show both the sliders we dont want and the output we dont want
Also if your datasets are static, like mtcars and iris its best to take them outside the server.R so you dont do extra unnecessary work
Finally its always a good idea to add req so you're not creating any objects if they are NULL
Your original error was due to the fact that you were passing the dataframe and not the list or vector to the slider, try to print out the objects if you're unsure and see their types
library(shiny)
library(shinyjs)
ui <- fluidPage(
titlePanel("Select a dataset"),
useShinyjs(),
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Dataset",
choices = c("Cars" = "Cars", "Iris" = "Iris")),
hidden(selectInput(inputId = "options_cars", "Select one", choices = NULL)),
hidden(selectInput(inputId = "options_iris", "Select one iris", choices = NULL))
),
mainPanel(
verbatimTextOutput("text_cars"),
verbatimTextOutput("text_iris")
)
)
)
cars_data <- unique(rownames(mtcars))
iris_data <- as.character(unique(iris$Species))
server <- function(input, output, session) {
observeEvent(input$dataset,{
if(input$dataset == "Cars"){
show('options_cars')
hide('options_iris')
show('text_cars')
hide('text_iris')
updateSelectInput(session,"options_cars", "Select one", choices = cars_data)
}else{
show('options_iris')
hide('options_cars')
show('text_iris')
hide('text_cars')
updateSelectInput(session,"options_iris", "Select one iris", choices = iris_data)
}
})
output$text_cars <- renderPrint({
req(input$options_cars)
input$options_cars
})
output$text_iris <- renderPrint({
req(input$options_iris)
input$options_iris
})
}
#Run the app
shinyApp(ui = ui, server = server)
Here is a code that allows the switch by selectInput
library(shiny)
library(datasets)
ui <- fluidPage(
titlePanel("Select a dataset"),
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Dataset",
choices = c("Cars" = "Cars", "Iris" = "Iris")),
##------removed this---------------
# uiOutput("select_cars"),
#uiOutput("select_iris")
##------------------------------
uiOutput("select_by_input")
),
mainPanel(
verbatimTextOutput("text")
# verbatimTextOutput("text2")
)
)
)
server <- function(input, output) {
cars <- reactive({
data("mtcars")
cars <- rownames(mtcars)
return(cars)
})
iris <- reactive({
# data("iris")
# iris <- data.frame(unique(iris$Species))
data('iris')
#colnames(iris) <- "iris"
# iris_names <- as.character(unique(iris$Species) )
iris_names <- c('a','b','c')
return(iris_names)
})
##------removed this---------------
# output$select_cars <- renderUI({
# selectInput(inputId = "options_cars", "Select one", choices = cars())
# })
#
# output$select_iris <- renderUI({
# selectInput(inputId = "options_iris", "Select one iris", choices = iris())
# })
#-----------------------------
output$select_by_input <- renderUI({
if (input$dataset=='Cars'){
selectInput(inputId = "options_x", "Select one", choices = cars())
}else if (input$dataset=='Iris'){
selectInput(inputId = "options_x", "Select one iris", choices = iris())
}
})
output$text <- renderPrint(input$options_x)
}
#Run the app
shinyApp(ui = ui, server = server)
the object of type ‘closure’ is not subsettable. error is caused by the iris data not being loaded after running the app. I used iris_names <- c('a','b','c') to demonstrate the dynamic change by selectInput
I'm trying to get the label output in shiny using textOutput function with var_label from the labelled package. I tried a couple of things but I'm not able to view the label in the output. The error I'm getting is Error in var_label.data.frame: object 'var1' not found.
Code:
library(shiny)
library(labelled)
library(haven)
dat <- read_spss("http://staff.bath.ac.uk/pssiw/stats2/SAQ.sav")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("var1", "Frequency Table", choices = names(dat), selected = NULL)
),
mainPanel(
verbatimTextOutput("name"),
textOutput("label")
)
)
)
server <- function(input, output) {
output$name <- renderPrint({
input$var1
})
output$label <- renderText({
var_label(input$var1)
})
}
shinyApp(ui = ui, server = server)
The issue is that input$var1 is the name of the variable. It does not contain any information about the variable in your dataset. To get the label associated with the variable called input$var1 use var_label(dat[[input$var1]]).
I am attempting to custom build a user-interface regression tool in R shiny for practice (i.e. my own version of spss for my general use-cases). I'm having trouble with the critical step of generating a regression formula from the user-uploaded dataset. I want the user to be able to select a dependent variable from a dropdown menu (and eventually to turn those generated variables into a formula in my server code).
I have tried to use a textOutput(names(userdata())) within the choices argument for a selectInput() function so that the user can select which of their variables should be a dependent variable once they have uploaded their dataset. However, this generates a list of properties of the dataset rather than the names of the columns themselves.
I've looked into other uses of reactive datasets that others have done but no one seems to have done precisely what I am trying to do or I am searching for them badly. (It seems like the most common probable use-case for Shiny, so I can't imagine how no one has figured this out yet, but I can't find anything)
library(shiny)
library(wired)
ui <- fluidPage(
sidebarLayout(sidebarPanel(
fileInput("FileInput", "Input Your Data Set (Must be .csv)"),
wired_select(inputId = "responsevar",
label = "Dependent Varibale:",
choices = textOutput(outputId = "variable_names")
)
), #sidebar panel
mainPanel(
tabsetPanel(
tabPanel("Table",
DT::dataTableOutput("table")
)
) #tabset Panel
) #main panel
) #sidebarlayout
) #fluidpage
server <- function(input, output, session) {
datasetInput <- reactive({
infile <- input$FileInput
if (is.null(infile))
return(NULL)
read.csv(infile$datapath, header = TRUE)
})
output$table = DT::renderDataTable(datasetInput())
output$variable_names <- reactive({
if (is.null(datasetInput()))
return(NULL)
names(datasetInput())
})
} #server
shinyApp(ui = ui, server = server)
textOutput is used to output text to the Shiny UI. This is includes generating appropriate HTML. As wired_select(..., choices = ???) is expecting an R object rather than HTML code this is unlikely to work.
One approach that is likely to work is using updateSelectInput. I do not know whether this has an equivalent function with the wired library, but in base shiny I would:
Initialise the selectInput with no choices
Update the choices in the drop down once the data has been selected
Try the following:
library(shiny)
ui <- fluidPage(
sidebarLayout(sidebarPanel(
fileInput("FileInput", "Input Your Data Set (Must be .csv)"),
selectInput(inputId = "responsevar",
label = "Dependent Varibale:",
choices = NULL)
), #sidebar panel
mainPanel(
tabsetPanel(
tabPanel("Table",
DT::dataTableOutput("table")
)
) #tabset Panel
) #main panel
) #sidebarlayout
) #fluidpage
server <- function(input, output, session) {
datasetInput <- reactive({
infile <- input$FileInput
if (is.null(infile))
return(NULL)
read.csv(infile$datapath, header = TRUE)
})
output$table = DT::renderDataTable(datasetInput())
observeEvent(datasetInput(),{
updateSelectInput(session, "responsevar", choices = names(datasetInput()))
})
} #server
shinyApp(ui = ui, server = server)
Hmm... as the above does not work with the wired library, I'll suggest another possible approach. (I can't install wired in my environment, so apologies if this is no better).
The idea here is to make the selector part of a dynamic R object (a UI object). Then if a file gets loaded, the UI object, which depends on the file, will also update.
library(shiny)
library(wired)
ui <- fluidPage(
sidebarLayout(sidebarPanel(
fileInput("FileInput", "Input Your Data Set (Must be .csv)"),
uiOutput("selector")
), #sidebar panel
mainPanel(
tabsetPanel(
tabPanel("Table",
DT::dataTableOutput("table")
)
) #tabset Panel
) #main panel
) #sidebarlayout
) #fluidpage
server <- function(input, output, session) {
datasetInput <- reactive({
infile <- input$FileInput
if (is.null(infile))
return(NULL)
read.csv(infile$datapath, header = TRUE)
})
output$table = DT::renderDataTable(datasetInput())
output$selector <- renderUI({
choices <- NULL
if(!is.null(datasetInput()))
choices <- names(datasetInput())
wired_select(inputId = "responsevar",
label = "Dependent Varibale:",
choices = choices)
})
} #server
Key differences to my original answer, are uiOutput replacing selectInput and a renderUI output component instead of an observer.
I am trying to write a script in shiny, which has two inputs and stores the inputs in two different variables and runs a code using these input variables.But i am getting an error which says :Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
The following is my ui code:
ui <- fluidPage(
titlePanel("Network Model"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "origin",
label = "Origin:",
choices = milk_runs$Origin),
selectInput(inputId = "destination",
label = "Destination:",
choices = milk_runs$Dest),
actionButton("go", "")
),
mainPanel(
tableOutput(
"view"))
)
)
server code :
server<- function(input, output){
origin <- input$origin
destination <- input$destination
observeEvent(input$go,source("nr9.R"))
output$summary <- renderPrint({
#dataset <- datasetInput()
summary(Tnetwork)
})
Can you please tell me how to get correct results.
I think (it would help if you provided a fully reproducible example) that the error is occurring because you are trying to run input$origin without reactive(). The input$origin will not invalidate and update based on user input unless put inside reactive. Based on the example you provided:
library(shiny)
ui <- fluidPage(
titlePanel("Network Model"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "origin", label = "Origin:", choices = c("A","B","C","D","E","F")),
selectInput(inputId = "destination", label = "Destination:", choices = c("A","B","C","D","E","F")),
actionButton("go", "GO")
),
mainPanel( tableOutput( "view"))
)
)
server<- function(input, output){
origin <- reactive(input$origin)
destination<-reactive(input$destination)
observeEvent(input$go,{
cat(origin(),'nextword',destination(),sep="-")
})
output$view <- renderTable({data.frame(origin=origin(),destination=destination())})
}
shinyApp(ui, server)
should print 'origin-nextword-destination' to the console when 'go' is activated, and the table should update. I changed a few bits in your example because it was not reproducible but hopefully it helps.
I am trying to gather user input given a data set. I want to insert a column where the user can determine whether they would want to own one of the cars in the mtdata set. This is completely subjective as opinions differ from person to person so I am not able to program this in. Is there a way to append an extra column that can be a checkbox or dropdown menu to identify cars that a user would "Want to own?
library(shiny)
shinyApp(ui = shinyUI(fluidPage(
titlePanel("Interesting Cars"),
sidebarLayout(
sidebarPanel(
helpText("This is a side bar")),
mainPanel(
tableOutput("view")
)
)
)),
server = function(input, output) {
output$view <- renderTable({
head(mtcars[, 1:4], n = 6)
})
})
How about this, you can use the DT library. By adding the filter option the user can define the different components one wants and see what cars come up.
library(shiny)
library(DT)
shinyApp(ui = shinyUI(fluidPage(
titlePanel("Interesting Cars"),
sidebarLayout(
sidebarPanel(
helpText("This is a side bar")),
mainPanel(
DT::dataTableOutput("view")
)
)
)),
server = function(input, output) {
output$view <- DT::renderDataTable({
datatable(mtcars,
filter = "top"
)
})
})
Edit
If it truly is so important to add another column indicating if it is 'interesting' there will be significantly more code to written if you intend to have users assign it on different conditions. Here is an example with just the mpg. The fundamental idea here is that you assign your data to the reactiveValues function. It can then be modified as you like. This can obviously be improved upon more (as it will continue to add columns) but it demonstrates the concept.
shinyApp(ui = shinyUI(fluidPage(
titlePanel("Interesting Cars"),
sidebarLayout(
sidebarPanel(
helpText("This is a side bar"),
uiOutput("mpg"),
actionButton("add_label", "Mark Interesting")
),
mainPanel(
DT::dataTableOutput("view")
)
)
)),
server = function(input, output) {
values <- reactiveValues(
mydata = mtcars
)
output$mpg <- renderUI({
numericInput("mpg_input", "MPG Cutoff?",
value = 15
)
})
output$view <- DT::renderDataTable({
datatable(values$mydata
)
})
observeEvent(input$add_label, {
validate(
need(!is.null(input$mpg_input), "need mpg value")
)
values$mydata <- data.frame(values$mydata,
Interesting_Flag =
ifelse(values$mydata$mpg > input$mpg_input,
"Interesting",
"Not Interesting"))
})
})