[RShiny][Shinydashboard]Show/Hide different input panel based on another input - r

I have built shiny App before, but I am getting rusty after not writing the code for a while.
Here is my goal:
I am designing a dynamic UI using shinydashboard. I have one selectInput (e.g., A, B, C and D).
What I want to make the app perform is that, based on the user option (either A, B, C or D), it will dynamically pop up another input panel (a panel with specific collection of "numericInput"s or "selectInput"s), as each A/B/C/D option defines different situation.
I could not provide my own code (as I do not have any idea on even how to get started), but I find some page with example code here:
An example of dynamic UI
It is the example in section 10.2.1 of the page using tabsetPanel and updateTabsetPanel.
I checked their app link and it seems to be exactly what I want, but I suspect that some of the code has been depleted by Shiny already as when I copy and paste the code to my R script, I could not run it without error.
If you could kindly help, either on how to properly adjust the code on that page, or if there is any other approach, it will be greatly appreciated.

Easiest way would be using uiOutput and renderUI.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("controller", "Show", choices = c("A", "B", "C", "D")),
uiOutput("newSelect")
),
mainPanel(
textOutput("textId")
)
)
)
server <- function(input, output, session) {
output$textId <- renderText({
input$controller
})
output$newSelect <- renderUI({
if(input$controller == "B"){
selectInput("controller2", "New Select", choices = c(1:4))
} else {
NULL
}
})
}
shinyApp(ui,server)
Bare in mind this can make your app laggish if it gets too complex. If so, you can use some js to show or hide the other selectInputs conditionally

Related

I'm getting some unexpected behaviour with a checkboxGroupInput in a Shiny app

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)

Conditionally display single or multiple tables in Shiny App

I have a shiny app that I am building where the user selects a report from a radio button menu, and then the table displays on the page. I would like to add an option for the user to simultaneously view all reports. I have found something close to what I want from this thread https://gist.github.com/wch/5436415/ , but I can't quite seem to implement it properly. Basically, I think what I have to do is:
In the UI, make a call to uiOutput() to reactively update the User Interface. I will need multiple calls to htmlOutput() if the user selects the "all" button, but only one call to htmlOutput() if the use simply selects one report. For the record, I am creating my tables with the kable() function, which is why I call htmlOutput() instead of tableOutput().
In the server function, I need to make a call to renderUI() that provides the instructions on how many htmlOutput() calls there will be and which reports will be in each call.
Create a loop that then makes a call to renderText that then sends the html code for the htmlOutput function to interpret.
I can get most of the way there. I can get Shiny to have reactive tables, and output individual reports, but I am struggling to get the looping range to reactively update so that I see all three tables in my testing app. Here is what I have:
library(shiny)
library(shinydashboard)
library(knitr)
library(kableExtra)
data("cars")
data("iris")
data("airquality")
UI<-dashboardPage(
dashboardHeader(),
dashboardSidebar(sidebarMenu(
menuItem("Options", radioButtons(inputId = "Tables", label="test", choices= c("cars", "iris", "airquality", "all"))
) )),
dashboardBody(
uiOutput(outputId = "TABLE"),
textOutput("N")
)
)
server<-function(input, output){
TBL<-list("cars", "iris", "airquality")
T1<-knitr::kable(head(cars))
T2<-knitr::kable(head(iris))
T3<-knitr::kable(head(airquality))
tmp<-list(cars=T1, iris=T2, airquality=T3)
TABLES<-reactive({
ifelse(input$Tables=="all", tmp, tmp[input$Tables])
})
val<-reactive({
tmp<-TABLES()
length(tmp)})
n<-isolate(val())
output$TABLE<-renderUI({
req(input$Tables)
TAB<-TABLES()
TBL<-names(TAB)
tbls<-lapply(1:length(TBL), function(i){
tblnm<-paste("tbl", i, sep="_")
htmlOutput(tblnm)})
do.call(tagList, tbls)
})#Close Render UI
for(i in 1:n){
local({
j<-i
tblnm<-paste("tbl", j, sep="_")
output[[tblnm]]<-renderText(kables(TABLES()))
})
}
output$N<-renderText(n)
}#Close Server
shinyApp(ui = UI, server = server)
Here is where I think I am going wrong:
I included a textOutput() for the value of n, and despite having the reactive() call to get the length of TABLES, when I isolate() I still get the value of 1 even when I select the "all" report button, which should give me 3. Am I misinterpreting how isolate() works? Is there another way to get a value out of a reactive() function that can be used outside of a *Output() or reactive() function? Any guidance would be much appreciated. Thanks so much.
I think your server function is needlessly complex.
render functions are a reactive context themselves, so no need to define variables which only exist in those contexts as specifically reactive.
server<-function(input, output){
TBL<-list("cars", "iris", "airquality")
T1<-knitr::kable(head(cars))
T2<-knitr::kable(head(iris))
T3<-knitr::kable(head(airquality))
tmp<-list(cars=T1, iris=T2, airquality=T3)
output$TABLE<-renderUI({
if(input$Tables=="all"){ind <- names(tmp)}else{ind <- input$Tables}
lapply(tmp, HTML)[ind]
})
output$N <- renderText({
if(input$Tables=="all"){ind <- names(tmp)}else{ind <- input$Tables}
length(ind)
})
}

