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!
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).
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 2024, we can use ShinyWidget::searchInput to achieve the same effect without any custom js.
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).
© 2022 - 2024 — McMap. All rights reserved.