Newb question on text input to Shiny app

How can I modify the Shiny text input example:

https://shiny.rstudio.com/reference/shiny/1.1.0/textInput.html

so that input$value can be passed as a character-string input to a function within the server?

I'm writing a Shiny app in which I want the user to be able to input an address like "3 Main Street, Anytown NY" and pass the address to mapping software that requires a character string format for the address. Sorry for spamming the forum with a question that is almost certainly very basic. I did look through the example apps and didn't see anything that addressed my issue...

I'm not sure what problem you have encountered, but textInput passes a character string to the server. You can see that in the Widget Gallery.

The textInput example is as follows:

## Only run examples in interactive R sessions
if (interactive()) {

ui <- fluidPage(
  textInput("caption", "Caption", "Data Summary"),
  verbatimTextOutput("value")
)
server <- function(input, output) {
  output$value <- renderText({ input$caption })
}
shinyApp(ui, server)
}

Suppose that I want to populate the ui textbox with "That's a long string" if the input string has more than 4 characters and with "That's a short string" if the input string has 4 or fewer characters. How do I modify the server function to accomplish this? A few failed attempts are:

 server <- function(input, output) {
    if (nchar(renderText({input$caption})) > 4) {
    output$value <- "That's a long string"
    } else {
      output$value <- "That's a short string"
    }
  }

and

 server <- function(input, output) {
    if (nchar{input$caption) > 4) {
    output$value <- renderText({"That's a long string"})
    } else {
      output$value <- renderText({"That's a short string"})
    }
  }

But these just show that I don't know what I'm doing and am missing some basic understanding. I somehow need to be able to grab the current value (as a text string) of the reactive object that populates a textInput field - and I don't know how to do it :-<

Does this do what you expect?

if (interactive()) {
  
  ui <- fluidPage(
    textInput("caption", "Caption", "Data Summary"),
    verbatimTextOutput("value")
  )
  
  server <- function(input, output) {
    output$value <- renderText({
      if (nchar(input$caption) > 4) {
        out = "That's a long string"
      } else {
        out = "That's a short string"
      }
      return(out)
    })
  }
  shinyApp(ui, server)
}

You can't have two outputs with the same name (like in your 2nd example).

1 Like

Thanks, Travis. That does what I was trying to do in the toy example I concocted to home in on my problem. But I do see why your example is correct, so now let me see if I can get the actual app working :smiley: Thanks again for your help.

I really recommend working through one of the shiny tutorials, whether video-style or written-style. They manage to hit on a lot of important conceptual topics in an efficient and coherent way. Iā€™m usually the type who loses patience with tutorials and prefers to jump in and start bashing my way through, but these tutorials are (in my opinion) totally worth it.

3 Likes

Thanks jcblum. I will go through the tutorials more carefully than I have.

But, upon thinking about and looking more at Travis' working code, I still don't see my issue addressed: I want to have the ui display driven by a user-specified address. So the user needs a text box to type in something like "1600 Pennsylvania Ave, Washington, DC", I need that text to be programmatically accessible as a character string input to a mapping function in my server. As Travis showed me, it seems that I'm allowed to access that input only within a renderText function, which can only be used for ui output.

Very concretely, the following code snippet works fine (if you have a google maps api code):

library(shiny)
library(googleway)
load("~/apikey.RData")

ui <- fluidPage(google_mapOutput("map"))
server <- function(input, output, session){
  api_key <- apikey
  location = "1600 Pennsylvania Ave NW, Washington, DC 20500"
  df = google_geocode(location,key=apikey)
  output$map <- renderGoogle_map({
    google_map(key = api_key, location = as.numeric(df$results$geometry$location))
  })
}
shinyApp(ui, server)

How do I replace the hard-coded location string above with user-input from a text box? Is it possible within Shiny?

You can update what is displayed within a textInput with updateTextInput. You will have to be careful with your logic to not get stuck in a reactivity loop when using the value of textInput to then update the value of textInput. This logic has obviously been worked out for web forms, but I'm not familiar with how it would work in Shiny.

I may be wrong, but it seems to me that all you want is your output$map function to use the value of input$caption in the call to google_map?

If so, just put all the code in your server function inside the output$map reactive:

library(shiny)
library(googleway)
load("~/apikey.RData")

ui <- fluidPage(
   textInput("caption", "Caption", "Data Summary"),
   google_mapOutput("map")
)

server <- function(input, output, session){
  output$map <- renderGoogle_map({
    api_key <- apikey
    location = input$caption ## you may want to check that input$caption contains a valid value
    df = google_geocode(location,key=apikey)
    google_map(key = api_key, location = as.numeric(df$results$geometry$location))
  })
}
shinyApp(ui, server)

Cheers
Steen

2 Likes

To follow up on this bit...

The elements in input are reactive values, so they can only be accessed within a "reactive context" ā€” meaning a function that knows how to handle them. In practice, this can be any of the renderSomething() functions, among other options. Note that the curly braces that you conventionally see inside render*() functions let you include as many lines of code as you want (as you can see in @stkrog's solution!). Whatever the last line returns is the thing that will be supplied to your output.

(This is all covered in Part II of the shiny video tutorial! How to Start Shiny (Complete) on Vimeo)

Having everything inside a render* function means that all of those lines will run every time there is any change to any of the inputs. Sometimes that's not what you want, often because some of the steps are slow(er) ā€” that's when people usually turn to an intermediate reactive expression. You can learn more about that approach in Step 6 of the shiny written tutorial and in greater depth starting with this section of Part II of the video tutorial.

In your case, your app could do the geocoding inside a separate reactive expression and supply that result to renderGoogle_map(). That wouldn't make sense in this simple example where there's only one input, but it might make sense if you had more inputs and you expected your user to enter a location to generate the initial map, then fiddle with the other inputs to adjust what the map looks like. Moving the geocoding to a separate reactive expression would be more efficient in that case, since the geocoding step wouldn't have to be needlessly re-run just because some other aspect of the map display changed.

Thank you, all. stkrog : Yes, you are right. This was exactly the point that I was missing. I didn't realize at the time that entire code blocks can be made reactive by enclosing them in a render function. And jcblum, your kind but firm nudges toward the well-crafted tutorials are noted. I'm diving in... :smiley:

1 Like