observeenvent with two conditions - r

I have tried to nest some "observe events" in Shiny to create a conditional rule.
It should go like this :
if one box is clicked display the corresponding single output when the button is clicked.
if both boxes are clicked display both outputs when the button is clicked.
but it always displays both outputs.
Any suggestion?
shinyApp(
ui = basicPage(
checkboxInput("box1", label = "Checkbox1", value = FALSE),
checkboxInput("box2", label = "Checkbox2", value = FALSE),
actionButton('buttn', 'Validate'),
verbatimTextOutput("out1"),
verbatimTextOutput("out2")
),
server = function(input, output) {
observeEvent(input$buttn, {
observeEvent(input$box1, {
output$out1 <- renderText({"Foo"})});
observeEvent(input$box2, {
output$out2 <- renderText({"bar"})})
})
}
)

Please note that it is bad practice to put observeEvents or reactives inside other observeEvents. See this slide and the two after it from a presentation by Joe Cheng.
One possible solution is to simply show or hide elements with the shinyjs package. A working example is given below.
Another solution is to use reactiveVal to hold the text to be displayed, and update that from your observer.
Hope this helps!
Solution 1
library(shiny)
library(shinyjs)
ui <- basicPage(
checkboxInput("box1", label = "Checkbox1", value = FALSE),
checkboxInput("box2", label = "Checkbox2", value = FALSE),
actionButton('buttn', 'Validate'),
shinyjs::hidden(div(id='div1', verbatimTextOutput("out1"))),
shinyjs::hidden(div(id='div2', verbatimTextOutput("out2"))),
useShinyjs()
)
server <- function(input, output) {
observeEvent(input$buttn, {
if(input$box1)
shinyjs::show('div1')
else
shinyjs::hide('div1')
if(input$box2)
shinyjs::show('div2')
else
shinyjs::hide('div2')
})
output$out1 <- renderText({"Foo"})
output$out2 <- renderText({"bar"})
}
shinyApp(ui,server)
Solution 2
library(shiny)
library(shinyjs)
ui <- basicPage(
checkboxInput("box1", label = "Checkbox1", value = FALSE),
checkboxInput("box2", label = "Checkbox2", value = FALSE),
actionButton('buttn', 'Validate'),
verbatimTextOutput("out1"),
verbatimTextOutput("out2")
)
server <- function(input, output) {
text1 <- reactiveVal(NULL)
text2 <- reactiveVal(NULL)
observeEvent(input$buttn, {
ifelse(input$box1,text1('Foo'),text1(NULL))
ifelse(input$box2,text2('Bar'),text2(NULL))
})
output$out1 <- renderText({text1()})
output$out2 <- renderText({text2()})
}
shinyApp(ui,server)

You don't need the extra event observers. Just observe the button click and use standard R conditional logic to adjust the output based on the checkboxes.
shinyApp(
ui = basicPage(
checkboxInput("box1", label = "Checkbox1", value = FALSE),
checkboxInput("box2", label = "Checkbox2", value = FALSE),
actionButton('buttn', 'Validate'),
verbatimTextOutput("out1"),
verbatimTextOutput("out2")
),
server = function(input, output) {
observeEvent(input$buttn, {
if (input$box1) {
output$out1 <- renderText({"Foo"})
}
if (!input$box1) {
output$out1 <- renderText({NULL})
}
if (input$box2) {
output$out2 <- renderText({"Bar"})
}
if (!input$box2) {
output$out2 <- renderText({NULL})
}
})
}
)

Related

Why is removeUI leaving behind the UI label?

I am trying to increase and decrease the number of UI elements based on user input. This MRE code kind of works but it is leaving behind the UI label when I use removeUI, which I did not expect. Any ideas on how to make the label go away along with the input box?
## Only run this example in interactive R sessions
if (interactive()) {
# Define UI
ui <- fluidPage(
numericInput(inputId = "assessors",label = "Number of Assessors",value = 1,min = 1,step = 1),
textInput(inputId = "assessor1",label = "Assessor 1 Columns")
)
# Server logic
server <- function(input, output, session) {
tot_app<-0
observeEvent(input$assessors, {
num<-input$assessors
if(num>tot_app){#add
adds<-seq(tot_app+1,num)
for(i in adds){
here<-paste0("#assessor",i-1)
insertUI(
selector = here,
where = "afterEnd",
ui = textInput(paste0("assessor", i),
paste0("Assessor ",i," columns"))
)
}
tot_app<<-num
} else if(num<tot_app){#subtract
subs<-seq(num+1,tot_app)
for(i in subs){
removeUI(selector = paste0("#assessor",i))
}
tot_app<<-num
}
})
}
# Complete app with UI and server components
shinyApp(ui, server)
}
As in the help example, it works if you use use this syntax:
removeUI(selector = paste0("div:has(> #assessor",i,")"))

