Question
In R Shiny, when using
renderUI
uiOutput
to dynamically generate sets of controls, such as:
checkboxes
radiobuttons
text boxes
how can I harvest those values or populate input by causing events?
As-is, those generated controls appear to be "display only". Making a selection, marking a checkbox, or entering data only updates the display, but no Event is created and the values are not populated into the "input" variable ( ReactiveValues ); thus, nothing is received by the Shiny server process.
If these control inputs are in-fact isolated, it completely undermines the point of dynamically creating controls.
Obviously, I'm hoping that this issue has been addressed, but my searches haven't turned it up.
In my specific case, the UI allows the user to:
Select and upload a CSV file.
The logic identifies numerical, date, and grouping columns, and produces 3 sets of radiobutton control sets. The idea is that you pick which columns you are interested in.
Picking a grouping column SHOULD return that columnID back to the server, where it will display a discrete list of groups from which to select. This fails, as the selections do not generate an Event, and the input variable (provided to server.R) only contains the ReactiveValues from the static controls.
That said, the display of the controls looks fine.
Step#0 screenshot:
Step#1 screenshot:
On the server.R side, I'm using code as below to create the radioButtons.
output$radioChoices <- reactive({
...
inputGroup <- renderUI({
input_list <- tagList(
radioButtons(inputId = "choiceGrp", label = "Available Grouping Columns", choices = grpColumnNames, inline = TRUE, selected = selectedGrp),
radioButtons(inputId = "choiceNumb",label = "Available Numerical Columns",choices = numColumnNames, inline = TRUE, selected = selectedNum),
radioButtons(inputId = "choiceDate",label = "Available Date Columns", choices = dateColumnNames, inline = TRUE, selected = selectedDate),
hr()
)
do.call(tagList, input_list)
})
print(inputGroup)
output$radioChoices <- inputGroup
})
I have played around with a Submit button and ActionButtons to try and force an Event, but no dice. My skull-storming is now going to places like "do I need to somehow use Javascript here?"
Many thanks to all of you who are lending me your cycles on this matter.
I'm not sure I understand your problem. Here's a MWE that accesses the value of a widget created by uiOutput/renderUI. The values of widgets created by uiOutput/renderUIcan be accessed just like those of any other widget.
If this doesn't give you what you want, please provide more details.
library(shiny)
ui <-
fluidPage(
uiOutput("dataInput"),
textOutput("result")
)
server <- function(input, output, session) {
output$dataInput <- renderUI({
selectInput("beatles", "Who's your favourite Beatle?", choices=c("- Select one -"="", "John", "Paul", "George", "Ringo"))
})
output$result <- renderText({
req(input$beatles)
paste0("You chose ", input$beatles)
})
}
shinyApp(ui, server)
I added the button but the values will automatically change before I hit "Update Order", I don't know how to fix it. Should be like this:enter image description hereBelow is my code:
library(shiny)
ui <- fluidPage(
titlePanel("My Simple App"),
sidebarLayout(
sidebarPanel(
helpText("Controls for my app"),
selectInput("fruitchoice",
label = "Choose a fruit",
choices = list("Apples",
"Oranges",
"Mangos",
"Pomegranate"),
selected = "Percent White"),
sliderInput("amt",
label = "Order Amount:",
min=0, max = 100, value=20),
actionButton ("Update","Update Order")
),
mainPanel(
helpText("Fruit Chosen:"),
verbatimTextOutput("fruit"),
helpText("Order Amount"),
verbatimTextOutput("amt")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
SelectInput <- eventReactive (input$Update , {
runif(input$fruitchoice,amt)
})
output$fruit = renderText(input$fruitchoice)
output$amt = renderText(input$amt)
}
# Run the application
shinyApp(ui = ui, server = server)
I will show you, how to rewrite your code to get this update behavior, however I would like to also get you know that this part of code:
SelectInput <- eventReactive (input$Update , {
runif(input$fruitchoice,amt)
})
Is wrong for three reasons: (1) object amt do not exists, you probably want input$amt; (2) even if you change amt to input$amt code won't work and you will get error; (3) now you are not using SelectInput in any place in your application, so there is no reason for this part to exists, however as I don't know what is your aim and how will look the final app, I'm not saying this is generally wrong.
Ok, so now about this update button. We will focus on this code:
output$fruit = renderText(input$fruitchoice)
output$amt = renderText(input$amt)
Here you instruct program to (re)render text when input$fruitchoice or (in second line) when input$amt change, but you want to (re)render text only when user clicks the button, so you need two things - first, be sure that user clicked the button and do not (re)render text when one of input$ changes. This will work:
output$fruit = renderText({
req(input$Update)
isolate(input$fruitchoice)
})
output$amt = renderText({
req(input$Update)
isolate(input$amt)
})
If I understand Shiny correctly, isolate() makes sure that text is not (re)rendering when input$ changes (however it has internally the new values) and req() makes sure that the input$Update was clicked; and when is clicked again, Shiny recomputes [(re)renders] text. It recomputes, because we didn't use isolate() on input$Update I think.
There's a few things wrong in your code. I will give a bit of explanation for each one:
You are initializing with reactive inputs. By using renderText(input$...) you create a text output that updates automatically when your input updates. Automatically is the problem here, you don't want that. We are going to write an alternative method that stores the inputs in a separate variable that we only allow to be updated when the button is pressed. We initialize that variable like so:
rv <- reactiveValues(fruit = "Apples",
amt = 20)
EventReactive creates a reactive variable that can later be used in the code. Generally speaking what you want to use in these kind of scenarios is observeEvent. You can do so like this:
observeEvent (input$Update , {
rv$fruit <- input$fruitchoice
rv$amt <- input$amt
})
We now have a list of variables under the name "rv" and an observeEvent that updates this variable every time the button gets pressed. All that is left to do is create the renderText which you can do like so:
output$fruit <- renderText(rv$fruit)
output$amt <- renderText(rv$amt)
I am working on a prediction model using R Shiny.
I have as input some variables for the model, like sex, age, height....
I than have a action button saying "Generate Prediction".
When pressed, some text and figures appear based on the prediction made with the input variables.
I also included a "reset" action button.
I want when this button is pressed for all variables to go to original value (that already works) AND that the output generated after the "Generate Prediction" button disappears.
My problem is with the second part of this wish.
Is it possible and how can I remove the output after pressing "reset"?
Find my script below as an example (the real script is more complex).
I would like the part stating from "Results of prediction" to disappear when reset is pressed.
library(shiny)
# Define UI ----
ui <- fluidPage(
titlePanel(title=div( "COPD risk prediction tool")),
p("Chronic Obstructive Pulmonary Disease (COPD) is a lung problem that can affect people mainly as they get older. One of the main features of COPD is a change in the airways that alters how the air is held in the lungs and the ease with which breathing occurs (the airways become 'obstructed'). This may cause breathlessness, frequent coughing, production of sputum from your chest, and chest infections."),
selectInput("sex", label=h4("What is your gender?"),
choices=list("Female"=0, "Male"=1), selected=0),
selectInput("age", label=h4("What is your age?"),
choices=list("18"=18, "19"=19, "20"=20, "21"=21, "22"=22, "23"=23, "24"=24, "25"=25, "26"=26, "27"=27, "28"=28, "29"=29, "30"=30), selected=20),
bsTooltip("age",
"What is your current age in years?","right"),
selectInput("weight", label=h4("What is your weight?"),
choices=list("50"=50, "51"=51, "52"=52, "53"=53, "54"=54, "55"=55, "56"=56, "57"=57, "58"=58, "59"=59, "60"=60, "61"=61, "62"=62, "63"=63, "64"=64, "65"=65, "66"=66, "67"=67, "68"=68, "69"=69, "70"=70, "71"=71, "72"=72, "73"=73, "74"=74, "75"=75, "76"=76, "77"=77, "78"=78, "79"=79, "80"=80, "81"=81, "82"=82, "83"=83, "84"=84, "85"=85, "86"=86, "87"=87, "88"=88, "89"=89, "90"=90, "91"=91, "92"=92, "93"=93, "94"=94, "95"=95, "96"=96, "97"=97, "98"=98, "99"=99, "100"=100), selected=75),
bsTooltip("weight",
"What is your current weight in kg?", "right"),
selectInput("height", label=h4("What is your height?"),
choices=list("140"=140, "141"=141, "142"=142, "143"=143, "144"=144, "145"=145, "146"=146, "147"=147, "148"=148, "149"=149, "150"=150, "151"=151, "152"=152, "153"=153, "154"=154, "155"=155, "156"=156, "157"=157, "158"=158, "159"=159, "160"=160, "161"=161, "162"=162, "163"=163, "164"=164, "165"=165, "166"=166, "167"=167, "168"=168, "169"=169, "170"=170, "171"=171, "172"=172, "173"=173, "174"=174, "175"=175, "176"=176, "177"=177, "178"=178, "179"=179, "180"=180, "181"=181, "182"=182, "183"=183, "184"=184, "185"=185), selected=170),
bsTooltip("height",
"What is your current height in cm?", "right"),
br(),
h4("Medical Disclaimer", style = "color:blue"),
p(strong("This risk prediction tool is for general information and should not replace advice from your GP who knows your individual history.", style = "color:blue")),
p(strong("Although we have included major risk factors, COPD can affect anyone and if you have symptoms or concerns you should speak to your doctor.", style = "color:blue")),
p(strong("This risk score is derived from Caucasian populations and may not be as accurate for other ethnic groups.", style = "color:blue")),
actionButton("submit", label = "Generate Prediction"), actionButton("reset", label=("Reset")),
h2(textOutput('title')),
h4(textOutput('label1')),
h5(textOutput('label2')),
verbatimTextOutput("prediction")
)
# Define server logic ----
server <- function(input, output,session) {
submit <- FALSE
output$title <- eventReactive(input$submit, {
'Results of prediction'
})
output$label1 <- eventReactive(input$submit, {
'COPD risk prediction score'
})
output$label2 <- eventReactive(input$submit, {
'Your predicted risk (%) of developing COPD in your lifetime is:'
})
output$prediction <- eventReactive(input$submit, {
round((copdRisk(weight=input$weight, height=input$height, sex=input$sex)*100), 1)
})
output$label5 <- eventReactive(input$submit, {
'This means that for every 100 people sharing your characteristics '
})
output$label6 <- eventReactive(input$submit, {
'would develop COPD in their lifetime.'
})
observe({
input$reset
updateSelectInput(session, "age", selected=20)
updateSelectInput(session, "weight", selected=75)
updateSelectInput(session, "height", selected=170)
updateSelectInput(session, "sex", selected=0)
#updateActionButton(session, "submit", selected=FALSE)
})
}
# Run the app ----
shinyApp(ui = ui, server = server)
In my answer below I will demonstrate how to achieve what you're asking for. In my answer I use the shinyjs package, both for resetting input values and for hiding/showing the results. I cannot run your code because there are extra packages and functions that you are using that I don't know about, and the code itself is not a minimal isolated example, so instead I'll write my own small app that does something similar and achieves what you want. Here is the code:
library(shiny)
ui <- fluidPage(
shinyjs::useShinyjs(),
numericInput("num", "Enter a number", 7),
actionButton("submit", "Square that number!"),
actionButton("reset", "Reset"),
shinyjs::hidden(
div(
id = "results",
h3("The square is"),
textOutput("square")
)
)
)
server <- function(input, output, session) {
output$square <- renderText({
input$submit
isolate(input$num * input$num)
})
observeEvent(input$reset, {
shinyjs::reset("num")
shinyjs::hide("results")
})
observeEvent(input$submit, {
shinyjs::show("results")
})
}
shinyApp(ui = ui, server = server)
To address your two questions specifically and how they are solved above:
To reset inputs to their original value, I use the shinyjs::reset() function. This is a much better approach than updating the inputs to a particular value, because the reset() function will guarantee to reset it to whatever value it was originally, whereas your approach means that if you change the initial value in the UI, you must remember to change it in the server as well.
To hide the results after pressing reset, I wrapped all the results UI inside a div(id = "results", ...). Then whenever the submit button is pressed, I use shinyjs to show it, and when reset is pressed I use shinyjs to hide it. I also wrapped the UI in a shinyjs::hidden(...) because you want the results to start off as not showing.
Both of the above require a call to shinyjs::useShinyjs() in the UI.
You should be able to build off this example and implement these techniques in your more complex app.
Also note that my sample app above does a few other things differently than yours. For example, you should not use output$X <- eventReactive(...). You should use the render functions (such as renderText()) when assigning into outputs.
For people who have the same problem, this might also be a solution: Restart Shiny Session. In this way all inputs and output are reset, but it takes more time.
In my shiny app I have several tabs as follows.
I have little complex functions running in this app. I think all the observe functions in the server function run when anything is done in any tab. So I need to run only relevant observe functions for relevant tab. As an example, when I am in Summary tab only the relevant observe function should run and all the other observe functions should not run. I have a code.
server <- function(input, output) {
summary <- observe({....})
occupancy<- observe({....})
Bookings<- observe({....})
Maps<- observe({....})
}
Is there any modification to the above code to run only the relevant observe function related to the tab opened in the app.?
Some approaches come to mind. But first; what do you want to do in your observers? If you are simply creating output to display to the user, don't forget to use reactive elements. They will automatically invalidate only when their output is used to display something to the user. Thus if reactive X uses input Y used to construct output for tab A, and input Y changes while we are looking at tab B, reactive X will not invalidate.
If you are using your observers to only create side-effects, such as updating inputs or showing modalDialogs, you could:
use observeEvent instead of observe to only listen to changes in a certain input or condition.
use isolate to make isolate certain dependencies.
build an if-statement in your observer, that checks which tab is selected. You can do that by giving your sidebarMenu an id (my_sidebarmenu in the example below), and check which tab is selected inside your observer by calling input$my_sidebarmenu.
Some examples given below, how this helps~
#UI.R
#loading shiny library
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(id='my_sidebarmenu',
menuItem('Tab 1', tabName='tab1'),
menuItem('Tab 2', tabName='tab2'),
actionButton(inputId = 'btn','Press me!')
)
),
dashboardBody(
tabItems(
tabItem('tab1',
p('Tab 1'),
actionButton('btn_tab1','Show message!')),
tabItem('tab2',
p('Tab 2'),
actionButton('btn_tab2','Show message!'))
)
)
)
server <- function(input, output,session)
{
observeEvent(input$btn,
{
if(input$my_sidebarmenu=='tab1')
{
updateTabItems(session,'my_sidebarmenu',selected='tab2')
}
if(input$my_sidebarmenu=='tab2')
{
updateTabItems(session,'my_sidebarmenu',selected='tab1')
}
})
observeEvent(input$btn_tab1,
{
showModal(modalDialog(
title = "One.",
"You are viewing tab 1!",
easyClose = TRUE,
footer = NULL
))
})
observeEvent(input$btn_tab2,
{
showModal(modalDialog(
title = "Two.",
"You are viewing tab 2!",
easyClose = TRUE,
footer = NULL
))
})
}
shinyApp(ui,server)
Let's say we have a set of widgets each with their own input label. How do we create a reactive object whose value is the character that represents the input ID of the last widget that was modified?
For example, if we have
ui.R
shinyUI(fluidPage(
textInput('txt_a', 'Input Text A'),
textInput('txt_b', 'Input Text B")
))
server.R
shinyServer(function(input, output) {
last_updated_widget <- reactive({
#hypothetical code that indicates ID value of last updated widget
})
})
The desired result is as follows. If the user modifies the first text box, then the value of last_updated_widget() would be "txt_a". If they modify the second box, the value of last_updated_widget() becomes "txt_b". I'm in search of a result that extends to the obvious generalization of setting the value to be the ID of any of the widgets that was adjusted last.
I'd like this to work for an arbitrary number of widget inputs, including the case that they were generated by a renderUI() statement. So making a separate reactive() statement for each widget isn't an option. However, if the reactive statement requires a loop over all the widget names (or something like that) I can certainly work with that. And multiple reactive statements is okay, as long as it's a fixed amount, and not a function of the number of widgets.
It seems like a pretty simple problem, so I was surprised when it became a roadblock for me. I feel like the solution would be really obvious and I'm just not seeing, so if it is, I apologize for making it a new question. But any help would be greatly appreciated.
Here's a solution that works, though it looks a little awkward because of a nested observe(). I'm not sure what a better way would be, but there could be something nicer.
Basically, use an observe() to loop over all the available inputs, and for each input, use another observe() that will only trigger when that input is changed and set a variable to the id of the input.
runApp(shinyApp(
ui = shinyUI(
fluidPage(
textInput('txt_a', 'Input Text A'),
textInput('txt_b', 'Input Text B'),
uiOutput('txt_c_out'),
verbatimTextOutput("show_last")
)
),
server = function(input, output, session) {
output$txt_c_out <- renderUI({
textInput('txt_c', 'Input Text C')
})
values <- reactiveValues(
lastUpdated = NULL
)
observe({
lapply(names(input), function(x) {
observe({
input[[x]]
values$lastUpdated <- x
})
})
})
output$show_last <- renderPrint({
values$lastUpdated
})
}
))
You can use a reactive value created with reactiveValues() to store the name of the last used widget. Later use an observer to keep track of the activity of each widget and update the reactive value with the name of the last used widget.
In the folowing example, the name of the last used widget is stored in last_updated_widget$v and will active the verbatimTextOutput each time it changes. You can use last_updated_widget$v at any place in the server.
library(shiny)
runApp(list(
ui = shinyUI(
fluidPage(
textInput('txt_a', 'Input Text A'),
textInput('txt_b', 'Input Text B'),
verbatimTextOutput("showLast")
)
),
server = function(input, output, session) {
last_updated_widget <- reactiveValues( v = NULL)
observe ({
input$txt_a
last_updated_widget$v <- "txt_a"
})
observe ({
input$txt_b
last_updated_widget$v <- "txt_b"
})
output$showLast <- renderPrint({
last_updated_widget$v
})
}
))