Shiny - conditional panel not recognizing input - r

I'm working with shiny and I'm having difficulty having R recognize input when using conditionalPanel. I have two such panels in my app because I need them to produce a certain list depending on which of the radio buttons a user selects.
Everything is rendered fine from the UI perspective, but for some reason R is unable to pick up any input when the 'Name' option is passed through the conditional panel, even if an item is entered in the field. The correct list is used, but for some reason it doesn't detect any input. Here's a screenshot of the selections and results.
I have no issues if I choose the other option from within the radio buttons and enter an item--it works as expected. Here's the code I'm working with.
ui <- fluidPage(
sidebarPanel(
# the user can choose from two options within the radio buttons
radioButtons("items_type", label = "Choose items for analysis"
, choices = list("Items - Item number & name" = "Both", "Items - Item name only" = "Name")
, selected = "Both")
# if the 'Both' option is selected, the items_list_names_id option is used
, conditionalPanel(
condition = "output.items_type == 'Both'"
, selectInput("items", label = NULL, choices = items_list_names_id, multiple = TRUE))
# if the 'Name' option is selected, the items_list_names option is used. input
# is not being detected here for some reason, and I'm wondering if it's because
# I use "items" for both selectInputs
, conditionalPanel(
condition = "output.items_type == 'Name'"
, selectInput("items", label = NULL, choices = items_list_names, multiple = TRUE))
# action button so the user can submit for analysis based on their selected options
, actionButton("go", "Run", style = "color: white; background-color: #2759aa")
)
)
server <- function(input, output){
# this portion is used to detect if the user alternates between either option from the radio buttons and uses the appropriate selectInput option
output$items_type <- reactive({
input$items_type
})
outputOptions(output, "items_type", suspendWhenHidden = FALSE)
Results <- eventReactive(input$go, {
# this portion is simply for testing for me to see how R recognizes the inputs
observe({print(input$items_type)})
observe({print(input$items)})
# checks to make sure the user selects at least 1 item. For some reason,
# this portion is triggered when at least 1 item is selected under the 'Name'
# condition from the conditional panel.
validate(
need(input$items > 0, 'Please select at least 1 item for analysis')
)
#other steps start here, but the above is the more pertinent part
}
EDIT: So it looks like having the same input id for both selectInput options is what's causing R to not recognize the input when it switches between the conditional panels. However, it would be ideal to have one input id because intput$item is used in other portions of my code not shown above. Rewriting the code so that it would use two variables, input$item1 and input$item2 for example, for each condition would be potentially very troublesome. I'm open to any suggestions to avoid this.
EDIT 2: I was thinking maybe using a single conditonalPanel and using a switch statement to alternate between the two lists depending on the user's selection. This should work, theoretically, and would be a convenient solution without modifying all of my other code. It looks like this:
, conditionalPanel(
condition = "output.items_list_selection"
, selectInput("items", label = 'Select items'
, choices = switch("output.items_list_selection", "Both" = items_list_names_id, "Name" = items_list_names)
, multiple = TRUE))
But a drop down menu doesn't appear as it's supposed to with this revision.

A possible solution is to use updateSelectInput , so there is no problem with two id having the samedi inputId
library(shiny)
items_list_names_id = c("id1", "id2")
items_list_names = c('name1', 'name2')
ui <- fluidPage(
sidebarPanel(
# the user can choose from two options within the radio buttons
radioButtons("items_type", label = "Choose items for analysis"
, choices = list("Items - Item number & name" = "Both", "Items - Item name only" = "Name")
, selected = "Both"),
# if the 'Both' option is selected, the items_list_names_id option is used
selectInput("items", label = NULL, choices = c('init'), multiple = TRUE),
# action button so the user can submit for analysis based on their selected options
actionButton("go", "Run", style = "color: white; background-color: #2759aa")
)
)
server <- function(input, output, session){
# change the choices depending on the value of input$intems_type
observeEvent(input$items_type, {
if(input$items_type == 'Both'){
updateSelectInput(session, 'items', label = NULL, choices = items_list_names)
}
if(input$items_type == 'Name'){
updateSelectInput(session, 'items', label = NULL, choices = items_list_names_id)
}
})
# check if it's working
observeEvent(input$go,{
print(input$items)
print(input$items_type)
})
}
shinyApp(ui, server)

Related

Different behavior of conditional panels in shinyApp when using do.call(fluidRow, ...)

This is a bit esoteric, but I am interested in the reason for this strange behavior.
I have a shiny app where I have a checkbox input for the user to select which menus they want to display. For each one they check, an additional "hidden" menu appears. I was interested in doing this programmatically with do.call to include an arbitrary number of menus. However, using do.call to execute fluidRow causes different behavior than calling fluidRow directly. Specifically, multiple rows are created instead of a single row, with some unwanted text between each row. Below is a reprex showing this behavior. Can anyone explain why this behavior is occurring and how to fix it?
Code
# Define function to show hidden menu.
hidden_menu <- function(i) {
column(width = 3,
conditionalPanel(
condition = paste0('input.display_menus.includes("', i, '")'),
checkboxGroupInput(paste0("categories_", i), "Select categories",
choiceNames = letters[1:2],
choiceValues = letters[1:2],
selected = letters[1:2]
)
)
)
}
# UI
ui <- fluidPage(
# Checkboxes of which menus to display
fluidRow(
checkboxGroupInput("display_menus", "Select menus to display",
choiceNames = c("one", "two", "three"),
choiceValues = 1:3,
selected = 1:3
)
),
# Menus displayed one way
fluidRow(
hidden_menu(1),
hidden_menu(2),
hidden_menu(3)
),
# Menus displayed another way; shouldn't this be identical to the one above?
do.call(fluidRow, sapply(1:3, hidden_menu))
)
library(shiny)
shinyApp(ui = ui, server = function(input, output) {})
Output
This shows the output with menus 1 and 3 selected. The first way of doing it, calling fluidRow directly, works as intended. The second way of doing it, using do.call, has a different orientation and some unwanted text output.

Action button and selectInput in R Shiny: Passing input of selectInput to a function after clicking an action button

I have a question regarding R Shiny.
I have defined a function in my global.R that takes 3 arguments and then displays a plot, so:
get.plot(mode, threshold, names = names)
In my Shiny App I have set up a sidebarPanel with a selectInput (multiple = FALSE) for mode, selectInput (multiple = TRUE) for names and a sliderInput for the threshold. In the mainPanel of my ShinyApp I dynamically render the plot using the inputs of the two selectInput objects and the sliderInput, which works perfectly fine.
The names list in my function contains 10 different names (all are shown in default setting) that can be combined in any way using the selectInput (multiple = FALSE). The problem is that my function is quite long and the calculations take long because it recalculates the function each time I delete a name in the selectInput. For example, if I want to display only 2 names, I have to remove 8 names of the selectInput, which means the function recalculates 8 times, which then takes very long.
I thought about showing an action button ("Apply Names") that can be clicked after the names have been selected. However, I still want the plot to adjust dynamically to the other arguments mode and threshold, and the button should only change the names argument.
Do you have any idea how I could solve that? Or do you have any other suggestions?
My setup looks approximately the following:
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "network_names",
label = "Select multiple names",
choices = names,
multiple = TRUE,
selected = names
),
selectInput(inputId = "network_mode",
label = "Select a mode",
choices = list("Mode 1",
"Mode 2")
),
sliderInput(inputId = "threshold",
label = "Threshold",
min = 1,
max = 10,
value = 3)
)
server <- function(input, output){
output$network <- renderPlot({
if(input$network_mode == "Mode 1") {
get.plot(1, input$threshold, names = input$network_names)
} else if(input$network_mode == "Mode 2") {
get.plot(2, input$threshold, names = input$network_names)
}
})
}
Any help is appreciated. Thank you very much.

