Shiny : how to reset selectInput or observeEvent? - r

When I open a modalDialog (by clicking a "modal" button) with a selectInput inside it, the first choice of the list is displayed and the observeEvent is automaticaly launched with this first element. That's ok.
If I choose a second element of the list, the observeEvent is launched again and that's right.
I "cancel" or "dismiss" the modalDialog and I open it again by clicking "modal" button in the main window.
The first choice of the list is displayed again and the observeEvent is automaticaly launched with this first element. That's ok.
Now I "cancel" or "dismiss" this modalDialog, without choosing any element of the selectInput.
If I open the modalDialog again, nothing happens BECAUSE the first choice is the same when I closed the modelDialog before so the observeEvent didn't detect any change so it doesn't launch the action.
Is there a way to RESET the selectInput or the observeEvent in order to "forget" which element was previously selected when the modalDialog is closed ? This MUST works even if the selectInput contain only one choice.
I tried to add an actionButton in the footer of the modalDialog in order to launch an action while closing it : it updates the selectInput with a fake value but I don't think it's a good way...
Here is a reproducible code, I set the selectInput with only one choice...
Remove the # in front of the updateSelectInput to test my workaround.
Thanx !
library(shiny)
ui <- fluidPage(
actionButton("open", "Modal")
)
server <- function(input, output, session) {
observeEvent(input$open, {
showModal(
modalDialog(
tagList(
div(id="choice",
selectInput(
"dateList", "History:",
choices = c("choice1")
)
)
)
,
footer = tagList(
actionButton("cancel","CANCEL"),
actionButton("save", "SAVE")
)
)
)
})
observeEvent( input$dateList,{
insertUI("#choice", ui=div("hello"))
})
observeEvent( input$cancel,{
removeModal()
#updateSelectInput(session, "dateList" , choices = c("fake"), selected = NULL)
})
}
shinyApp(ui, server)

Interesting. Here is a way which works but I don't fully like it, because it generates a JavaScript warning. But no worry, this warning is harmless.
The idea is to use a reactive value which reacts to both input$datelist and input$cancel.
reacVal <- eventReactive(list(input$dateList, input$cancel), {
runif(1)
})
observeEvent(reacVal(), {
insertUI("#choice", ui = div("hello"))
})
observeEvent(input$cancel, {
removeModal()
})
The warning is due to the facet that there is no div #choice when the cancel button is pressed.
EDIT
Here is a better way. No warning and clearer. I "reset" input$dateList by setting it to NULL when the cancel button is clicked, using some JavaScript in the onclick attribute of this button.
observeEvent(input$open, {
showModal(
modalDialog(
tagList(
div(id="choice",
selectInput(
"dateList", "History:",
choices = c("choice1", "choice2")
)
)
)
,
footer = tagList(
actionButton(
"cancel", "CANCEL",
onclick = "Shiny.setInputValue('dateList', null);"
),
actionButton("save", "SAVE")
)
)
)
})
observeEvent(input$dateList, {
insertUI("#choice", ui = div("hello"))
})
observeEvent(input$cancel, {
removeModal()
})
I choose NULL because the observer does not react to NULL (by default), that's why the previous warning does not occur here.

Related

Why is my remove UI function in R shiny not working?

