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
Consistent Experience: Custom handlers should provide the same experience whether triggered by direct clicks or linking
Error Handling: Wrap custom logic in
tryCatch()
for robustnessPerformance: Keep custom handlers lightweight to avoid UI lag
Visual Feedback: Provide clear visual feedback for user actions
Deselection: Always handle the case where
selected_data
isNULL
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.