RShiny: Hiding / Showing a Table based on Radio Buttons

I have two tables and I'm trying to show one at a time based on user input in radio buttons. If the input from the radio buttons is "table", i'd like to show table1. If the input is else i'd like to show table2.
observeEvent(input$visuBtn,{
req(input$visuBtn)
print(input$visubtn)
if(input$visuBtn == "table"){
hide("table2")
#DT::dataTableOutput("table1")
renderUI(
DT::dataTableOutput("table1")
)
}else{
print("Should show table2")
# removeUI(
# selector = "table"
# )
renderUI(
DT::dataTableOutput("table2")
)
#DT::dataTableOutput("table2")
#show("table2")
}
})
I've tried doing this by showing and hiding the two tables and can't figure out how to get that to work. I"ve also tried using renderUI as well. What would be the best methodology to go about this?
mainPanel(
tabsetPanel(id = "sim.tabset",
tabPanel(title = "Results",
# tableOutput("table")
DT::dataTableOutput("table"),
DT::dataTableOutput("table2")
),
)
Depending on your app, you can toggle the visibility of the table in the frontend with a little bit of javascript. In the UI, create a button and wrap the dataTableOutput in a generic container.
# some where in your UI
actionButton("toggleTable", "Toggle Table"),
tags$div(
id = "tableContainer",
DT::dataTableOutput("table")
)
...
There are many ways to toggle the visibility of an element (changing the display properties, toggling css classes, modifying other attributes, etc.). The following function toggles the html attribute hidden when the button is clicked. This can be defined in the UI using the tags$script function or loaded from an external javascript file.
const btn = document.getElementById('toggle');
const elem = document.getElementById('tableContainer');
btn.addEventListener('click', function(event) {
if (elem.hasAttribute('hidden')) {
elem.removeAttribute('hidden');
} else {
elem.setAttribute('hidden', '');
}
});
In the server, render the datatable as normal and you can remove the toggling (unless you need additional things to happen when the button is clicked).
Here is the full example.
library(shiny)
shinyApp(
ui = tagList(
tags$main(
id = "main",
tags$h1("Collapsible Table Example"),
actionButton("toggleTable", "Toggle Table"),
tags$div(
id = "tableContainer",
DT::dataTableOutput("table")
)
),
tags$script(
type = "text/javascript",
"
const btn = document.getElementById('toggleTable');
const elem = document.getElementById('tableContainer');
btn.addEventListener('click', function(event) {
if (elem.hasAttribute('hidden')) {
elem.removeAttribute('hidden');
} else {
elem.setAttribute('hidden', '');
}
});
"
)
),
server = function(input, output, session) {
output$table <- DT::renderDataTable({
data.frame(
group = sample(c("A", "B"), 20, replace = TRUE),
x = rnorm(n = 20, mean = 50, sd = 2),
y = rnorm(n = 20, mean = 50, sd = 2)
)
})
}
)
I opted to go with a simple solution, just having one table that renders based on the choice of the radiobuttons. Meaning the if/else is just within the renderDataTable function
library(shiny)
library(DT)
ui <- fluidPage(
radioButtons("Buttons", "CHOOSE!", choices = c("MTCARS", "IRIS")),
DT::dataTableOutput("THETABLE")
)
server <- function(input, output, session) {
output$THETABLE<-DT::renderDataTable({
req(input$Buttons)
if(input$Buttons == "MTCARS") {
DT::datatable(mtcars)
} else {
DT::datatable(iris)
}
})
}
shinyApp(ui, server)
Alternatively, you could use conditional panel, so it shows the table based on the radiobutton selection:
library(shiny)
library(DT)
ui <- fluidPage(
radioButtons("Buttons", "CHOOSE!", choices = c("MTCARS", "IRIS")),
conditionalPanel("input.Buttons == 'MTCARS'",
DT::dataTableOutput("TABLEMTCARS")
),
conditionalPanel("input.Buttons == 'IRIS'",
DT::dataTableOutput("TABLEIRIS"))
)
server <- function(input, output, session) {
output$TABLEMTCARS<-DT::renderDataTable({
DT::datatable(mtcars)
})
output$TABLEIRIS<-DT::renderDataTable({
DT::datatable(iris)
})
}
shinyApp(ui, server)

R Shiny - Dynamically adding dependent inputs using insertUI

The app below contains an actionButton, Add, that inserts two inputs when clicked.
The first input is a selectInput with choices A and B. The second input is a textInput if the user selects A and a numericInput if they select B. I'm not sure how to capture this dependency.
I tried attaching an event handler (the second observeEvent in the code below) to each selectInput that listens to the value of that selectInput and renders the second input accordingly using renderUI. This doesn't work though. I am also wary about using a render function inside an observeEvent since I have read that using a render inside an observer is not good practice (not exactly sure why). It also looks messy.
The app:
library(shiny)
ui <- fluidPage(
actionButton('add', 'Add'),
div(id = 'placeholder')
)
server <- function(input, output, session) {
rv = reactiveValues(ctn = NULL)
observeEvent(input$add, {
rv$ctn = rv$ctn + 1
Id = function(id, ctn = rv$ctn) paste0(id, ctn)
insertUI(
selector = '#placeholder',
ui = div(
id = Id('div'),
selectInput(Id('letter'), 'Letter:', LETTERS[1:2]),
uiOutput(Id('input'))
)
)
observeEvent(Id('letter'), {
output[[Id('input')]] <- renderUI({
switch(
input[[Id('letter')]],
'A' = textInput(Id('text'), '', ''),
'B' = numericInput(Id('numeric'), '', '')
)
})
})
})
}
shinyApp(ui = ui, server = server)
The first UI chunk that is inserted works as expected - both inputs are rendered. However, in subsequent chunks (chunks 2 and 3 in the screenshot below) only the selectInput is rendered.
Here is a screenshot:
Below is a working code. The main problem was your ctn reactive value initialized to NULL, because NULL + 1 = numeric(0) and numeric(0) + 1 = numeric(0).
library(shiny)
ui <- fluidPage(
actionButton('add', 'Add'),
div(id = 'placeholder')
)
server <- function(input, output, session) {
ctn <- reactiveVal(0)
Id <- reactive({
function(id){
paste0(id, ctn())
}
})
observeEvent(input$add, {
ctn(ctn() + 1)
insertUI(
selector = '#placeholder',
ui = div(
id = Id()('div'),
selectInput(Id()('letter'), 'Letter:', LETTERS[1:2]),
uiOutput(Id()('input'))
)
)
})
observeEvent(ctn(), {
id <- Id()('input')
selection <- Id()('letter')
output[[id]] <- renderUI({
req(input[[Id()('letter')]])
switch(
input[[selection]],
'A' = textInput(Id()('text'), 'ENTER TEXT', ''),
'B' = numericInput(Id()('numeric'), 'ENTER NUMBER', '')
)
})
}, ignoreInit = TRUE)
}

delete verbatimtextOutput

ran into this weird issue when teaching a student about shiny programming.
What i wanted was to make code that deletes the verbatimtextOuput element, rather than print an empty value
This is the code he wrote, but it deletes all buttons, the whole UI basically. Can this be done? I know more complex options like conditional panels etc, but just trying to figure out why removeUI doesn't do what I expected here.
Thanks!
app:
library(shiny)
ui<-fluidPage( h5("Hello there"), #First text on the window
br(), #empty line
actionButton(inputId = "ClickonMe", label = "Make data"), # button 1
actionButton(inputId = "ClickonMe2", label = "Print data"), # button 2
actionButton(inputId = "ClickonMe3", label = "Transform data"),
verbatimTextOutput("Response_text") #reactive text output )
server <- function(input,output) {
values <- reactiveValues()
observeEvent(input$ClickonMe,
values$name <- TRUE )
observeEvent(input$ClickonMe3,
if (values$name == TRUE) { values$name <- FALSE}
else { values$name <- TRUE} )
observeEvent(input$ClickonMe2,
if (values$name == TRUE) { output$Response_text <- renderPrint( isolate({values$name}) ) }
else if (values$name == FALSE) { removeUI(
selector = "div:has(> #Response_text)"
)
}
) }
shinyApp(ui, server)
EDIT VERSION:
changed pork chops answer a little so that this version removes and remakes the verbatim element in the ui.
What i now try to fully understand is, is why the piece req(....) has such an impact. the print(values$name) proofs that the variable exist, and the observer sees it, yet if you # the req( ) line, suddenly the app stops recreating the verbatimtextouput after it has been removed the first time.
Hope I can learn why this is the case. Thank you!
library(shiny)
ui <- fluidPage(
h5("Hello there"), # First text on the window
br(), # empty line
actionButton(inputId = "ClickonMe", label = "Make data"), # button 1
actionButton(inputId = "ClickonMe2", label = "Print data"), # button 2
actionButton(inputId = "ClickonMe3", label = "Transform data"),
mainPanel(verbatimTextOutput("Response_text"))
)
server <- function(input,output,session) {
values <- reactiveValues()
values$name <- T
observeEvent(input$ClickonMe,{
values$name <- T
})
observeEvent(input$ClickonMe3,{
if (values$name){ values$name <- F}
else{ values$name <- T }
})
observeEvent(input$ClickonMe2,{
print(values$name)
output$Response_text <- renderPrint({ isolate({
req(values$name)
if(!values$name){
removeUI(
selector = "div:has(> #Response_text)"
)
}else {
as.character(values$name)}
})
})
})
}
1) First of all please have a look at the Google's R Style Guide when writing code and try to stick to it I think both you and your students will benefit from it.
2) Use curly braces too when using functions such as observeEvent and renderPrint
3) Familiarise yourself with req function which is very handy
Sample Code how to remove UI:
library(shiny)
ui <- fluidPage(
h5("Hello there"), # First text on the window
br(), # empty line
actionButton(inputId = "ClickonMe", label = "Make data"), # button 1
actionButton(inputId = "ClickonMe2", label = "Print data"), # button 2
actionButton(inputId = "ClickonMe3", label = "Transform data"),
mainPanel(verbatimTextOutput("Response_text"))
)
server <- function(input,output,session) {
values <- reactiveValues()
values$name <- NULL
observeEvent(input$ClickonMe,{
values$name <- T
})
observeEvent(input$ClickonMe3,{
if (values$name){
values$name <- F}
else{
values$name <- T
}
})
observeEvent(input$ClickonMe2,{
if (values$name){
values$name <- F
}
else{
values$name <- T
}
})
output$Response_text <- renderPrint({
req(values$name)
if(!values$name){
removeUI(
selector = "div:has(> #Response_text)"
)
}
as.character(values$name)})
}
shinyApp(ui, server)

