I am writing a web application in shiny and I am not to sure I am writing efficient code for conditional panels. What I want to happen is, if the user clicks "Yes" on the radioButton, then many more inputs will populate. I originally used "conditionalPanel" in my Ui followed buy 5 uiOutputs. However, I am noticing that the renderUi runs at the start of the app, even when the button does not say "Yes". I have changed the code so I do not need a conditional panel BUT I am seeing the same thing occur. Is this efficient? I will have multiple uiOutputs, here is just one for example.
UI.R
radioButtons("add_indicator1", "Add long indicators", choices = c("No", "Yes")),
uiOutput("long1"),
SERVER.R
button1 = reactive({
input$add_indicator1
})
output$long1 = renderUI({
if(button1() == "No"){NULL
}else{selectInput ("indicator1", "Select the first long indicator",
choices = c("Choices" = "", long_indicators))}
})
Related
I'm new to Shiny - a lot I don't fully understand yet. I'm working on a Shiny app to display some data. I want to allow for optional filtering of the data before being plotted. In order to construct my criteria, the UI will supply some selectInputs. My plan is that these are initially disabled. I could add, to the UI, buttons to activate each selectInput independently. But I wanted to try and use a checkboxGroupInput for this task. To begin developing this, I tried working on just one of the checkboxGroupInput values enabling/disabling just one of the selectInputs. It works perfectly except for one specific case. If I select the targeted checkboxGroupInput, the targeted selectInput gets enabled. But if I deselect that targeted checkboxGroupInput, the targeted selectInput does not get disabled. However, this behavior only occurs if no other checkboxGroupInput selection is currently selected. If any, or multiple, other checkboxGroupInputs are selected, the target will enable and disable in exactly the way I want and expect based on my (limited) understanding of the code.
Below is (hopefully) a clear and simple piece of code to demonstrate this behavior. I have a checkboxGroupInput with four items, and 4 selectInputs. Checking the third checkboxGroupInput item is supposed to enable the third selectInput. Unchecking (deselecting) the third checkboxGroupInput item is supposed to disable the third selectInput. Again, it behaves in precisely this way - if at least one other checkboxGroupInput is selected. The third selectInput will always be enabled by selecting the third checkboxGroupInput, but deselecting the third checkboxGroupInput item will not disable the third selectInput unless at least one other item is currently selected in the checkboxGroupInput.
I added output of the currently selected checkboxGroupInput contents to try and understand what was happening.
Last thing before the code -- I also first constructed the checkboxGroupInput using choices instead of choiceNames and choiceValues; didn't seem to matter. Also, my first try with the conditional test in the 'if' block was to use is.element instead of %in%; again, no difference in behavior.
library(shiny)
library(shinyjs)
# Let's make lists for the drop boxes
list_1 <- list("a", "b", "c")
list_2 <- list("d", "e", "f")
list_3 <- list("g", "h", "i")
list_4 <- list("j", "k", "l")
# Define UI for application
ui <- fluidPage(
useShinyjs(), # Set up shinyjs
# Application title
titlePanel("What's wrong with this??"),
# Sidebar
sidebarLayout(
sidebarPanel(
checkboxGroupInput("enabled", "Search Filters to Enable:",
choiceNames = list("List_1", "List_2", "List_3", "List_4"),
choiceValues = list("List_1_chosen", "List_2_chosen", "List_3_chosen", "List_4_chosen")),
# Input: Select from the following lists - default behavior is they are all disabled to start
disabled(selectInput("List_1_choice", "Choose from List 1:",
choices = list_1)),
disabled(selectInput("List_2_choice", "Choose from List 2:",
choices = list_2)),
disabled(selectInput("List_3_choice", "Choose from List 3:",
choices = list_3)),
disabled(selectInput("List_4_choice", "Choose from List 4:",
choices = list_4)),
verbatimTextOutput("text_choice")),
# Show a plot
mainPanel(
# empty
)
)
)
# Define server logic
server <- function(input, output) {
# This output is so I can see what's selected in the checkboxGroupInput
output$text_choice <- renderPrint({
return(paste0(input$enabled))})
observeEvent(input$enabled, {
# Here's the problem (I think) -- this 'if' block is not working the way I expect
if("List_3_chosen" %in% input$enabled){
enable("List_3_choice")
}else{
disable("List_3_choice")
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
I have lot's of workarounds to complete this project, but I would very much like to understand what I'm doing wrong here.
Help! And thanks for your time and attention.
When nothing is selected, checkboxGroupInput returns NULL. You need to explicitly tell the observeEvent to not ignore it:
observeEvent(input$enabled, {
# Here's the problem (I think) -- this 'if' block is not working the way I expect
if("List_3_chosen" %in% input$enabled){
enable("List_3_choice")
}else{
disable("List_3_choice")
}
}, ignoreNULL = FALSE)
I'm looking to make some picker inputs in Shiny for each of the 50 states, but I'd like to separate them into three different groups such that no group has the same state. I was just wondering if there was a way to ensure that the three picker inputs didn't both select the same state or if there was perhaps a better way of doing this in R that I was not aware of. Thank you!
It takes a bit of work to set up, but you can accomplish that by updating the
available choices for other inputs when one changes. If you only have two or
three inputs that should be linked like this, it may be tempting to just
write out the observers and be done with it. But really, this is a
generalizable pattern, so I think it makes sense to use a helper function
instead. That way, you can link however many inputs you need, and also re-use
the logic in different apps.
All that the helper function needs to know is the IDs of the participating
inputs, and the set of shared choices. It’s not strictly necessary here, but
also making the choices reactive lets them dynamically change.
selectPool <- function(inputIds, choices = reactive(NULL)) {
stopifnot(is.reactive(choices))
session <- getDefaultReactiveDomain()
input <- session$input
# Keep track of all selected values in the pool
alreadySelected <- reactive({
Reduce(union, lapply(inputIds, \(id) input[[id]]))
})
# ... and based on that, what's left to select from.
remainingChoices <- reactive({
setdiff(choices(), alreadySelected())
})
# When an input changes, update remaining choices for others
lapply(inputIds, \(id) {
observe({
lapply(setdiff(inputIds, id), \(otherId) {
otherSelected <- input[[otherId]]
updateSelectInput(
session = session,
inputId = otherId,
# Anything already selected must remain a choice
choices = c(remainingChoices(), otherSelected),
selected = otherSelected
)
})
}) |> bindEvent(input[[id]], ignoreNULL = FALSE)
})
}
Once we’ve taken the time to do that, it’s very straightforward to use in an app:
library(shiny)
ui <- fluidPage(
titlePanel("Star Wars Alliance Builder"),
selectInput("alliance1", "Alliance 1", NULL, multiple = TRUE),
selectInput("alliance2", "Alliance 2", NULL, multiple = TRUE),
selectInput("alliance3", "Alliance 3", NULL, multiple = TRUE),
)
server <- function(input, output, session) {
selectPool(
inputIds = c("alliance1", "alliance2", "alliance3"),
choices = reactive(unique(dplyr::starwars$species))
)
}
shinyApp(ui, server)
By "picker inputs" I assume you mean selectInput/selectizeInput.
There are multiple ways you could do this. One way would be to use updateSelectInput() to update the reminding inputs after the first/second has been selected. The possible states to choose from would then be all states except the one(s) already selected. This would make it impossible to choose the same state in multiple inputs from the UI.
However, this might be a bit involved for your need. In that case I suggest that you:
either replace your three inputs with one selectInput(..., multiple = TRUE), and use validate() to check that the user has selected exactly three states
or simply just use validate() to throw an error to the user if they have selected the same state more than once in any of the three inputs.
I am stuck in a small problem related to shiny/R.
I am reading in a text file and displaying selective column names returned by grep search into the shiny application on the fly. For this, I am using the dynamicUI.
After the file is read in, the following function runs in server.R. It checks for specific colnames and displays this on the UI using uiOutput. Whatever column names are selected by the user, they are sent to another function for data processing and the plot it returned on the mainPanel.
server.R
output$Bait <- renderUI({
data <- input.data();
if(is.null(data)) return()
colnames <- names(data)
colnames = colnames[grep("*LFQ*",colnames,ignore.case=TRUE)]
# Creating the checkboxes using the above colnames
checkboxGroupInput("bait", "Choose Bait LFQ columns",
choices = colnames,
selected = colnames)
})
ui.R
shinyUI(
sidebarPanel(
uiOutput("Bait"),
),
mainPanel(
plotOutput(outputId = 'plot'),
)
)
Everything is fine, what I am trying to create is an action button for the checkboxes. Some files are big and have a longer list of column names >60, so whenever a checkbox is clicked, the whole function runs for processing and displays a plot. This gets unnecessary when the user has to deselect/select more than 10 columns.
An easy fix is, I kept selected=NULL but what I want is to add an actionButton after the checkboxGroupInput, so that user can select as many as checkBoxes but the function only runs when the GO button is pressed via the actionButton. If add a actionButton control after the checkbocGroupInput, it doesnt' works.
Can someone guide me in this regard. After working on this for sometime, now I am bit lost.
Thanks
Did you look into ?isolate? Lets say i want function initialFunction() only be evaluated if input$actionButtonis clicked.
observe({
input$actionButton # everything that triggers initialFunction() should come before isolate()
isolate({
# everything that should not trigger initialFunction() should come inside isolate()
initialFunction()
})
})
My Shiny app uses open data from a bird atlas, including lat/lon coordinates by species. The bird species names come in different languages, plus as an acronym.
The idea is that the user first selects the language (or acronym). Based on the selection, Shiny renders a selectizeInput list of unique bird species names. Then, when one species is selected, a leaflet map is generated.
I've done a couple of Shiny apps but this time I miss something obvious. When the app starts, all is well. But, the selectizeInput list is not re-rendered when a new language is selected.
All the present code with some sample data is here as a GitHub Gist https://gist.github.com/tts/924b764e7607db5d0a57
If somebody could point to my problem, I'd be grateful.
The problem is that both the renderUI and the birds reactive blocks are dependent on the input$lan input.
If you add print(input$birds) in the birds block, you will see that it uses the name of the birds before renderUI has a chance to update them to fit the new language. The data you then pass the leaflet plot is empty.
Try adding isolate around input$lan in the birds expressions so that it is only dependent on input$birds:
birds <- reactive({
if( is.null(input$birds) )
return()
data[data[[isolate(input$lan)]] == input$birds, c("lon", "lat", "color")]
})
When you change the language, the renderUI will change the selectize, which will trigger the input$birds and update the data.
Instead of using renderUI, you could also create the selectizeInput in your ui.R using (replacing the uiOutput):
selectizeInput(
inputId = "birds",
label = "Select species",
multiple = F,
choices = unique(data[["englanti"]])
)
And in your server.R, update it using:
observe({
updateSelectizeInput(session, 'birds', choices = unique(data[[input$lan]]))
})
I am new to Shiny and trying to build a more accessible input and output for a function I built. I am giving this to people that don't run R so trying to build something that runs my functions in the background and then spits out the answer.
I am having some trouble getting everything in the way that I want it unfortunately and dealing with a bunch of errors. However, here is my more pointed question:
The actual function that I want to run takes a name (in quotations as "Last,First") and a number.
PredH("Last,First",650)
So I want a shiny application that takes a name input and a number input that then runs this program and then spits back out a data table with my answer. So a couple of questions.
How do I get it in the right form to input into my equation on the server side script, do I need to return it in the function so it can be accessed using a function$table type access? (Right now I am just printing using cat() function in the console for the function but know that may not be usable for this type of application.
I want to return a dataframe that can be gotten at PredH14$table. How do I go about building that shiny?
Here is my code so far:
UI:
library(shiny)
shinyUI(pageWithSidebar(
# Application title
headerPanel("Miles Per Gallon"),
# Sidebar with controls to select the variable to plot against mpg
# and to specify whether outliers should be included
sidebarPanel(
textInput("playername", "Player Name (Last,First):", "Patch,Trevor"),
radioButtons("type", "Type:",
list("Pitcher" = "P",
"Hitter" = "H"
)),
numericInput("PAIP", "PA/IP:", 550),
submitButton("Run Comparables")
),
mainPanel(
textOutput("name")
)
Server:
library(shiny)
shinyServer(function(input, output) {
sliderValues <- reactive({
data.frame(
Name = c("name", "PA"),
Value = c(as.character(playername),
PAIP),
stringsAsFactors=FALSE)
})
name=input[1,2]
PAIP=input[2,2]
testing <- function(name,PAIP){
a=paste(name,PAIP)
return(a) }
output$name=renderText(testing$a)
})
I am not quite sure I understood your question 100% but I clearly see you are wondering how to pass the input of the UI into the server and maybe, the other way back.
In your server code, clearly you are not getting any input from the UI. Basically you have created three input variables in your ui.R:
1. input$playername
2. input$type
3. input$PAIP
And one output:
1. output$name
Just let you know, the function sliderValues <- reactive(..) is called every time there is any input from the input... like people click the dropdown list or people modifies words in the text box.
You can even get started without the submit button just to get started. But the existence of the submit button actually makes everything easier. Create a submit button for an input form. Forms that include a submit button do not automatically update their outputs when inputs change, rather they wait until the user explicitly clicks the submit button.
So you can put your code in a way similar like this:
# server.R
library(shiny)
shinyServer(function(input, output) {
sliderValues <- reactive({
result <- ... input$playername ... input$type ... input$PAIP
return(result)
})
output$name <- renderPlot/renderText (... sliderValues...)
})
# ui.R
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("Miles Per Gallon"),
sidebarPanel(
textInput("playername" ... ),
radioButtons("type" ... ),
numericInput("PAIP" ... ),
submitButton("...")
),
mainPanel(
textOutput/plotOutput...("name")
)
))
In the end, check out the shiny example that might be what you want.
library(shiny)
runExample('07_widgets')