Add n reactive radioButtons to shiny app depending on user input

I am trying to create a shiny app in which the user can select variables from a data frame in order to subset the data. The output will (eventually) be a data table with the users subset. I need to create n number of input boxes depending on the number of variables the user selects for the subset. Ideally, the input boxes will be dynamic radio buttons (for subsetting factors). I have not yet gotten to printing the data frame, but I have been able to create multiple radio buttons. I have used to following code with the mtcars dataset to achieve this:
server.R
library(shiny)
shinyServer( function(input, output, session) {
output$variables <- renderUI({
numVar <- length(as.integer(input$in0))
lapply(1:numVar, function(i) {
list(radioButtons("dynamic", input$in0,
choices = c("Choice one" = "one", "Choice two" = "two"), selected = "one"))
})
})
})
ui.R
library(shiny)
shinyUI(pageWithSidebar (
headerPanel("mtcars subset"),
sidebarPanel(
selectInput(inputId = 'in0', label = 'Choose variables',
choices = colnames(mtcars),
multiple = TRUE, selectize = TRUE),
uiOutput("variables")
),
mainPanel()
))
The code creates n number of radio buttons; however, for some reason it defaults to printing two sets of buttons. Also, the name of each new set of buttons is always the name of the first variable selected. Moreover, selecting choice one in one set changes the choice of the other sets. I would like each radio button set to have a name corresponding to the variable in the data frame and I would like the choice of each button to be independent of the other selected buttons.
Your problem (see warnings) is that you always use same name for each radioButtons, while you should index them with i:
lapply(1:numVar, function(i) {
list(radioButtons(paste0("dynamic",input$in0[i]), input$in0[i],
choices = c("Choice one" = "one", "Choice two" = "two"), selected = "one"))
})
Or a bit more elegant:
lapply(input$in0, function(x) {
list(radioButtons(paste0("dynamic",x), x,
choices = c("Choice one" = "one",
"Choice two" = "two"), selected = "one"))
})