Capture the label of an actionButton once it is clicked

Is it possible to capture the label of an actionButton once it is clicked?
Imagine I have 3 buttons on my ui.R and depending on which one I click I want to perform a different action on the server.R.
One caveat is that the buttons are created dynamically on the server.R with dynamic labels (thus the necessity of capturing the label on click)
Thanks
Something like that ?
library(shiny)
server <- function(input, session, output) {
output$printLabel <- renderPrint({input$btnLabel})
}
ui <- fluidPage(
actionButton("btn1", "Label1",
onclick = "Shiny.setInputValue('btnLabel', this.innerText);"),
actionButton("btn2", "Label2",
onclick = "Shiny.setInputValue('btnLabel', this.innerText);"),
verbatimTextOutput("printLabel")
)
shinyApp(ui = ui, server = server)
1) What button was clicked last by the user?
To answer this you can user observeEvent function and by setting up a a variable using reactiveValues function. Make sure you update your libraries and work in the latest version of R (version 3.1.3) as shiny is dependant on this version. Working on windows you can follow example on how to update here
rm(list = ls())
library(shiny)
ui =fluidPage(
sidebarPanel(
textInput("sample1", "Name1", value = "A"),
textInput("sample2", "Name2", value = "B"),
textInput("sample3", "Name3", value = "C"),
div(style="display:inline-block",uiOutput("my_button1")),
div(style="display:inline-block",uiOutput("my_button2")),
div(style="display:inline-block",uiOutput("my_button3"))),
mainPanel(textOutput("text1"))
)
server = function(input, output, session){
output$my_button1 <- renderUI({actionButton("action1", label = input$sample1)})
output$my_button2 <- renderUI({actionButton("action2", label = input$sample2)})
output$my_button3 <- renderUI({actionButton("action3", label = input$sample3)})
my_clicks <- reactiveValues(data = NULL)
observeEvent(input$action1, {
my_clicks$data <- input$sample1
})
observeEvent(input$action2, {
my_clicks$data <- input$sample2
})
observeEvent(input$action3, {
my_clicks$data <- input$sample3
})
output$text1 <- renderText({
if (is.null(my_clicks$data)) return()
my_clicks$data
})
}
runApp(list(ui = ui, server = server))
2) Save the clicks for further manipulation is below
Here's small example based on the work of jdharrison from Shiny UI: Save the Changes in the Inputs and the shinyStorage package.
rm(list = ls())
#devtools::install_github("johndharrison/shinyStorage")
library(shinyStorage)
library(shiny)
my_clicks <- NULL
ui =fluidPage(
#
addSS(),
sidebarPanel(
textInput("sample_text", "test", value = "0"),
uiOutput("my_button")),
mainPanel(uiOutput("text1"))
)
server = function(input, output, session){
ss <- shinyStore(session = session)
output$my_button <- renderUI({
actionButton("action", label = input$sample_text)
})
observe({
if(!is.null(input$sample_text)){
if(input$sample_text != ""){
ss$set("myVar", input$sample_text)
}
}
})
output$text1 <- renderUI({
input$action
myVar <- ss$get("myVar")
if(is.null(myVar)){
textInput("text1", "You button Name")
}else{
textInput("text1", "You button Name", myVar)
}
})
}
runApp(list(ui = ui, server = server))

Resources