I was wondering if it is possible to create a popup dialog box interactive by using shiny (and shinyBS).
For example, I have a string and I want to change it and before doing a dialog box shows up asking if I really want to change it. In case I say "yes", it does it otherwise it discards the change. Here's my try but I found two issues: 1. if you click "yes" or "no", nothing changes 2. you always need to close the box by the bottom "close".
rm(list = ls())
library(shiny)
library(shinyBS)
name <- "myname"
ui =fluidPage(
textOutput("curName"),
br(),
textInput("newName", "Name of variable:", name),
br(),
actionButton("BUTnew", "Change"),
bsModal("modalnew", "Change name", "BUTnew", size = "small",
textOutput("textnew"),
actionButton("BUTyes", "Yes"),
actionButton("BUTno", "No")
)
)
server = function(input, output, session) {
output$curName <- renderText({paste0("Current name: ", name)})
observeEvent(input$BUTnew, {
output$textnew <- renderText({paste0("Do you want to change the name?")})
})
observeEvent(input$BUTyes, {
name <- input$newName
})
}
runApp(list(ui = ui, server = server))
Other proposals are more than welcome!!
Here's a proposition, I mainly changed the server.R:
library(shiny)
library(shinyBS)
ui =fluidPage(
textOutput("curName"),
br(),
textInput("newName", "Name of variable:", "myname"),
br(),
actionButton("BUTnew", "Change"),
bsModal("modalnew", "Change name", "BUTnew", size = "small",
HTML("Do you want to change the name?"),
actionButton("BUTyes", "Yes"),
actionButton("BUTno", "No")
)
)
server = function(input, output, session) {
values <- reactiveValues()
values$name <- "myname";
output$curName <- renderText({
paste0("Current name: ", values$name)
})
observeEvent(input$BUTyes, {
toggleModal(session, "modalnew", toggle = "close")
values$name <- input$newName
})
observeEvent(input$BUTno, {
toggleModal(session, "modalnew", toggle = "close")
updateTextInput(session, "newName", value=values$name)
})
}
runApp(list(ui = ui, server = server))
A couple of points:
I created a reactiveValues to hold the name that the person currently has. This is useful because you can then update or not update this value when the person clicks on the modal button. You can access the name using values$name.
You can use toggleModal to close the modal once the user has clicked on yes or no
#NicE provided a nice solution. I'm going to offer an alternative solution using the shinyalert package instead, which I believe results in easier to understand code (disclaimer: I wrote that package so may be biased).
The main difference is that the modal creation is no longer in the UI and is now done on the server when the button is clicked. The modal uses a callback function to determine if "yes" or "no" were clicked.
library(shiny)
library(shinyalert)
ui <- fluidPage(
useShinyalert(),
textOutput("curName"),
br(),
textInput("newName", "Name of variable:", "myname"),
br(),
actionButton("BUTnew", "Change")
)
server = function(input, output, session) {
values <- reactiveValues()
values$name <- "myname"
output$curName <- renderText({
paste0("Current name: ", values$name)
})
observeEvent(input$BUTnew, {
shinyalert("Change name", "Do you want to change the name?",
confirmButtonText = "Yes", showCancelButton = TRUE,
cancelButtonText = "No", callbackR = modalCallback)
})
modalCallback <- function(value) {
if (value == TRUE) {
values$name <- input$newName
} else {
updateTextInput(session, "newName", value=values$name)
}
}
}
runApp(list(ui = ui, server = server))
You can do something like this using conditionalPanel, I would further suggest adding a button to ask for confirmation oppose to instant update.
rm(list = ls())
library(shiny)
library(shinyBS)
name <- "myname"
ui = fluidPage(
uiOutput("curName"),
br(),
actionButton("BUTnew", "Change"),
bsModal("modalnew", "Change name", "BUTnew", size = "small",
textOutput("textnew"),
radioButtons("change_name", "", choices = list("Yes" = 1, "No" = 2, "I dont know" = 3),selected = 2),
conditionalPanel(condition = "input.change_name == '1'",textInput("new_name", "Enter New Name:", ""))
)
)
)
server = function(input, output, session) {
output$curName <- renderUI({textInput("my_name", "Current name: ", name)})
observeEvent(input$BUTnew, {
output$textnew <- renderText({paste0("Do you want to change the name?")})
})
observe({
input$BUTnew
if(input$change_name == '1'){
if(input$new_name != ""){
output$curName <- renderUI({textInput("my_name", "Current name: ", input$new_name)})
}
else{
output$curName <- renderUI({textInput("my_name", "Current name: ", name)})
}
}
})
}
runApp(list(ui = ui, server = server))
Related
Good days, I am programming in Rstudio, using shiny, and I wanted to generate an alert that is activated only when I want to leave a tabPanel without completing a condition, but not if I do not enter the tabPanel before, this is the way I found. The problem is that every time that I leave the Panel 1 without fulfilling the condition of completing text, alerts are generated that are accumulating (1 alert the first time, two the second, three the third, etc.) I wanted to consult if somebody knows why it is this and how to avoid it.
thank you very much
library(shiny)
library(ggplot2)
library(shinyalert)
ui <- fluidPage(
tabsetPanel(
id = "tabselected",
tabPanel("Tab2",""),
tabPanel("Tab1", textInput("requiredText", "Required Text"))
))
server <- function(input, output, session) {
observe({
req(input$tabselected == "Tab1")
observeEvent(
input$tabselected,
if (input$tabselected != "Tab1" & !isTruthy(input$requiredText)) {
shinyalert(title = "Save your work before changing tab",
type = "warning",
showConfirmButton = TRUE
)
updateTabsetPanel(session, inputId = "tabselected", selected = "Tab1")
}
)
}
)
}
shinyApp(ui = ui, server = server)
Is this the behavior you desire? Your example was recursive so you had reoccurring popup event. We can create a reactiveValues variable to keep track of the events, like so:
library(shiny)
library(ggplot2)
library(shinyalert)
ui <- fluidPage(
tabsetPanel(
id = "tabselected",
tabPanel("Tab2",""),
tabPanel("Tab1", textInput("requiredText", "Required Text"))
))
server <- function(input, output, session) {
v <- reactiveValues(to_alert = FALSE)
observeEvent(input$tabselected,{
if (input$tabselected != "Tab1" & !isTruthy(input$requiredText)) {
v$to_alert <- TRUE
}else{
v$to_alert <- FALSE
}
},ignoreInit = TRUE)
observeEvent(v$to_alert,{
if (v$to_alert){
shinyalert(title = "Save your work before changing tab", type = "warning",showConfirmButton = TRUE)
updateTabsetPanel(session, inputId = "tabselected", selected = "Tab1")
}
})
}
shinyApp(ui = ui, server = server)
I have the following app which allows for text to be entered and it is then saved as VALUE and printed on a panel.
Although it looks like I can only do this with one text input at a time - even if I click add (so I don't believe this button is working). On top of that I would like for the user to be able to add multiple inputs (like I have below).
And then my VALUE function should be list with multiple inputs.
code below
library(shiny)
ui <- fluidPage(
headerPanel("R Package App"),
sidebarPanel(
# selectInput("options", "options", choices=c('abc','def')),
textInput("textbox", "Enter R Package Name", ""),
actionButton("add","Add")
),
mainPanel(
textOutput("caption")
)
)
server <- function(input, output, session) {
observe({
VALUE <- ''
if(input$add>0) {
isolate({
VALUE <- input$textbox
})
}
updateTextInput(session, inputId = "textbox", value = VALUE)
})
output$caption <- renderText({
input$textbox
})
}
shinyApp(ui = ui, server = server)
Have you considered using selectizeInput with it's create option?
library(shiny)
packagesDF <- as.data.frame(installed.packages())
ui <- fluidPage(
headerPanel("R Package App"),
sidebarPanel(
selectizeInput(
inputId = "selectedPackages",
label = "Enter R Package Name",
choices = packagesDF$Package,
selected = NULL,
multiple = TRUE,
width = "100%",
options = list(
'plugins' = list('remove_button'),
'create' = TRUE,
'persist' = TRUE
)
)
),
mainPanel(textOutput("caption"))
)
server <- function(input, output, session) {
output$caption <- renderText({
paste0(input$selectedPackages, collapse = ", ")
})
}
shinyApp(ui = ui, server = server)
I am trying to implement something similar to this within the app and not at the browser level as described here.
After capturing the value of the new tab (tabPanel value) selected, could not display the confirmation message before switching to the newly selected tab to display its content.
library(shiny)
library(ggplot2)
library(shinyalert)
ui <- fluidPage(useShinyalert(),
tabsetPanel(id = "tabselected",
tabPanel("Tab1"),
tabPanel("Tab2",plotOutput("plot"))
)
)
server <- function(input, output) {
observeEvent(input$tabselected, {
if(input$tabselected == "Tab2")
{
shinyalert(title = "Save your work before changing tab", type = "warning", showConfirmButton = TRUE)
output$plot <- renderPlot({ ggplot(mtcars)+geom_abline() })
}
})
}
shinyApp(ui = ui, server = server)
You can simply redirect to Tab1 via updateTabsetPanel as long as your desired condition is met.
Here is an example requiring the user to type something in the textInput before it's allowed to switch the tab.
library(shiny)
library(ggplot2)
library(shinyalert)
ui <- fluidPage(useShinyalert(),
tabsetPanel(
id = "tabselected",
tabPanel("Tab1", p(), textInput("requiredText", "Required Text")),
tabPanel("Tab2", p(), plotOutput("plot"))
))
server <- function(input, output, session) {
observeEvent(input$tabselected, {
if (input$tabselected == "Tab2" && !isTruthy(input$requiredText)) {
updateTabsetPanel(session, inputId = "tabselected", selected = "Tab1")
shinyalert(title = "Save your work before changing tab",
type = "warning",
showConfirmButton = TRUE)
output$plot <- renderPlot({
ggplot(mtcars) + geom_abline() + ggtitle(req(input$requiredText))
})
}
})
}
shinyApp(ui = ui, server = server)
By the way an alternative approach wpuld be using showTab and hideTab to display the tabs only if all conditions are fulfilled.
I am having some trouble getting the functionality in my app that I'm looking for because of the way observeEvent works (which is normally very intuitive).
The basic functionality I'm looking for is that a user can input a couple numbers, click "submit", and then a modal pops up to take the user's name. After that, the app records the name and sums the numbers, and then clears the inputs. Then I'd like the user to be able repeat the process using the same name - but the app currently is structured so that the sums use an observeEvent that responds only when the name is different (i.e., using the same name twice in a row doesn't work, though I'd like it to). You can see in the app that my attempt at a solution is to reset the input for the inputSweetAlert (using shinyjs), but it can't access it, I assume because it's not actually on the UI side. I am using shinyWidgets sweetAlerts, which I'd like to continue doing.
Here's an example app:
library("shiny")
library("shinyWidgets")
library("shinyjs")
ui <- fluidPage(
shinyjs::useShinyjs(),
numericInput("num1", "Enter a number", value=NULL),
numericInput("num2", "Enter another number", value=NULL),
actionButton(inputId = "go", label = "submit"),
verbatimTextOutput(outputId = "res1"),
verbatimTextOutput(outputId = "res2")
)
server <- function(input, output, session) {
observeEvent(input$go, {
inputSweetAlert(session = session, inputId = "name", title = "What's your name?")
})
x <- reactiveValues(val=NULL)
observeEvent(input$name, {
x$val <- input$num1 + input$num2
confirmSweetAlert(session = session, inputId = "confirmed", title = "Success!", text = "Your responses have been recorded. All is in order.", type = "success", btn_labels = c("Ok, let me continue")
)
})
## A possible approach to a solution...
observeEvent(input$confirmed, {
shinyjs::reset("num1")
shinyjs::reset("num2")
shinyjs::reset("name")
})
output$res1 <- renderPrint(paste("Name:", input$name))
output$res2 <- renderPrint(paste("Sum:", x$val))
}
shinyApp(ui = ui, server = server)
Thanks for any help you can provide!
You could reset input$name via JS:
runjs('Shiny.setInputValue("name", null, {priority: "event"});')
Here is a working example:
library("shiny")
library("shinyWidgets")
library("shinyjs")
ui <- fluidPage(
shinyjs::useShinyjs(),
numericInput("num1", "Enter a number", value = NULL),
numericInput("num2", "Enter another number", value = NULL),
actionButton(inputId = "go", label = "submit"),
verbatimTextOutput(outputId = "res1"),
verbatimTextOutput(outputId = "res2")
)
server <- function(input, output, session) {
observeEvent(input$go, {
inputSweetAlert(session = session, inputId = "name", title = "What's your name?")
runjs('Shiny.setInputValue("name", null, {priority: "event"});')
})
x <- reactiveValues(val = NULL)
observeEvent(input$name, {
x$val <- input$num1 + input$num2
confirmSweetAlert(session = session, inputId = "confirmed", title = "Success!", text = "Your responses have been recorded. All is in order.", type = "success", btn_labels = c("Ok, let me continue"))
})
observeEvent(input$confirmed, {
shinyjs::reset("num1")
shinyjs::reset("num2")
shinyjs::reset("mytext")
})
output$res1 <- renderPrint(paste("Name:", input$name))
output$res2 <- renderPrint(paste("Sum:", x$val))
}
shinyApp(ui = ui, server = server)
For further information please see this article.
EDIT: In apps using modules, the call to runjs() can be adapted like this in order to namespace the id:
runjs(paste0("Shiny.setInputValue(\"", ns("name"), "\", null, {priority: \"event\"});"))
Here is a workaround. The idea consists in changing the input id at each click on the button.
library("shiny")
library("shinyWidgets")
library("shinyjs")
ui <- fluidPage(
shinyjs::useShinyjs(),
numericInput("num1", "Enter a number", value=NULL),
numericInput("num2", "Enter another number", value=NULL),
actionButton(inputId = "go", label = "submit"),
verbatimTextOutput(outputId = "res1"),
verbatimTextOutput(outputId = "res2")
)
server <- function(input, output, session) {
go <- reactive({
input$go
})
observeEvent(input$go, {
inputSweetAlert(session = session, inputId = sprintf("name-%d", go()),
title = "What's your name?")
})
x <- reactiveValues(val=NULL)
observeEvent(input[[sprintf("name-%d", go())]], {
x$val <- input$num1 + input$num2
confirmSweetAlert(session = session, inputId = "confirmed", title = "Success!", text = "Your responses have been recorded. All is in order.", type = "success", btn_labels = c("Ok, let me continue")
)
})
## A possible approach to a solution...
observeEvent(input$confirmed, {
shinyjs::reset("num1")
shinyjs::reset("num2")
shinyjs::reset("mytext")
})
output$res1 <- renderPrint(paste("Name:", input[[sprintf("name-%d", go())]]))
output$res2 <- renderPrint(paste("Sum:", x$val))
}
shinyApp(ui = ui, server = server)
I want to add the input text to a vector in a Shiny app every time a button is clicked. This is the example I'm working with:
library(shiny)
ui <- fluidPage(
textInput(inputId = "inquiry", label = "enter text"),
actionButton(inputId = "searchButton", label = "Run"),
verbatimTextOutput("queryList", placeholder = FALSE)
)
server <- function(input, output, session) {
queryList <- c()
observeEvent(input$searchButton, {
queryList[length(queryList)+1] <- input$inquiry
output$queryList <- renderPrint({
queryList
})
})
}
shinyApp(ui = ui, server = server)
So if "item1" is entered and the button is clicked, then "item2" is entered and the button is clicked again, queryList should look like c("item1", "item2"), but it seems to just be replacing "item1" with "item2". I'm sure I'm missing something very simple...queryList[length(queryList)+1] looks a little strange, but it works in a non-reactive environment.
Making queryList reactive fixed it for me:
library(shiny)
ui <- fluidPage(
textInput(inputId = "inquiry", label = "enter text"),
actionButton(inputId = "searchButton", label = "Run"),
verbatimTextOutput("queryList", placeholder = FALSE)
)
server <- function(input, output, session) {
queryList <- reactiveValues()
queryList$values <- c()
observeEvent(input$searchButton, {
queryList$values[length(queryList$values) + 1] <- input$inquiry
})
output$queryList <- renderPrint({
if (!is.null(queryList$values)) {
queryList$values
}
})
}
shinyApp(ui = ui, server = server)