Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shiny Responds to Enter

Tags:

r

shiny

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!

like image 279
Miller Zhu Avatar asked Jul 14 '15 19:07

Miller Zhu


2 Answers

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())
})
like image 191
Stéphane Laurent Avatar answered Sep 29 '22 14:09

Stéphane Laurent


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 = ", ")
      # })
    }
  )
)
like image 23
VeilleData Avatar answered Sep 29 '22 13:09

VeilleData