rshinyleafletr-leaflet

What is the equivalent of onclick for textInput in R Shiny leaflet popup?


I'm working on a Shiny app with a leaflet map that allows users to change names of points using popups. The idea is that you can click on a point, see the name and edit its textInput. The problem is that I don't know how to make the name reactive. I know how this works with an actionButton (using onclick) but I don't know how to create an event when the textInput is edited.

Here's a basic reprex with 1 point and a popup with an actionButton (it works: if you click the observeEvent button_EditName will be triggered) and a textInput field (but that does not trigger any thing because onclick or onChange are not parameters of textInput).

library(shiny)
library(leaflet)
library(sf)

ui <- fluidPage(
  leafletOutput("Leaf"),
  textOutput("test")
)

server <- function(input, output, session) {
 
  test <- reactiveVal("")
  
  ### Init
  Pts <- st_as_sf(data.frame(Lon=1, Lat=2, ID="Pt1", Popup=NA), coords = c("Lon","Lat"), crs=st_crs(4326))
  
  Pts$Popup <- as.character(paste0(
    actionButton(paste0("EditNameButton_", Pts$ID[1]),
                 label="Click",
                 onclick="Shiny.onInputChange(\"button_EditName\" , this.id)"
    ),
    "<br>",
    textInput(paste0("EditName_", Pts$ID[1]),
              label="",
              value=Pts$ID
    )
  ))
  
  Leaf <- leaflet() %>%
    addTiles() %>%
    addMarkers(data=Pts, popup=Pts$Popup)
  
  ### Events
  observeEvent(input$button_EditName, {
    print(input$button_EditName)
    test("The button was clicked")
  })
  
  ### Outputs
  output$Leaf <- renderLeaflet(Leaf)
  output$test <- renderText(test())
}

shinyApp(ui = ui, server = server)

Solution

  • Because you transform textInput directly as text and use it inside popup, you can't just observe the onchange event input[[paste0("EditName_", Pts$ID[1])]] like you would normally. So you can write a helper function textIn to add this onchange behaviour manually. Here are some usual behaviours:

    observer occurs when
    onblur an HTML element loses focus = when you click or tab out of it
    onchange the value of an HTML element is changed - also when you are typing and then hitting enter
    oninput an element gets input. = each keystroke
    library(shiny)
    library(leaflet)
    library(sf)
    
    ui <- fluidPage(
      leafletOutput("Leaf"),
      textOutput("test")
    )
    
    textIn <- \(text_input_id) {
        textInput(
          paste0("EditName_", text_input_id),
          label = "Edit name:",
          placeholder = text_input_id
        ) |>
        sub(
          'type="text"',
          'type="text" onchange="Shiny.onInputChange(\'text_onchange\', {id: this.id, value: this.value})"',
          x = _)
    }
    
    server <- function(input, output, session) {
      
      test <- reactiveVal("")
      text <- reactiveVal("")
      
      ### Init
      Pts <- st_as_sf(data.frame(Lon=1, Lat=2, ID="Pt1", Popup=NA), coords = c("Lon","Lat"), crs=st_crs(4326))
      
      Pts$Popup <- as.character(paste0(
        actionButton(paste0("EditNameButton_", Pts$ID[1]),
                     label="Click",
                     onclick="Shiny.onInputChange(\"button_EditName\" , this.id)"
        ),
        "<br>",
        textIn(Pts$ID[1])
        )
      )
      
      Leaf <- leaflet() %>%
        addTiles() %>%
        addMarkers(data=Pts, popup=Pts$Popup)
      
      ### Events
      observeEvent(input$button_EditName, {
        print(input$button_EditName)
        test("The button was clicked")
      })
      
      observeEvent(input$text_onchange, {
        e <- input$text_onchange
        print(e)
        test(paste(e$id, "was changed to: ", e$value))
      })
      
      ### Outputs
      output$Leaf <- renderLeaflet(Leaf)
      output$test <- renderText(test())
    }
    
    shinyApp(ui = ui, server = server)
    

    res