I am trying to create a shiny app where it allows you to select an input of what operation calculate. if the user chooses "Addition" it will show the two numeric input boxes (so they can input two numbers), if the user chooses "square" it will show only one numeric input box to square.
With this, I use conditionalPanel and if the condition is satisfied, it fetches through uiOutput() the numericInputs that I want. and same thing for square.
Now when I run this app, the conditional panels do not appear. Where did I go wrong? Thanks for checking out my question.
ui <- fluidPage( theme = shinytheme("slate"),
titlePanel("Basic Calculator"),
sidebarPanel(
selectInput("ops","Select what Operation use",choices = c("ADDITION","SQUARE")),
helpText("Please input the appropriate number depending on the operations"),
conditionalPanel("input.ops=='ADDITION'", uiOutput("var2")),
conditionalPanel("input.ops=='SQUARE'", uiOutput("var1"))
),#sidebar panel
)#fluidpage
server <- function(input, output) {
output$basicmath <- renderText( ifelse(input$ops=="ADDITION",input$a+input$b,
ifelse(input$ops=="SUBTRACTION",input$a-input$b,
ifelse(input$ops=="SQUARE",input$a*input$a,
ifelse(input$ops=="SQUARE ROOT",sqrt(input$a),
ifelse(input$ops=="MULTIPLICATION",input$a*input$b,"not a valid operation"))))),
output$var2 <- renderUI({
helpText("this will show to input two numerics to be added")
}) ,
output$var1 <- renderUI({
helpText("this will show to input one numeric to square")
})
)}
shinyApp(ui = ui, server = server)
The key issue you were having is that you put the uiOutputs inside the calculation output that you anticipated. It is better to separate them, since the calculation output won't run until it has the necessary prerequisite values (your input values). In addition, because you hadn't specified an output location for basicmath, the app didn't know where to put anything inside that call to renderText(). Below is working code that gets the right UI elements to appear.
One other thing you were missing in your renderUI was the use of tagList(). This helps ensure that all of your elements are packaged together, not just the last one.
Note that the math part does not work, but it looks like that was just a placeholder. When you do start to use it, be sure to use unique ids for each input. You have several instances of input$a and input$b, which probably isn't a workable approach.
library(shiny)
library(shinythemes)
ui <- fluidPage( theme = shinytheme("slate"),
titlePanel("Basic Calculator"),
sidebarPanel(
selectInput("ops","Select what Operation use",choices = c("ADDITION","SQUARE")),
helpText("Please input the appropriate number depending on the operations"),
conditionalPanel("input.ops=='ADDITION'", uiOutput("var2")),
conditionalPanel("input.ops=='SQUARE'", uiOutput("var1"))
),
mainPanel(
textOutput("basicmath")
)
)#fluidpage
server <- function(input, output) {
output$var2 <- renderUI({
tagList(
helpText("this will show to input two numerics to be added"),
splitLayout(
numericInput("var2a", label = "Input one number", value = NULL),
numericInput("var2b", label = "Input another number", value = NULL)
)
)
})
output$var1 <- renderUI({
tagList(
helpText("this will show to input one numeric to square"),
numericInput("var1a", label = "Input a number", value = NULL)
)
})
output$basicmath <- renderText( {ifelse(input$ops=="ADDITION",input$a+input$b,
ifelse(input$ops=="SUBTRACTION",input$a-input$b,
ifelse(input$ops=="SQUARE",input$a*input$a,
ifelse(input$ops=="SQUARE ROOT",sqrt(input$a),
ifelse(input$ops=="MULTIPLICATION",input$a*input$b,"not a valid operation")))))
})
}
shinyApp(ui = ui, server = server)
Related
I want to take a user's input and store it as a variable that will be used in a plotting function. My code:
ui <- fluidPage(
mainPanel(
plotlyOutput("plot", width = '100%'),
br(),
textAreaInput("list", "Input List", ""),
actionButton("submit", "Submit", icon = icon("refresh"), style="float:right")
))
server <- function(input, output, session) {
my_text <<- renderText({
req(input$submit)
return(isolate(input$list))
my_text ->> subv
})
bindEvent(my_text,
output$plot <- renderPlotly({
#my very long plot code goes here which takes subv as input. This part has been tested outside of shiny and I know works.
}
I am trying to store the text in the subv variable as it will dictate what the renderPlotly will generate. When I hit submit nothing happens and the variable is only created after the session ends. The newly created subv variable in my environment does not show the text that was inputted but lists subv as an empty function i.e. subv function(...)
Below you can find a working prototype of what you would like to achieve with some information on what the issues were
First, we need to have a textOutput where our text will be shown. I understand this may not be necessary for the actual use case but it is important for this answer's demonstration purposes.
Next, we should not need to set variables to global via <<- or ->>. This is generally not good practice. Instead, we should store our result in a reactive. See also reactiveVals (but this is harder to follow when the app gets complex).
Since we need to only get the value when we click submit, we should use an event bind to only run when we click submit. This is essentially similar to eventReactive.
Finally, we can use bindCache to cache our result on the input list.
ui <- fluidPage(
mainPanel(
plotlyOutput("plot", width = '100%'),
br(),
textAreaInput("list", "Input List", ""),
actionButton("submit", "Submit", icon = icon("refresh"),
style="float:right"),
textOutput("hello_out")
))
server <- function(input, output, session) {
my_text <- reactive({
input$list
}) %>%
shiny::bindCache(input$list
) %>%
shiny::bindEvent(input$submit)
output$hello_out <- renderText({
my_text()
})
}
shinyApp(ui, server)
How to hide a conditional panel in Shiny? Please, see the following example:
library(shiny)
ui <- fluidPage(
actionButton("eval","Evaluate"),
numericInput("num_input", "If number is changed, cp must hide", value = 0),
conditionalPanel(
condition = "input.eval",
"text"))
server <- function(input, output, session) {
observeEvent(input$num_input, {
input$eval <- 0
})}
shinyApp(ui, server)
What I want to achieve is: Once the user clicks the evaluate-button the conditional panel should appear, but once the number in num_input is changed the panel should disappear. My idea was to null the evaluate-button, but this does not work (the app opens with gray background and seems frozen).
I also tried it with shinyjs like so:
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
actionButton("eval","Evaluate"),
numericInput("num_input", "If number is changed, cp must hide", value = 0),
conditionalPanel(
id = "cond_panel",
condition = "input.eval",
"text"))
server <- function(input, output, session) {
observeEvent(input$num_input, {
reset("cond_panel")})}
shinyApp(ui, server)
But this does not work either: the app opens regularly and the conditional panel is shown once the evaluate-button is clicked, but nothing happens once the number is changed.
You can create an output value and use it just for the conditional panel. The article on dynamic UI explains how to do this:
http://shiny.rstudio.com/articles/dynamic-ui.html
The condition can also use output values; they work in the same way (output.foo gives you the value of the output foo). If you have a situation where you wish you could use an R expression as your condition argument, you can create a reactive expression in the server function and assign it to a new output, then refer to that output in your condition expression.
If you do this, make sure to also set outputOptions(output, [newOutputName], suspendWhenHidden = FALSE). (This is necessary because Shiny normally doesn’t send values to the browser for outputs that are hidden or not present in the UI. In this case, however, the browser does need to know the most up-to-date output value in order to correctly evaluate the condition of the contitionalPanel function - suspendWhenHidden = FALSE ensures this will happen.)
library(shiny)
ui <- fluidPage(
actionButton("eval","Evaluate"),
numericInput("num_input", "If number is changed, cp must hide", value = 0),
conditionalPanel("input.eval && !output.hide_panel", "text")
)
server <- function(input, output, session) {
output$hide_panel <- eventReactive(input$num_input, TRUE, ignoreInit = TRUE)
outputOptions(output, "hide_panel", suspendWhenHidden = FALSE)
}
shinyApp(ui, server)
Another way would be to renderUI the conditional panel, and show it until input$num_input changes.
I have never played much with conditionalPanel, so not sure if it has default settings to hide/show. Following might work to give you the desired output.
library(shiny)
library(shinyjs)
if(interactive()){
shinyApp(
ui <- fluidPage(
shinyjs::useShinyjs(),
actionButton("eval","Evaluate"),
numericInput("num_input", "If number is changed, cp must hide", value = 0),
shinyjs::hidden(
div(
id = "cp1",
conditionalPanel(condition = "input.eval",
textOutput("text1")))
)
),
server = function(input, output, session){
output$text1 <- renderText({
input$num_input
})
observeEvent(input$eval,{
shinyjs::show("cp1")
})
observeEvent(input$num_input,{
shinyjs::hide("cp1")
})
}
)
}
I am hiding the conditionalPanel initially using shinyjs, displaying numeric input entered using renderText, and having two observeEvent to hide\show the panel accordingly.
I would like to design a Shiny app with two buttons. Users can click the "Add UI" button as many times as they want, which will return text boxes. Users can then type numbers to the input boxes, click the "Sum" button, and calculate the total.
Below is my current code, modified from the sample code from ?insertUI. My question is I am not sure how to refer to the input id from the updated UI (in this case, the new text boxes). My current attempt cannot calculate the sum. The end result is always 0.
# Define UI
ui <- fluidPage(
actionButton("add", "Add UI"),
actionButton("sum", "Sum"),
# Report the output
h4("The total from input"),
textOutput("text")
)
# Server logic
server <- function(input, output, session) {
observeEvent(input$add, {
insertUI(
selector = "#add",
where = "afterEnd",
ui = textInput(paste0("txt", input$add),
"Insert some text")
)
})
# Calculate the total from the text inputs
output$text <- eventReactive(input$sum, {
as.character(sum(as.numeric(unlist(mget(ls(pattern = "^txt"))))))
})
}
# Complete app with UI and server components
shinyApp(ui, server)
You can use the special Shiny variable input to check and access the current inputs (and values) in your app. Thus you can get at newly inserted UI elements (assuming they all follow a pattern) and compute against them.
output$text <- eventReactive(input$sum, {
txt_inpt_names <- names(input)[grepl("^txt", names(input))]
sum(sapply(txt_inpt_names, function(x) as.numeric(input[[x]])), na.rm = T)
})
Worth noting that Shiny requires single (one-at-a-time) access to input values so thats why sapply() is required and not just input[[txt_inpt_names]].
How to hide a conditional panel in Shiny? Please, see the following example:
library(shiny)
ui <- fluidPage(
actionButton("eval","Evaluate"),
numericInput("num_input", "If number is changed, cp must hide", value = 0),
conditionalPanel(
condition = "input.eval",
"text"))
server <- function(input, output, session) {
observeEvent(input$num_input, {
input$eval <- 0
})}
shinyApp(ui, server)
What I want to achieve is: Once the user clicks the evaluate-button the conditional panel should appear, but once the number in num_input is changed the panel should disappear. My idea was to null the evaluate-button, but this does not work (the app opens with gray background and seems frozen).
I also tried it with shinyjs like so:
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
actionButton("eval","Evaluate"),
numericInput("num_input", "If number is changed, cp must hide", value = 0),
conditionalPanel(
id = "cond_panel",
condition = "input.eval",
"text"))
server <- function(input, output, session) {
observeEvent(input$num_input, {
reset("cond_panel")})}
shinyApp(ui, server)
But this does not work either: the app opens regularly and the conditional panel is shown once the evaluate-button is clicked, but nothing happens once the number is changed.
You can create an output value and use it just for the conditional panel. The article on dynamic UI explains how to do this:
http://shiny.rstudio.com/articles/dynamic-ui.html
The condition can also use output values; they work in the same way (output.foo gives you the value of the output foo). If you have a situation where you wish you could use an R expression as your condition argument, you can create a reactive expression in the server function and assign it to a new output, then refer to that output in your condition expression.
If you do this, make sure to also set outputOptions(output, [newOutputName], suspendWhenHidden = FALSE). (This is necessary because Shiny normally doesn’t send values to the browser for outputs that are hidden or not present in the UI. In this case, however, the browser does need to know the most up-to-date output value in order to correctly evaluate the condition of the contitionalPanel function - suspendWhenHidden = FALSE ensures this will happen.)
library(shiny)
ui <- fluidPage(
actionButton("eval","Evaluate"),
numericInput("num_input", "If number is changed, cp must hide", value = 0),
conditionalPanel("input.eval && !output.hide_panel", "text")
)
server <- function(input, output, session) {
output$hide_panel <- eventReactive(input$num_input, TRUE, ignoreInit = TRUE)
outputOptions(output, "hide_panel", suspendWhenHidden = FALSE)
}
shinyApp(ui, server)
Another way would be to renderUI the conditional panel, and show it until input$num_input changes.
I have never played much with conditionalPanel, so not sure if it has default settings to hide/show. Following might work to give you the desired output.
library(shiny)
library(shinyjs)
if(interactive()){
shinyApp(
ui <- fluidPage(
shinyjs::useShinyjs(),
actionButton("eval","Evaluate"),
numericInput("num_input", "If number is changed, cp must hide", value = 0),
shinyjs::hidden(
div(
id = "cp1",
conditionalPanel(condition = "input.eval",
textOutput("text1")))
)
),
server = function(input, output, session){
output$text1 <- renderText({
input$num_input
})
observeEvent(input$eval,{
shinyjs::show("cp1")
})
observeEvent(input$num_input,{
shinyjs::hide("cp1")
})
}
)
}
I am hiding the conditionalPanel initially using shinyjs, displaying numeric input entered using renderText, and having two observeEvent to hide\show the panel accordingly.
I am facing a problem with submitButton usage in my Shiny application (which I use as some time-consuming rendering is done with the data supplied by the app-user). I also use some radioButtons with conditionalPanel to define the variables group of which user may choose the parameters. Please see the attached image to get the idea (user is selecting a list type, and - based on his list choice - a particular list appears (conditionalPanel is working) from which the user is selecting a parameter), or run a reprodicible example supplied below.
Of course, only the parameter is a value that is using in rendering an output, and I would like to force a submitButton to pass only the parameter in an automatic way. The problem is that submitButton affects also the radioButtons element, which unables the use to choose the desired value (as the values list are not switching).
QUESTION: Is there any way to define which UI elements are to be stop-from-automatic-update by submitButton so as to solve my problem? Thank you for any help!
UI:
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("Problem with submit button"),
sidebarPanel(
radioButtons("selectionway", "Choose list type", c(number='number', letter='letter')),
conditionalPanel(
condition = "input.selectionway == 'number'",
selectInput("numberlist", "Choose NUMBER:", choices = c("11111", "22222", "33333"))
),
conditionalPanel(
condition = "input.selectionway == 'letter'",
selectInput("letterlist", "Choose LETTER:", choices = c("A", "B", "C"))
),
submitButton("submitButton")
),
mainPanel(
verbatimTextOutput("list"),
verbatimTextOutput("value")
)
))
SERVER:
library(shiny)
shinyServer(function(input, output) {
selected.value <- reactive({
if(input$selectionway=="letter"){
return(input$letterlist)
} else {
return(input$numberlist)
}
})
output$list <- renderText({
input$selectionway
})
output$value <- renderText({
selected.value()
})
})