shiny app for a taking input of multiple fields

I `m trying to build an app like this:-
Choose: # this is selectInput (So only one can be chosen at a time)
choice 1
choice 2
choice 3
once a choice is made then say choice 1 then the scree should display further sub option to that as,
In choice 1 select :
op1
op2
op3
op4
and these are multiple check box that is user can tick mark any number of checkboxs.
After this the relevant output is to be displayed.
and also I want the app to reflect the changes when the inputs are changed.
I have no clue to do this ,I`m trying this from few days but only managed to get the ui part done but nothing much on server.R code
hope this will help you.
UI.R file
library(shinyBS)
library(shiny)
shinyUI(fluidPage(
# input control for first choice
selectInput("first_choice",
label = h1("First Answer a General Question"),
choices = list("select","A","B","C"),
selected = "select"
),
#collapsable panel for second choice
h1("then get into details"),
bsCollapse(
bsCollapsePanel( title = "details",
uiOutput("second_choice")
),
id = "collapser", multiple = FALSE, open = NULL
),
h2("first answer"),
h3(textOutput("first_answer")),
h2("second answer"),
h3(textOutput("second_answer"))
))
server.R file
library(shiny)
shinyServer(function(input, output,session) {
#retrieve selected values and render text from selection
output$first_answer <- renderText({input$first_choice})
output$second_answer <- renderText({input$dynamic})
output$second_choice <- renderUI({
switch(input$first_choice,
"A" = checkboxGroupInput("dynamic", "Dynamic",
choices = c("Aragon","Frodo"),
selected = "option2"),
"B" = checkboxGroupInput("dynamic", "Dynamic",
choices = c("Bilbo","Gandalf","Sauron"),
selected = "option2"),
"C" = checkboxGroupInput("dynamic", "Dynamic",
choices = c("Boromir","Legolas"),
selected = "option2")
)
})
#observe function in order to open the collapsable panel when the first answer is given
observe({
if (input$first_choice != "select") {
updateCollapse(session,"collapser",open = c('details'))
}
})
})
this result in the following shiny app:
where the second menu opens only after the first get an answer and the options for the second question are dynamically changed based on the first answer.
selected answers are assigned to first_answer and second_answer.
Please note the use of shinyBS package for the collapsable panel.
You can find more on dynamic UI changes at the following Rstudio sources:
http://shiny.rstudio.com/articles/dynamic-ui.html
http://shiny.rstudio.com/gallery/dynamic-ui.html

Force no default selection in selectInput()