I've reviewed similar posts and havenĀ“t found any that address this specific need. Below is very simple MWE of what I'm trying to do: shown in 2 images, and in runnable code. My "Hide" button (or remove UI) doesn't work. Help!! I'm sure it's a simple solution but I'm new to this.
What I'm trying to do: Click on the "Add" button and a file input prompt appears below. Click "Hide" button and the file input prompt goes away. Click "Add" again (after "Hide") and the file input prompt appears again. If you click "Add" now (and repeatedly), that single file input prompt remains. (Most other posts have the object appearing repeatedly, again and again in a growing column, with every additional click of the button - this isn't what I need). Just one click to make it appear (and clicking "Add" over and over just keeps it there in its original single manifestation), and "Hide" makes it go away. Simple as that.
Images:
library(shiny)
ui <- fluidPage(
h2("Testing showing and hiding of a function in UI ..."),
br(),
h3(actionButton("addBtn", "Add")),
h3(actionButton("hideBtn","Hide")),
uiOutput("FileInput"),
) # close fluid page
server <- function(input, output, session) {
output$FileInput <- renderUI({
"txt"
req(input$addBtn)
tagList(fileInput("file1", "Choose file",multiple= FALSE,
accept=c("csv",
"comma-separated-values",
".csv"), # close c
width=250,
buttonLabel = "select one file",
placeholder = "Add file"
), # close file input
)} # close tag list
)} # close render UI
observeEvent(input$hideBtn, {
removeUI(
selector = "div:has(> #txt)")
}) # close observe event
shinyApp(ui, server)
Perhaps you can use shinyjs package to get the desired result. Try this
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
h2("Testing showing and hiding of a function in UI ..."),
br(),
h3(actionButton("addBtn", "Add")),
h3(actionButton("hideBtn","Hide")),
uiOutput("FileInput"),
) # close fluid page
server <- function(input, output, session) {
output$FileInput <- renderUI({
"txt"
req(input$addBtn)
tagList(fileInput("file1", "Choose file",multiple= FALSE,
accept=c("csv",
"comma-separated-values",
".csv"), # close c
width=250,
buttonLabel = "select one file",
placeholder = "Add file"
) # close file input
) # close tag list
}) # close render UI
observeEvent(input$addBtn, {
shinyjs::show("FileInput")
})
observeEvent(input$hideBtn, {
shinyjs::hide("FileInput")
#removeUI(selector = "div:has(> #txt)")
})
}
shinyApp(ui, server)
If you do not want to use shinyjs package, you can use insertUI and removeUI as shown below.
library(shiny)
ui <- fluidPage(
h2("Testing showing and hiding of a function in UI ..."),
br(),
h3(actionButton("addBtn", "Add")),
h3(actionButton("hideBtn","Hide")),
uiOutput("FileInput"),
) # close fluid page
server <- function(input, output, session) {
output$FileInput <- renderUI({
req(input$addBtn)
tagList(tags$div(id = 'placeholder1')
) # close tag list
}) # close render UI
observeEvent(input$addBtn, {
if (input$addBtn==0){return(NULL)
}else {
insertUI(
selector = '#placeholder1' ,
## wrap element in a div with id for ease of removal
ui = tags$div(id="fi",
div(style="display: inline-block; width: 185px ;" ,
fileInput("file1", "Choose file",multiple= FALSE,
accept=c("csv",
"comma-separated-values",
".csv"), # close c
width=250,
buttonLabel = "select one file",
placeholder = "Add file"
) # close file input
))
)
}
})
observeEvent(input$hideBtn, {
if (input$hideBtn==0){return(NULL)
}else {
removeUI(selector = "div:has(> #fi)")
}
})
}
shinyApp(ui, server)

Is it possible for reset (actionButton) and submitButton to work independently in Shiny app?

I have a reset (actionButton) and update button (submitButton) in my Shiny app. The problem is that to reset the app, I have to click on the reset button followed by the update button. Is it possible to reset the app without having to click on update?
EDIT: I do want the app to update only after the user explicitly clicks update. This is because in my app they will have the option to select several selectors to filter the data. Happy to use something else other than submitbutton, but so far this has been the only function that worked for the purpose.
In the example below, I have to click on update twice to get the whole app to reset :
library(shiny)
shinyApp(
ui = basicPage(
numericInput("num", label = "Make changes", value = 1),
submitButton("Update", icon("refresh")),
shinyjs::useShinyjs(),
actionButton("reset", "Reset"),
helpText(
"When you click the button above, you should see",
"the output below update to reflect the value you",
"entered at the top:"
),
verbatimTextOutput("value")
),
server = function(input, output) {
# submit buttons do not have a value of their own,
# they control when the app accesses values of other widgets.
# input$num is the value of the number widget.
output$value <- renderPrint({
input$num
})
observeEvent(input$reset, {
shinyjs::reset("num")
})
}
)
I hope someone can enlighten me!
Perhaps actionButton in combination with updateNumericInput() will meet your needs. Try this
library(shiny)
shinyApp(
ui = basicPage(
numericInput("num", label = "Make changes", value = 1),
actionButton("Update", "refresh"),
shinyjs::useShinyjs(),
actionButton("reset", "Reset"),
helpText(
"When you click the button above, you should see",
"the output below update to reflect the value you",
"entered at the top:"
),
verbatimTextOutput("value")
),
server = function(input, output,session) {
# submit buttons do not have a value of their own,
# they control when the app accesses values of other widgets.
# input$num is the value of the number widget.
observeEvent(input$Update, {
output$value <- renderPrint({
isolate(input$num)
})
})
observeEvent(input$reset, {
#shinyjs::reset("num")
updateNumericInput(session,"num",value=1)
})
}
)

R Shiny toggle text of actionLink

I am trying to do something which I thought would be relatively simple, but I cannot seem to figure it out.
I am attempting to have an actionLink which, when pressed, provides additional information for the user. When pressed again it hides the information. I can do this fine, but what I am struggling with is updating the text of the actionLink.
I want it to read "Show additional" when the extra information is hidden, and then "Hide additional" when the information is exposed. I have read the following questions/answers, but cannot quite get it to work.
Modify shiny action button once it is clicked
Update label of actionButton in shiny
I have provided a simple code for this below, though the real example will be a lot more complex.
Thank you for your time and help.
shinyApp(
ui = shinyUI(fluidPage(useShinyjs(),
actionLink("button", "Show additional"),
hidden(
div(id='text_div',
verbatimTextOutput("text")
)
)
)
),
server = function(input, output, session){
observeEvent(input$button, {
toggle('text_div')
output$text <- renderText({"Additional"})
})
}
)
You can check for the value of input$button(which increments by 1 each time you click on it) and update the actionLink label parameter in function of its value with updateActionButton :
shinyApp(
ui = shinyUI(
fluidPage(useShinyjs(),
actionLink("button", "Show additional"),
hidden(div(id='text_div', verbatimTextOutput("text")))
)
),
server = function(input, output, session){
observeEvent(input$button, {
toggle('text_div')
output$text <- renderText({"Additional"})
if (input$button %% 2 == 1) {
txt <- "Hide Additional"
} else {
txt <- "Show Additional"
}
updateActionButton(session, "button", label = txt)
})
}
)

