Shiny Responds to Enter
Asked Answered
N

5

17

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!

Nasia answered 14/7, 2015 at 19:10 Comment(11)
https://mcmap.net/q/547290/-r-shiny-key-input-bindingOberammergau
Glad I could be helpful!Oberammergau
@MillerZhu her name is JimboFauver
@AndyYao By the time I commented he was still Eugene...Is the earth still self-spinning in 24 hrs?Nasia
@MillerZhu look at the sky. How many moons are there???Fauver
@AndyYao There's only a tiger's tail pointing to the left...Nasia
@MillerZhu Is your question solved?Bobolink
@Bobolink Not yet. Do you have a solution? Thanks!Nasia
@MillerZhu Not but I have different possibilities, I will try some of them and I answer this question for you and the community.Bobolink
@Bobolink Thank you! I look forward to it!Nasia
@MillerZhu Do you see my answer? Tell if you get it or not.Bobolink
B
-1

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).

Bobolink answered 10/9, 2015 at 17:36 Comment(0)
K
17

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())
})
Kohlrabi answered 28/9, 2017 at 17:59 Comment(0)
A
4

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 = ", ")
      # })
    }
  )
)
Amoritta answered 17/11, 2016 at 10:48 Comment(0)
D
3

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.

Denominative answered 19/7, 2017 at 10:24 Comment(0)
P
0

In 2024, we can use ShinyWidget::searchInput to achieve the same effect without any custom js.

Presswork answered 10/8 at 15:54 Comment(0)
B
-1

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).

Bobolink answered 10/9, 2015 at 17:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.