1

I'm using event_data like:

clickData = event_data("plotly_clicked", source = paste0("heatmap_", heatmapName))

Is it possible to find what source triggered the event? In my case it is not always known. The elements in clickData are curveNumber, pointNumber, x, and y.

Edit: It seems what is happening is the last plot created in a loop gets treated as the source for all click events from the other plots. Maybe there is something wrong with the logic in my code. Example below: add plots by adding selections and click plots to see what source triggered the click - always the last plot added.

library(shiny)
library(ggplot2)
library(plotly)

ui = fluidPage(
  fluidRow(selectInput("selections", NULL, choices = c("A", "B", "C"), multiple = T)),
  fluidRow(uiOutput("plots"))
)

server = function(input, output, session)
{
  # Add plot rows for each dataset selected
  observe({
    output$plots = renderUI({
      lapply(input$selections, function(name)
      {
          fluidRow(plotlyOutput(paste0(name, "_plot")))
      })
    })
  })
  
  
  # Make plots upon selection
  observe({
    for(name in input$selections)
    {
      p = ggplot(mtcars[, c(3,4)]) + 
        geom_point(aes(x = disp, y = hp))
      
      p_ly = ggplotly(p, source = name)
      
      output[[paste0(name, "_plot")]] = renderPlotly({p_ly})
    }
  })
  
  # Process clicks
  observe({
    for(name in input$selections)
    {
      # Get click data
      clickData = event_data("plotly_click", source = name)
      if(!is.null(clickData))
      {
        # Show click source
        showNotification(paste0("Clicked ", name))
        
        # Set click to NULL
        session$userData$plotlyInputStore[[paste0("plotly_click-", name)]] = NULL
        
        break
      }
    }
  })
}

shinyApp(ui, server)
matt
  • 318
  • 3
  • 11
  • You are defining the `source` plot in your `event_data` call - so why is the source not known? Anyway, you might want to check the [customdata argument](https://stackoverflow.com/a/70110162/9841389). – ismirsehregal May 09 '23 at 07:21
  • Thanks for your comment. The plots are created using `renderUI` in a loop based on the items in `selectInput` and it's possible for example that `event_data("plotly_click", source)$x` and `$y` have the same value for two different plots so can't use that info to discriminate between them. Trying to access the click data by loop through `selectInput`, setting each click to NULL after accessing but no work correct. The simplest solution I can think of would be to access the source of the click directly, if possible. The plot originates from `heatmaply` which doesn't have a `customdata` argument. – matt May 09 '23 at 16:45
  • To me this sounds like a xy problem. Why do you use `renderUI` in the first place? Please provide a proper example reflecting the problem. Using `heatmaply` also results in a `plotly` object. You can add `customdata` to it via `style()`: `fig <- heatmaply(mtcars) |> style(customdata = seq_len(nrow(mtcars)))`. – ismirsehregal May 09 '23 at 20:37
  • Nevertheless, even when the plots are created in a loop the source is not unkown. – ismirsehregal May 09 '23 at 21:01
  • @ismirsehregal, example added. – matt May 09 '23 at 23:06

1 Answers1

1

The issue is caused by the first for-loop / observe in your example code. Furthermore, the following avoids using renderUI (It is faster to manage the plot visibility in the UI function instead of re-rendering on the server side):

library(shiny)
library(ggplot2)
library(plotly)

plot_names <- c("A", "B", "C")

ui <- fluidPage(fluidRow(
  selectizeInput(
    inputId = "selections",
    label = NULL,
    choices = plot_names,
    selected = NULL,
    multiple = TRUE,
    options = list(
      'plugins' = list('remove_button'),
      placeholder = "Please select a plot..."
    )
  )
),
lapply(plot_names, function(name) {
  conditionalPanel(sprintf("input.selections.includes('%s')", name),
                   fluidRow(plotlyOutput(
                     paste0(name, "_plot")
                   )),
                   style = "display:none;")
}))

server <- function(input, output, session)
{
  # render plots once
  lapply(plot_names, function(name) {
    p <- ggplot(mtcars[, c(3, 4)]) + geom_point(aes(x = disp, y = hp))
    p_ly <- ggplotly(p, source = name) |> layout(title = name, margin = list(t = 60, b = 60))
    output[[paste0(name, "_plot")]] <- renderPlotly({
      p_ly
    })
  })
  
  # Process clicks
  observe({
    for (name in input$selections)
    {
      # Get click data
      clickData = event_data("plotly_click", source = name)
      if (!is.null(clickData))
      {
        # Show click source
        showNotification(paste0("Clicked ", name))
        
        # Set click to NULL
        session$userData$plotlyInputStore[[paste0("plotly_click-", name)]] = NULL
      }
    }
  })
}

shinyApp(ui, server)
ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
  • Wonderful. Would you please explain why the observer/for loop in the above example didn't work? – matt May 10 '23 at 15:59
  • 1
    @matt please check [this](https://stackoverflow.com/questions/54327204/shiny-inserting-ui-with-a-for-loop-returns-the-same-elements-in-every-outputs/54331019#54331019) related answer. – ismirsehregal May 10 '23 at 17:44