Skip to contents

Customizing Click Behaviors

While linkeR provides sensible defaults, you often want custom behaviors when users click on linked components. This vignette shows how to create rich, interactive experiences.

Custom Leaflet Click Handler

You can define exactly what happens when a leaflet marker is clicked (either directly or via linking):

server <- function(input, output, session) {
  
  business_data <- reactive({
    # Your business data here
  })
  
  output$business_map <- renderLeaflet({
    # Render your map (no popup needed - custom handler will create it)
    leaflet(business_data()) %>%
      addTiles() %>%
      addCircleMarkers(
        lng = ~longitude,
        lat = ~latitude,
        layerId = ~business_id,
        radius = ~sqrt(revenue/1000) + 3
      )
  })
  
  output$business_table <- renderDT({
    # Your table rendering code
  })
  
  # Link with custom leaflet behavior
  link_plots(
    session,
    business_map = business_data,
    business_table = business_data,
    shared_id_column = "business_id",
    
    # Custom handler for leaflet clicks
    leaflet_click_handler = function(map_proxy, selected_data, session) {
      if (!is.null(selected_data)) {
        # Create rich popup
        popup_content <- paste0(
          "<div style='min-width: 200px;'>",
          "<h4 style='color: #2c3e50;'>", selected_data$name, "</h4>",
          "<p><strong>Revenue:</strong> $", format(selected_data$revenue, big.mark = ","), "</p>",
          "<p><strong>Category:</strong> ", selected_data$category, "</p>",
          "<p><strong>Rating:</strong> ", selected_data$rating, "/5.0 ⭐</p>",
          "</div>"
        )
        
        # Custom zoom and popup
        map_proxy %>%
          leaflet::setView(
            lng = selected_data$longitude, 
            lat = selected_data$latitude, 
            zoom = 15  # Custom zoom level
          ) %>%
          leaflet::clearPopups() %>%
          leaflet::addPopups(
            lng = selected_data$longitude,
            lat = selected_data$latitude,
            popup = popup_content
          )
      } else {
        # Handle deselection
        map_proxy %>% leaflet::clearPopups()
      }
    }
  )
}

Custom DT Click Handler

Similarly, you can customize table selection behavior:

link_plots(
  session,
  business_map = business_data,
  business_table = business_data,
  shared_id_column = "business_id",
  
  # Custom handler for table clicks
  dt_click_handler = function(dt_proxy, selected_data, session) {
    if (!is.null(selected_data)) {
      # Find the row and select it
      current_data <- business_data()
      row_idx <- which(current_data$business_id == selected_data$business_id)
      
      if (length(row_idx) > 0) {
        # Select and scroll to row
        DT::selectRows(dt_proxy, selected = row_idx[1])
        
        # Optional: scroll to the row
        page_size <- 10  # Your page size
        target_page <- ceiling(row_idx[1] / page_size)
        DT::selectPage(dt_proxy, target_page)
        
        # Show additional info
        showNotification(
          paste("Selected:", selected_data$name, "- Revenue: $", 
                format(selected_data$revenue, big.mark = ",")),
          type = "message",
          duration = 3
        )
      }
    } else {
      # Handle deselection
      DT::selectRows(dt_proxy, selected = integer(0))
    }
  }
)

Global Selection Callback

You can also add a callback that fires whenever any selection changes:

link_plots(
  session,
  business_map = business_data,
  business_table = business_data,
  shared_id_column = "business_id",
  
  # Global callback for all selection changes
  on_selection_change = function(selected_id, selected_data, source_id, session) {
    if (!is.null(selected_data)) {
      # Update other UI elements
      output$selected_business_info <- renderText({
        paste0(
          "Selected: ", selected_data$name, "\n",
          "Source: ", source_id, "\n",
          "Revenue: $", format(selected_data$revenue, big.mark = ",")
        )
      })
      
      # Log for analytics
      cat("Selection event:", selected_id, "from", source_id, "\n")
      
      # Update other reactive values
      current_selection(selected_data)
    } else {
      # Handle deselection
      output$selected_business_info <- renderText("No selection")
      current_selection(NULL)
    }
  }
)

Best Practices

  1. Consistent Experience: Custom handlers should provide the same experience whether triggered by direct clicks or linking

  2. Error Handling: Wrap custom logic in tryCatch() for robustness

  3. Performance: Keep custom handlers lightweight to avoid UI lag

  4. Visual Feedback: Provide clear visual feedback for user actions

  5. Deselection: Always handle the case where selected_data is NULL

Advanced Example: Conditional Behavior

leaflet_click_handler = function(map_proxy, selected_data, session) {
  if (!is.null(selected_data)) {
    # Different behavior based on data properties
    if (selected_data$risk_level == "High") {
      # Show warning for high-risk items
      showModal(modalDialog(
        title = "High Risk Alert",
        paste("This location has high risk level:", selected_data$name),
        easyClose = TRUE
      ))
    }
    
    # Color popup based on risk
    popup_color <- switch(selected_data$risk_level,
      "Low" = "#d4edda",
      "Medium" = "#fff3cd",
      "High" = "#f8d7da"
    )
    
    popup_content <- paste0(
      "<div style='background-color: ", popup_color, "; padding: 10px; border-radius: 5px;'>",
      "<h4>", selected_data$name, "</h4>",
      "<p>Risk Level: ", selected_data$risk_level, "</p>",
      "</div>"
    )
    
    map_proxy %>%
      leaflet::setView(lng = selected_data$longitude, lat = selected_data$latitude, zoom = 13) %>%
      leaflet::clearPopups() %>%
      leaflet::addPopups(
        lng = selected_data$longitude,
        lat = selected_data$latitude,
        popup = popup_content
      )
  }
}

This approach lets you create rich, context-aware interactions that respond intelligently to your data.