3

With the Shiny selectizeInput widget, the user can type in text as well as select a value from a list of values. Is there a way in R to read the current value of the text?

(Added) I should make it clear that I want to be able to read the text the user enters before he has made a selection. As zimia points out, after he has made a selection, the value of whatever he selected becomes available as input$input_id (assuming that the selectize input has the id "input_id").

Daryl McCullough
  • 303
  • 5
  • 20

3 Answers3

2

you can just use the input$input_id. See below

ui <- fluidRow(
  selectizeInput("input1","Enter Text",choices=c("A","B") ,options = list(create=TRUE)),
  textOutput("output1")
)

server<- function(input, output, session){
  output$output1 <- renderText({
    req(input$input1)
    input$input1
  })
}
zimia
  • 930
  • 3
  • 16
  • No, that does not give any value until the user has made a selection. So, for example, if the user types in "C" in the selectizeInput textbox, until he actually chooses to "add" that value, input$input1 will be empty. – Daryl McCullough Feb 13 '21 at 13:35
  • 2
    If that is what you want then you should be use a `textInput`. `selectizeInput` will be constained by "add". You can add a value in the `textInput()` function if you want there to be a certain choice at the start. Can you provide a little bit of context on what you are actually trying to achieve from both a UI and server point of view, as in what will the input value be used for? – zimia Feb 13 '21 at 14:08
  • 2
    The particular application I have in mind looks at the user's text input, and then computes a list of completions, and creates a dropdown menu for those completions. I don't want to give a complete list of all possible choices, because in my application, that could literally be millions of possibilities. So my approach is to wait until the user has typed a certain number of characters, then compute completions based on those characters, and add them to the list of selectizeInput choices. – Daryl McCullough Feb 13 '21 at 14:19
2

Shiny's selectizeInput still gives access to the internal properties, methods and callbacks of Selectize.js through "options" argument of updateSelectizeInput. Selectize.js exposes callback "onType", so you can use like that:

updateSelectizeInput(
  session,
  'input1',
  options = list(
    onType = I('
      text => Shiny.setInputValue("input1_searchText", text)
    '),
    onBlur = I('
      () => Shiny.setInputValue("input1_searchText", null)
    ')
  )
)

and consume it with observer:

observeEvent(input$input1_searchText, {
  print(input$input1_searchText)
  }, ignoreNULL = FALSE
)

Please, note, that I was also interested to know when the select looses focus and hence used "onBlur" event to set the search text to NULL.

1

I was thinking that there would be a solution at the level of R/shiny. But I have found a solution that uses javascript.

    library(shiny)
    js <- '
      $(document).on("keyup", function(e) {
        minChars = 4;
        tag1 = document.activeElement.getAttribute("id");
        val1 = document.activeElement.value;
        if (tag1 == "input1-selectized") {
          if (Math.sign(val1.length +1 - minChars) == 1) {
            obj = { "val": val1 };
            Shiny.onInputChange("valueEntered", obj);
          }
        }
      });
'
      ui <- fluidRow(
        tags$script(js),
        selectizeInput(
          "input1", 
          "Enter text", 
          choices = c("A", "B"), 
          options = list(create = FALSE)),
        textOutput("output1")
      )

      server <- function(input, output, session) {
        output$output1 <- renderText({
          req(input$valueEntered$val)
          input$valueEntered$val
        })
      }
  
      shinyApp(ui = ui, server = server)

The javascript looks for text entered into the element with id "input1-selectized", and if the value is length 4 or more, sets the shiny variable "valueEntered" to an object with val = the text.

Then the observeEvent uses the shiny variable input$valueEntered to set the output text.

The reason in the javascript for using the Math.sign function rather than >, and the reason for nested if's is because for some reason, Shiny replaces infix operators such as > and && by their html equivalents, > and &&.

Note that if the selectizeInput widget has id "input1", then the corresponding text field has id "input1-selectized".

Daryl McCullough
  • 303
  • 5
  • 20
  • 2
    You can fix the issue you had with the javascript being misinterpreted by using `tags$script(HTML(js))` – pseudospin Feb 14 '21 at 17:15
  • that's almost perfect ... the only thing is, even if you clear the input, the value of input$valueEntered$val does not change. Is there any way round that? I think it would make more sense if this input reflected the actual text in the selectize rather than its history? – Steve Powell Mar 22 '22 at 14:47