Disabling buttons in Shiny

I am writing some Shiny code where the user will enter some inputs to the app and then click a an action button. The action button triggers a bunch of simulations to run that take a long time so I want once the action button is clicked for it to be disabled so that the user can't keep clicking it until the simulations are run. I came across the shinyjs::enable and shinyjs::disable functions but have been having a hard time utilizing them. Here is my server code:
output$button1= renderUI({
if(input$Button1 > 0) {
shinyjs::disable("Button1")
tableOutput("table")
shinyjs::enable("Button1")}
})
However, when I use this code, and click the action button nothing happens. I.e., teh action button doesn't grey out nor does the table get generated. However, when I take away the shinyjs::enable() command, i.e.,
output$button1= renderUI({
if(input$Button1 > 0) {
shinyjs::disable("Button1")
tableOutput("table")
}
})
The table gets generated first, and then the button goes grey, however I would have expected the button to go grey and then the table to generate itself.
What am I doing wrong here?
Here is my updated code based on Geovany's suggestion yet it still doesn't work for me
Button1Ready <- reactiveValues(ok = FALSE)
observeEvent(input$Button1, {
shinyjs::disable("Button1")
RunButton1Ready$ok <- FALSE
RunButton1Ready$ok <- TRUE
})
output$SumUI1= renderUI({
if(Button1Ready$ok){
tableOutput("table")
shinyjs::enable("Button1")
}
})
where for clarification I have also:
output$table <- renderTable({
#My code....
)}
I think that you are using shinyjs::disable and shinyjs::enable in the same reactive function. You will only see the last effect. I will recommend you to split in different reactive functions the disable/enable and use an extra reactive variable to control the reactivation of the button.
I don't know how exactly your code is, but in the code below the main idea is illustrated.
library(shiny)
library(shinyjs)
ui <- fluidPage(
shinyjs::useShinyjs(),
sidebarLayout(
sidebarPanel(
actionButton("Button1", "Run"),
shinyjs::hidden(p(id = "text1", "Processing..."))
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output) {
plotReady <- reactiveValues(ok = FALSE)
observeEvent(input$Button1, {
shinyjs::disable("Button1")
shinyjs::show("text1")
plotReady$ok <- FALSE
# do some cool and complex stuff
Sys.sleep(2)
plotReady$ok <- TRUE
})
output$plot <-renderPlot({
if (plotReady$ok) {
shinyjs::enable("Button1")
shinyjs::hide("text1")
hist(rnorm(100, 4, 1),breaks = 50)
}
})
}
shinyApp(ui, server)

shiny checkbox unchecks - cross-referring to inputs in same renderUI call

I intended to have some optional extra buttons appearing in my shiny app using renderUI. I would prefer to have new buttons inserted in the one and same renderUI call. I wrote a renderUI expession were the second button only is rendered if input of fist button is not null and(&&) is TRUE. It does not work as first button rapidly unchecks itself again whenever checked. I made a solution by splitting into two renderUI calls.
My question are:
Why exactly does the first code piece fail?
Would it be possible to achieve what the second code piece does in only one renderUI call?
.
library(shiny)
ui = fluidPage(
uiOutput("checkUI")
)
server = function(input,output) {
output$checkUI = renderUI({
list(
checkboxInput('check1',"check me first"),
if(!is.null(input$check1)&&input$check1==T) {
checkboxInput('check2',"check me to win")
} else {
NULL
}
)
})
}
shinyApp(ui,server)
but this works...
ui = fluidPage(
uiOutput("checkUI"),
uiOutput("textUI")
)
server = function(input,output) {
#UI first button
output$checkUI = renderUI({
list(
checkboxInput('check1',"check me first")
)
})
#UI second button
output$textUI = renderUI({
list(
if(!is.null(input$check1) && input$check1)
checkboxInput('check2',"check me to win") else NULL
)
})
}
shinyApp(ui,server)
Your first piece of code fails because the renderUI runs every time there is a change in input, so when you check the first check box. Since you have:
checkboxInput('check1',"check me first")
The renderUI immediately resets the input$check1 and then the renderUI is run again, unsetting the checkbox. This is why the second checkbox briefly flashes.
Luckily this is a common problem, so there is a Shiny solution to this with conditionalPanels:
library(shiny)
ui = fluidPage(
checkboxInput('check1',"check me first"),
conditionalPanel(
condition = "input.check1 == true",
checkboxInput("check2", "check me to win")
)
)
server = function(input,output){
}
shinyApp(ui,server)

Resources