This document compares srv_mdr_scatterplot_simple (no events) with srv_mdr_scatterplot_table (with selection events) to show the pattern for adding interactive plotly selection functionality.
Example: table with filtering
| Feature | Simple Version | Table Version |
|----------------------------|----------------|---------------|
| Event registration | ❌ No | ✅ Yes |
| Event data capture | ❌ No | ✅ Yes |
| Data filtering | ❌ No | ✅ Yes |
| Table rendering | ❌ No | ✅ Yes |
| plot_data access | ❌ No | ✅ Yes |
| Reactive chain | 2 steps | 5 steps |
The table version adds 3 key components:
1. Event registration (plotly::event_register)
2. Event capture (plotly::event_data)
3. Data filtering based on selection
UI differences
The change in UI is quite straight-forward. ui_t_reactables(ns("subtables")) added to display tables.
Table: Filters data based on selected points using:
Extract customdata from plotly selection
Find corresponding rows in plot_data
Get subject_var values from selected rows
Filter tables to only show those subjects
filtered_data_q <-reactive({req(plotly_selected_scatter()) # ← Wait for selection scatter_selected <-plotly_selected_scatter()if (!is.null(scatter_selected)) {# Extract selected values from plot data selected_values <-scatter_q()$plot_data |># ← Access plot_data from scatter_q dplyr::filter(customdata %in% scatter_selected$customdata)# Filter the actual data tablesdata() |>within( {for (table_name in table_datanames) { current_table <-get(table_name) filtered_table <- current_table |> dplyr::filter(subject_var_sym %in% subject_var_selected)assign(table_name, filtered_table) } },table_datanames = table_datanames,subject_var_sym =str2lang(subject_var),subject_var_selected = selected_values[[subject_var]] # ← Filter by subject ) } else {data() # ← Return unfiltered data if no selection }})
Part 6: render tables based on filtered data
KEY DIFFERENCE 4:
Simple: No tables to render
Table: Renders tables that automatically update when filtered_data_q() changes
srv_t_reactables("subtables",data = filtered_data_q, # ← Use filtered data reactivefilter_panel_api = filter_panel_api,datanames = table_datanames,reactable_args = reactable_args)
Flow diagram
SIMPLE VERSION FLOW:
color_inputs() → scatter_q() → output$scatter_plot
(No event handling, no tables)
TABLE VERSION FLOW:
color_inputs() → scatter_q() → output$scatter_plot
↓
plotly_selected_scatter() ← User selects points
↓
filtered_data_q() ← Filter data based on selection
↓
srv_t_reactables() ← Update tables
plotly_selected_scatter <-reactive({ plotly::event_data("plotly_selected", source = session$ns("scatterplot"))# ← Source must match the source in scatterplotly() call})
FILTER DATA based on selection
filtered_data_q <-reactive({req(plotly_selected_scatter()) scatter_selected <-plotly_selected_scatter()if (!is.null(scatter_selected)) {# Extract selected subject IDs selected_values <-scatter_q()$plot_data |> dplyr::filter(customdata %in% scatter_selected$customdata)# Filter your data tablesdata() |>within({# Filter logic here }) } else {data() # Return unfiltered if no selection }})
USE FILTERED DATA in downstream components
srv_t_reactables("subtables",data = filtered_data_q, # ← Use filtered data ...)
Key takeaways
SOURCE IDENTIFIER:
Must match between scatterplotly() and event_data()