I need to place a text panel in my shiny app that disappears 10 seconds after it starts (like an advice), anyone know if it is posible? I've tryed to use the command "invalidLater", but it always appear again.
Thanks
Louro, J.
As #bunk is showing a good way is to use invalidateLater, here's some examples:
library(shinyjs)
ui <- shinyUI(
fluidPage(
tags$head(
tags$script(
HTML(
'
Shiny.addCustomMessageHandler("registerTimer", function(message){
console.log("Timer registered for $("+message.selector+")with delay "+message.delay);
setTimeout(removeElementFromDOM, message.delay, message.selector);
});
function removeElementFromDOM(selector){
$("#"+selector).remove();
}
'
)
)
),
uiOutput("ui1"),
textOutput("ui2"),
plotOutput("plt1"),
div(id="txtDiv","Some text here")
)
)
rm(active)
server <- shinyServer(function(input,output, session){
data <- data.frame("x"=runif(10),"y"=runif(10))
txt1 <- "Some text"
makeReactiveBinding('txt1')
makeReactiveBinding('data')
# Remove with javascript
session$sendCustomMessage('registerTimer',
message=list(selector='txtDiv',delay=4000))
# Hide with shinyjs
output$plt1 <- renderPlot({
if (is.null(data)){
hide("plt1")
} else{
plot(x~y,data)
}
})
output$ui2 <- renderText({
txt1
})
# Continously update, output nothing after time
output$ui1 <- renderUI({
invalidateLater(1000, session);
if ((active <<- exists('active'))) return()
div("Text here")
})
# Triggers change
reactiveTimer(2000,{
txt1 <- NULL
})
reactiveTimer(3000,{
data <- NULL
})
})
shinyApp(ui=ui,server=server)
ui1 uses the invalidateLater method, ui2 uses a reactive value that is set to NULL and plt1 is a variation of ui2 where shinyjs is used to hide the plotOutput.
Edited
I've added a Javascript solution to this you can use it on any element of the DOM.
Related
The code at the bottom is taken from an example in https://shiny.rstudio.com/articles/modules.html though I de-modularized it so I can understand something more basic. With this code, each click of the action button renders a counter which counts the number of clicks. Fine.
Instead of counting the number of clicks in the same output of verbatimTextOutput() as the code currently works, I'd like each click to be represented as a new output of verbatimTextOutput(). See illustration below which shows what I'm trying to derive, assuming the user clicks the action button 3 times. I don't know how many times the user will click the action button so there's no way to pre-set or hard-code the number of outputs and assign output names such as output$out1, output$output2, etc. Is there a way to reactively generate the outputs names, as a I naively attempted in the below code with output$"paste(out,count())" and verbatimTextOutput("paste(out,count())") (without the quote marks, I only put them in so the example code would work)? If this is possible this could be a way to achieve the results I am seeking.
Illustration:
Code:
library(shiny)
newOutput <- function(x,y){verbatimTextOutput("paste(out,count())")}
ui <- fluidPage(uiOutput("uiButton"))
server <- function(input,output,session){
count <- reactiveVal(0)
observeEvent(input$button, {count(count() + 1)})
output$"paste(out,count())" <- renderText({count()})
count
output$uiButton <-
renderUI(
tagList(
actionButton("button", label = "Click me"),
newOutput()
)
)
}
shinyApp(ui, server)
This is an alternative approach using insertUI.
Compared to #stefan's renderUI based solution it has the advantage, that the UI elements are rendered only once. Using renderUI every element is re-rendered on button click, accordingly things will slow down depending on the number of elements.
library(shiny)
ui <- fluidPage(
actionButton("add", "Add UI")
)
server <- function(input, output, session) {
observeEvent(input$add, {
output_name <- paste0("out_", input$add)
output[[output_name]] <- renderText({
isolate(input$add)
})
insertUI(
selector = ifelse(input$add == 0L, "#add", paste0("#", "out_", input$add-1)),
where = "afterEnd",
ui = verbatimTextOutput(output_name)
)
}, ignoreNULL = FALSE)
}
shinyApp(ui, server)
Also check ?removeUI.
Adapting this example to dynamically create graphs to your example you could do:
library(shiny)
library(purrr)
newOutput <- function(x) {
verbatimTextOutput(x)
}
ui <- fluidPage(
actionButton("button", label = "Click me"),
uiOutput("uiText")
)
server <- function(input, output, session) {
count <- reactiveVal(0)
observeEvent(input$button, {
count(count() + 1)
i <- count()
output_name <- paste("out", i)
output[[output_name]] <- renderText({
i
})
})
output$uiText <- renderUI({
out_list <- map(seq_len(count()), ~ {
tagList(
newOutput(paste("out", .x))
)
})
tagList(out_list)
})
}
shinyApp(ui, server)
I have a problem with updating a reactive value in shiny.
So what my app basically does, is to save textInputs from the user.
When the user decides to upload all text inputs, I want to reset the textInputs.
Following example code:
ui.R
ui <- fluidPage(
sidebarPanel(
textInput("words", "Please enter a word"),
actionButton("submit_new_word", "Save"), # this submits each single word
textOutput("submitted_new_words"), # this show all submitted words
actionButton("submit_upload", "Upload my Results") # this uploads all submitted words
)
)
server.R
server <- function(input, output, session) {
words_submitted <- paste("") # initial value
w_submitted <- eventReactive(input$submit_new_word, {
words_submitted <- paste(words_submitted, " ", input$words)
words_submitted <<- words_submitted
updateTextInput(session,
inputId = "words",
value = "")
return(words_submitted)
}, ignoreNULL=FALSE)
output$submitted_new_words <- renderText({
w_submitted()
})
observeEvent(input$submit_upload, {
# saveData(data_final) # upload, not needed for example here
words_submitted <<- paste("")
})
}
If you try this minimal example, you will see that the text inputs will be resetted,
but only after the "Save" button is clicked again.
I however would like to have the text inputs to be resetted when the "submit_upload" button is clicked.
Does somebody have an idea?
You probably best to do it with some sort of reactive. The way shiny works is that if there is no reactivity attached to it wont invalidate (refresh) anything on the client side such as renderText
library(shiny)
ui <- fluidPage(
sidebarPanel(
textInput("words", "Please enter a word"),
actionButton("submit_new_word", "Save"), # this submits each single word
textOutput("submitted_new_words"), # this show all submitted words
actionButton("submit_upload", "Upload my Results") # this uploads all submitted words
)
)
v <- reactiveValues()
server <- function(input, output, session) {
v$words_submitted <- paste("") # initial value
observeEvent(input$submit_new_word, {
v$words_submitted <- paste(v$words_submitted, " ", input$words)
updateTextInput(session, inputId = "words",value = "")
}, ignoreNULL=FALSE)
output$submitted_new_words <- renderText({
v$words_submitted
})
observeEvent(input$submit_upload, {
# saveData(data_final) # upload, not needed for example here
v$words_submitted <- paste("")
})
}
# Run the application
shinyApp(ui = ui, server = server)
I'm trying to get an app which updates it output after a set amount of time (i.e. to make text fade away).
In the example code, I would want "Waiting" to display on the actionButton press, then delay for 5 seconds, then the text changes to "Finished".
For some reason the whole observeEvent executes at once, so the outcome is that there is the actionButton is pressed, then there is a 5 second delay with nothing displayed, then "Finished" displays.
Sorry I don't know how to better explain the issue really - but hoping someone can help. If possible I'd like to stick to just R here, without delving too much into javascript.
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
textOutput("text"),
actionButton("press", label = "press")
)
server <- function(input, output) {
num <- reactiveVal()
observeEvent(input$press, {
output$text <- renderText("waiting")
num(1)
})
observe({
if(!is.null(num())){
output$text <- renderText({
Sys.sleep(5)
"finished"
})
}
})
}
shinyApp(ui = ui, server = server)```
You can use the delay() function from shinyjs
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
textOutput("text"),
actionButton("press", label = "press")
)
server <- function(input, output) {
num <- reactiveVal()
observeEvent(input$press, {
output$text <- renderText("waiting")
delay(5000,
output$text <- renderText("finished"))
})
}
shinyApp(ui = ui, server = server)
I am using Plotly's event_data("plotly_click") to do stuff (opening a modal) after the user clicked on a marker in a scatter plot. Afterwards (e.g. closing the modal), event_data("plotly_click") does of course not change and clicking on the same marker therefore does not trigger the same action again.
Minimal example:
library(plotly)
ui <- fluidPage(
plotlyOutput("plot")
)
server <- function(input, output, session) {
output$plot <- renderPlotly({
mtcars %>% plot_ly(x=~disp, y=~cyl)
})
# Do stuff after clicking on a marker in the plot
observeEvent(event_data("plotly_click"), {
print("do some stuff now") # this is not executed after second click on same marker
})
}
shinyApp(ui, server)
I have tried workarounds with shinyjs's onclick, to no avail (it works well in empty areas of the plot but not when clicking on markers):
shinyjs::onclick(id="plot", print("clicked"))
I have also tried using a reactive Value that stores the last click and is reset immediately afterwards (e.g. by event_data("plotly_hover")), but all tries fail because event_data("plotly_click") remains in its old value.
Can anyone help?
[Edit: The issue has been fixed in Plotly 4.9.0. See answer below. This answer works up to Plotly 4.8.0. From plotly 4.9.0., delete the string .clientValue- from the source code or use below answer.]
I finally solved this issue. I know this is bothering some people, so I'll post my solution here:
Basically I use shinyjs package to reset the data about the last click (the source where event_data("plotly_click") gets its information from) on a certain event (a button in my case).
The definition of the function is (note that "A" needs to be replaced with plotly-source string if used):
extendShinyjs(text = "shinyjs.resetClick = function() { Shiny.onInputChange('.clientValue-plotly_click-A', 'null'); }")
Then this is called upon button click by js$resetClick().
Minimal example:
library(shiny)
library(plotly)
library(shinyjs)
ui <- shinyUI(
fluidPage(
useShinyjs(),
# code to reset plotlys event_data("plotly_click", source="A") to NULL -> executed upon action button click
# note that "A" needs to be replaced with plotly source string if used
extendShinyjs(text = "shinyjs.resetClick = function() { Shiny.onInputChange('.clientValue-plotly_click-A', 'null'); }"),
actionButton("reset", "Reset plotly click value"),
plotlyOutput("plot"),
verbatimTextOutput("clickevent")
)
)
server <- shinyServer(function(input, output) {
output$plot <- renderPlotly({
plot_ly(mtcars, x=~cyl, y=~mpg)
})
output$clickevent <- renderPrint({
event_data("plotly_click")
})
observeEvent(input$reset, {
js$resetClick()
})
})
shinyApp(ui, server)
The issue has finally been fixed on Plotly side: https://github.com/ropensci/plotly/issues/1043
event_data("plotly_click", priority = "event") updates on every click, not only on shiny input change (as before). Working from Plotly 4.9.0 on.
Minimal example using Plotly 4.9.0:
library(shiny)
library(plotly)
ui <- shinyUI(
fluidPage(
plotlyOutput("plot", height = 200),
verbatimTextOutput("time_last_click")
)
)
server <- shinyServer(function(input, output) {
output$plot <- renderPlotly({
plot_ly(mtcars[1,], x=~cyl, y=~mpg, size = 1)
})
output$time_last_click <- renderPrint({
tmp <- event_data("plotly_click", priority = "event")
Sys.time()
})
})
shinyApp(ui, server)
I had the same problem, and came up with a solution where I specified the source argument of the plotly object to be a reactive value as follows:
In plot_ly(data,x,y,...,source = x) and event_data(...,source = x) let x be an element of a reactiveValues object. When your event triggers, change the value of x (increment or hash), which instantiates a new event_data() object.
Worked like a charm.
I have a textInput widget, and now whenever I start typing in the widget, shinyApp tries to evaluate the unfinished content in the textInput widget and results in many errors. I'm aware that adding an action Button "Calculate" would easily solve the problem. However, my app does not have space left for one more button. So, I'd like to know if there's a way that the textInput widget would "listen" to a keyboard event, such as when the user hits "Enter?" Thanks in advance!
Very good question. Here is an example of the way I use; this app shows a ggplot and the user gives the title of the ggplot in a textbox - but the title changes reacts only when "Return" is pressed:
js <- '
$(document).on("keyup", function(e) {
if(e.keyCode == 13){
Shiny.onInputChange("keyPressed", Math.random());
}
});
'
shinyApp(
ui = bootstrapPage(
tags$script(js),
textInput("title", label = "Title"),
plotOutput("ggplot")
),
server = function(input, output, session){
Title <- reactiveVal()
observeEvent(input[["keyPressed"]], {
Title(input[["title"]])
})
output[["ggplot"]] <- renderPlot({
ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width)) +
geom_point() +
ggtitle(Title())
})
}
)
Explanations:
This Javascript code:
$(document).on("keyup", function(e) {
if(e.keyCode == 13){
Shiny.onInputChange("keyPressed", Math.random());
}
});
creates a new Shiny input, namely input$keyPressed which receives a random number when the "Return" key is pressed anywhere.
Then I define a reactive value which takes the value input$title given in the textbox by the user, only when input$keyPressed changes:
Title <- reactiveVal()
observeEvent(input[["keyPressed"]], {
Title(input[["title"]])
})
And finally I pass this reactive value to ggtitle:
output[["ggplot"]] <- renderPlot({
ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width)) +
geom_point() +
ggtitle(Title())
})
Here is an app that I built, and solves a similar problem.
The idea is to have listen to both the keypress and the button, and make sure they work together well. In your case, you should be able to make something even simpler because you don't need the button.
I hope you like it.
library(shiny)
# This is a demo app to test a key binding on an actionButton
# Uncommenting the info item (on both UI and server) will display internal stuff
runApp(
list(
#############################################
# UI
#############################################
ui = bootstrapPage(
textInput ("myinput", label = "Write something here"),
tags$script('
$(document).on("keydown", function (e) {
Shiny.onInputChange("lastkeypresscode", e.keyCode);
});
'),
actionButton("GO", "Lancer le matching !"),
# verbatimTextOutput("info"),
verbatimTextOutput("results")
),
#############################################
# SERVER
#############################################
server = function(input, output, session) {
# There are state variables for the input text and GO button
curr.val <- "" # Corresponds to the current displayed input$myinput
curr.go <- 0 # Corresponds to the last known GO value (integer)
lastEvent <- reactive({
# Is reactive to the following events
input$GO
input$lastkeypresscode
# Decide which action should be taken
if(input$GO > curr.go) {
# The user pushed the GO actionButton, so take action
action <- 1
curr.go <<- input$GO
} else if(input$lastkeypresscode == 13) {
# The user pressed the Enter key, so take action
action <- 1
} else {
# The user did anything else, so do nothing
action <- 0
}
return(action)
})
output$results = renderPrint({
if(lastEvent() == 1) {
curr.val <<- isolate(input$myinput)
}
curr.val
})
# output$info = renderText({
# paste(curr.val, curr.go, input$lastkeypresscode, sep = ", ")
# })
}
)
)
I created a simple app as an example, where the user can write the name of a city and after pressing ENTER it returns latitude and longitude:
library(shiny)
library(ggmap)
runApp(
list(
#############################################
# UI
#############################################
ui = fluidPage( title = "City Search" ,
position= "static-top",
tags$script(' $(document).on("keydown", function (e) {
Shiny.onInputChange("lastkeypresscode", e.keyCode);
});
'),
# Search panel:
textInput("search_city", "" , placeholder= "City"),
verbatimTextOutput("results")),
#############################################
# SERVER
#############################################
server = function(input, output, session) {
observe({
if(!is.null(input$lastkeypresscode)) {
if(input$lastkeypresscode == 13){
target_pos = geocode(input$search_city, messaging =FALSE)
LAT = target_pos$lat
LONG = target_pos$lon
if (is.null(input$search_city) || input$search_city == "")
return()
output$results = renderPrint({
sprintf("Longitude: %s ---- Latitude: %s", LONG, LAT)
})
}
}
})
}
)
)
Note that for catching the ENTER input the code is 13, i.e. input$lastkeypresscode == 13.
In your case, the problem is reactive programming and this is the reason that you need something to manage this situation. My recommendation is to use observer pattern or validate function.
Observer pattern: shiny implements the observer pattern which is
useful to act when an event happens in an object (it can be a click
in a button, new value in an input...).
Validate function: the functionality of this process is similar to an
if/else statement. Indeed, there is need what is the if to check the
parameter, if the values are wrong, there will be an error message.
To know how to use observe pattern and the validate function, click on the previous link (in the Shiny website is everything explained).