The Shiny documentation mentions that for selectInput():
selected The value (or, if none was supplied, the title) of the navigation item that should be selected by default. If NULL, the first navigation will be selected.
What if by default I don't want to select any value from select list?
Actually my select value is getting selected by default and rest part of app is getting executed automatically. But I don't want to select any value initially. What should I supply to the selected argument in selectInput() to do that?
Indeed, I don't want anything to be selected automatically. I used the code below but still it's selecting first available value from the list. I want there to be no selection by default, so the user can then select any option.
output$Choose_App <- renderUI({
selectInput("app",
"Select App:",
choices = as.character(mtrl_name),
selected = NULL ,
multiple = FALSE
)
})
Going through the documentation I noticed that the selection can be empty only if I select multiple=TRUE. Is this correct?
When I changed to multiple=TRUE, then it it's not getting selected by default, which is what I want. But unfortunately before making any selection I am also getting following error message:
ERROR: bad 'file' argument
Does anybody know about this if I am doing something wrong? But if I select this file then error is gone.
I am using following code for this:
# server.R
setwd("/opt/shiny-server/samples/sample-apps/P-Dict_RDS2")
mtrl_name <- try(system("ls | grep -i .rds", intern = TRUE))
shinyServer(function(input, output) {
# First UI input (Service column) filter clientData
output$Choose_Molecule <- renderUI({
selectInput("molecule",
"Select Molecule:",
choices = as.character(mtrl_name),
selected = input$molecule,
multiple = TRUE
)
})
You can use the selectize input instead of the select input, with some custom selectize options to set the initial selection to be empty. An example has been provided in the Shiny Gallery. In particular, see the example #6.
# make sure you have shiny >= 0.9.1
selectizeInput(
'e6', '6. Placeholder', choices = state.name,
options = list(
placeholder = 'Please select an option below',
onInitialize = I('function() { this.setValue(""); }')
)
)
BTW, for the error "ERROR: bad 'file' argument", I do not think anybody can help you without seeing the source code of your app, and it may be a separate question.
Faced a similar issue. The solution I found is based of #MKa's answer. You do not want to set multiple=T if your code can't handle multiple values. What I suggest is:
selectInput("molecule",
"Select Molecule:",
choices = c("",as.character(mtrl_name)),
selected = NULL,
multiple = F
)
And to retrieve the value selected:
if(!is.null(input$molecule))
{
if(nchar(input$molecule)>1)
{
#do your thing..
}
}
Fixed my problem. Let me know if you found a better solution.
I think you can get around it by adding an empty string to your choice list:
selectInput("app",
"Select App:",
choices = c("", as.character(mtrl_name)),
selected = NULL,
multiple = FALSE)
#Yihui Xie's answer requires selectInput(..., selectize = TRUE). If you want selectize = FALSE, you can still achieve a similar effect as follows.
This is not documented:
selected The initially selected value (or multiple values if multiple = TRUE). If not specified then defaults to the first value for single-select lists and no values for multiple select lists.
But for single-select lists if you can use selectize = FALSE, size = 4 (any non-NULL size would work), then you can set selected = FALSE to force no default selection.
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
mainPanel(
uiOutput("Choose_Molecule")
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
# First UI input (Service column) filter clientData
output$Choose_Molecule <- renderUI({
selectInput("molecule",
"Select Molecule:",
choices = rownames(mtcars),
selected = FALSE,
multiple = FALSE
, selectize = FALSE, size = 4 ##needed for `selected = FALSE` to work
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
A cleaner solution is to use selectizeInput, allow multiple selections, but then limit the maximum number of selections to 1. This means the default selection will be NULL, the user can only select 1 option, and you don't have to add an empty string to your choices or similar workarounds previously listed here.
selectizeInput(
"tag",
"Label",
choices = choices,
multiple = TRUE,
options = list(maxItems = 1)
)
A workaround would be to use updateSelectInput. The point here is that you set your selected = argument to length(your_vector_with_choices).
You can create a reactiveValues if you want your selectInput to be updated at the very beginning (in that case only observed at program start).
library(shiny)
shinyApp(
ui = fluidPage(
selectInput("state", "Choose a state:", NULL),
textOutput("result")
),
server = function(input, output, session) {
values <- reactiveValues(start = NULL)
observe({
choiceList <- list(`East Coast` = list("NY", "NJ", "CT"),
`West Coast` = list("WA", "OR", "CA"),
`Midwest` = list("MN", "WI", "IA"))
updateSelectInput(session, "state", choices = choiceList, selected = choiceList[length(choiceList)])
})
output$result <- renderText({
paste("You chose", input$state)
})
}
)
Answering after 6 years, as I got the same error now. We can write
selected = character(0)
to force not selecting any user input.
A modification of #rookieJoe answer is to use req() instead of the ifs. Read this page for an intro to the req() usage (in this specific question, the fact that req() takes the string "" as a "falsy" value).
For example, if the selectInput is like his:
selectInput("molecule",
"Select Molecule:",
choices = c("", as.character(mtrl_name)),
selected = NULL,
multiple = F
)
To retrive the value in a reactive as:
my_reactive <- reactive({
req(input$molecule)
# do your stuff
})

Resources