How to dynamically generate Shiny sidebarPanel contents?

I'm trying to create an app where the user is initially presented with a sidebar selectInput containing a list of choices: A, B, C and D – populated by file-system contents.
Depending on which choice the user selects, I want to populate the rest of the sidebar with choice-specific contents. For example, if the user chooses "A", then the sidebar will contain an additional selectInput and dateRangeInput.
To keep the code clean, I've kept all "A"-specific code in handle_A.R, etc. This means that eventually when I decide to add a new choice "E", I just need to put all the code in handle_E.R.
ui.R
pageWithSidebar(
headerPanel("Test"),
sidebarPanel(
selectizeInput("choice", "Choice:", c()),
uiOutput("sidebar")
),
mainPanel(uiOutput("main"))
)
server.R (relevant bits)
output$sidebar <- renderUI({
sidebarRenderer[[input$choice]](input, output, session)
})
I have code in server.R that sources all the handle_*.R scripts – each registering their callbacks.
So far, so good. I can select various choices and the relevant callbacks are called. However, I don't know how to implement the callbacks such that I can update the sidebar widgets as the user interacts with the sidebar. I want to essentially do the following (which will not work, because the function needs to return something to renderUI):
handle_A.R
sidebarRenderer[["A"]] <<- function(input, output, session) {
selectInput("day", "Day:", c("Mon", "Wed", "Fri"))
dates <- getDateList(input$day)
dateRangeInput("date", "Date:", start=dates[0], end=dates[length(dates)])
if (hasPublicHoliday(dates))
checkboxInput("ignoreHolidays", "Ignore public holidays")
}
So what I want is to automatically update dateRangeInput to the calculated start and end dates for the corresponding Mon/Wed/Fri. Furthermore, if any of the dates contain a public holiday, I want to display an extra checkbox to let the user ignore public holidays.
If anybody can help me out, I'd greatly appreciate it!
Turns out all I needed was to put the logic in observe and call updateCheckBoxInput.

Can I save the old value of a reactive object when it changes?

Note: After coming up with the answer I reworded the question to make if clearer.
Sometimes in a shiny app. I want to make use of a value selected by the user for a widget, as well as the previous value selected for that same widget. This could apply to reactive values derived from user input, where I want the old and the new value.
The problem is that if I try to save the value of a widget, then the variable containing that value has to be reactive or it will not update every time the widget changes. But, if I save the the value in a reactive context it will always give me the current value, not the previous one.
How can I save the previous value of a widget, but still have it update every time the user changes the widget?
Is there a way that does not require the use of an actionButton every time the user changes things? Avoiding an actionButton can be desirable with adding one is otherwise unnecessary and creates excess clicking for the user.
Seeing as the session flush event method seems to be broken for this purpose, here is an alternative way to do it using an observeEvent construct and a reactive variable.
library(shiny)
ui <- fluidPage(
h1("Memory"),
sidebarLayout(
sidebarPanel(
numericInput("val", "Next Value", 10)
),
mainPanel(
verbatimTextOutput("curval"),
verbatimTextOutput("lstval")
)
)
)
server <- function(input,output,session) {
rv <- reactiveValues(lstval=0,curval=0)
observeEvent(input$val, {rv$lstval <- rv$curval; rv$curval <- input$val})
curre <- reactive({req(input$val); input$val; rv$curval})
lstre <- reactive({req(input$val); input$val; rv$lstval})
output$curval <- renderPrint({sprintf("cur:%d",curre())})
output$lstval <- renderPrint({sprintf("lst:%d",lstre())})
}
options(shiny.reactlog = TRUE)
shinyApp(ui, server)
Yielding:
Update This answer was posted before the advent of the reactiveValues/observeEvent model in shiny. I think that #MikeWise 's answer is the better way to do this.
After some playing around this is what I came up with. The ui.r is nothing special
ui.r
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId="XX", label="Choose a letter",choices=letters[1:5])
),
mainPanel(
textOutput("Current"),
textOutput("old")
)
)
))
"Current" will display the current selection and "old" displays the previous selection.
In the server.r I made use of three key functions: reactiveValues, isolate and session$onFlush.
server.r
library(shiny)
server <- function(input, output,session) {
Values<-reactiveValues(old="Start")
session$onFlush(once=FALSE, function(){
isolate({ Values$old<-input$XX })
})
output$Current <- renderText({paste("Current:",input$XX)})
output$old <- renderText({ paste("Old:",Values$old) })
}
The server.r works like this.
First, Values$old is created using the reactiveValues function. I gave it the value "Start" to make it clear what was happening on load up.
Then I added a session$onFlush function. Note that I have session as an argument in my server function. This will run every time that shiny flushes the reactive system - such as when the selectizeInput is changed by the user. What is important is that it will run before input$XX gets a new value - so the value has changed at the selectizeInput but not at XX.
Inside the session$onFlush I then assign the outgoing value of XX to Values$old. This is done inside an isolate() as this will prevent any problems with input$XX gets updated with the new values. I can then use input$XX and Values$old in the renderText() functions.

Shiny - Taking Text input into auxiliary function

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

Resources