Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

event_data not working for Shiny Modules? #659

Closed
happyshows opened this issue Jul 27, 2016 · 17 comments
Closed

event_data not working for Shiny Modules? #659

happyshows opened this issue Jul 27, 2016 · 17 comments

Comments

@happyshows
Copy link

happyshows commented Jul 27, 2016

Hi,

I tried a couple combinations but couldn't get it to work, please advise:

`
library(shiny)
library(shinyBS)
library(plotly)

Module.testUI <- function(id){
ns <- NS(id)
plotlyOutput(ns('ply_main'))
}

Module.test <- function(input, output, session, sess) {
ns <- session$ns

output$ply_main <- renderPlotly({
df1 <- data.frame(x = 1:10, y = 1:10)
plot_ly(df1, x = ~x, y = ~y) %>% add_markers()
})

observe({
# eventdata <- event_data(ns('plotly_click'))
eventdata <- event_data('plotly_click',source = ns('ply_main'))
# eventdata <- event_data('plotly_click')
isolate({
print(eventdata)
})
})
}

ui <- fluidPage(
tagList(
Module.testUI('test'),
Module.testUI('test2')
)
)

server <- function(input, output) {

callModule(Module.test,'test', sess)

callModule(Module.test,'test2', sess)
}

shinyApp(ui = ui, server = server)
`

@royr2
Copy link

royr2 commented Aug 6, 2016

I have the same issue but its interesting to note that an event_data() call outside the module function seems to work fine and works for multiple plotly charts simultaneously.

Here's an example -

library(plotly)
library(shiny)

plotlyUI <- function(id){
  ns <- NS(id)
  plotlyOutput(ns("plotlyout"))
}

plotlyModule <- function(input, output, session){
  output$plotlyout <- renderPlotly(
    plot_ly(x = 1:100, y = (1:100), mode = "lines + markers")
  )
}

ui <- fluidPage(
  plotlyUI("plotlytest1"),
  plotlyUI("plotlytest2"),
  verbatimTextOutput("text")
)

server <- function(input, output, session){
  callModule(plotlyModule, "plotlytest1")
  callModule(plotlyModule, "plotlytest2")

  output$text <- renderPrint(
    event_data("plotly_click")
  )
}

shinyApp(ui, server)

@happyshows
Copy link
Author

I think such behavior is actually very dangerous for modular design.
It even works across modules.

library(plotly)
library(shiny)

plotlyUI <- function(id){
ns <- NS(id)
plotlyOutput(ns("plotlyout"))
}

plotlyModule <- function(input, output, session){
output$plotlyout <- renderPlotly(
plot_ly(x = 1:100, y = (1:100), mode = "lines + markers")
)
}

plotlyUI2 <- function(id){
ns <- NS(id)
plotlyOutput(ns("plotlyout"))
}

plotlyModule2 <- function(input, output, session){
output$plotlyout <- renderPlotly(
plot_ly(x = 1:100, y = (1:100), mode = "lines + markers")
)
}

ui <- fluidPage(
plotlyUI("plotlytest1"),
plotlyUI2("plotlytest2"),
verbatimTextOutput("text")
)

server <- function(input, output, session){
callModule(plotlyModule, "plotlytest1")
callModule(plotlyModule2, "plotlytest2")

output$text <- renderPrint(
event_data("plotly_click")
)
}

shinyApp(ui, server)

@charleyferrari
Copy link

charleyferrari commented Aug 9, 2016

Outside of allowing event_data() to work within a module, here's a way to keep it outside the module, reuse a module, and attach event_data() to only one of the modules.

The source argument in plot_ly() is what ties the event_data() function to a particular graph. I just reworked module.test() to add the source argument only in a particular namespace.


library(shiny)
library(shinyBS)
library(plotly)

Module.testUI <- function(id){
  ns <- NS(id)
  fluidPage(
    plotlyOutput(ns('ply_main'))#,
    #verbatimTextOutput(ns('textmain'))
    #verbatimTextOutput('textmain')
  )
}

Module.test <- function(input, output, session, sess) {
  ns <- session$ns

  if(ns(NULL) == 'test'){
    output$ply_main <- renderPlotly({
      df1 <- data.frame(x = 1:10, y = 1:10)
      plot_ly(df1, x = ~x, y = ~y, source='listenhere') %>% add_markers()
    })
  } else{
    output$ply_main <- renderPlotly({
      df1 <- data.frame(x = 1:10, y = 1:10)
      plot_ly(df1, x = ~x, y = ~y) %>% add_markers()
    })
  }
}

ui <- fluidPage(
  tagList(
    Module.testUI('test'),
    Module.testUI('test2'),
    verbatimTextOutput('textmain')
  )
)

server <- function(input, output) {

  callModule(Module.test,'test', sess)

  callModule(Module.test,'test2', sess)

  output$textmain <- renderPrint({
    d <- event_data('plotly_click', source='listenhere')
    if(is.null(d)) 'To be populated' else d
  })
}

shinyApp(ui = ui, server = server)

@happyshows
Copy link
Author

@charleyferrari my expectation of using the event_data is to be inside the module. This is because I need the interaction to exist strictly inside that module. My goal is to develop hundreds of modules and not have to worry about naming collision and the source of the event_data

@cpsievert
Copy link
Collaborator

Supporting this would require non-trivial changes to event_data(), and it's quite possible that event_data() will be deprecated after #554 is merged since crosstalk will provide a more "official" API for accessing JS events in a shiny app. Here is an example.

@happyshows
Copy link
Author

@cpsievert , could you confirm once crosstalk is more mature it would work in module namespace?
I think module and crosstalk are two directions, one focus on encapsulation while the other focus on collaboration.

My use case for using plotly in modules is:
each module can be ran as independent app, but launched from the same 'launchpad' or landing page, each module will have it's own ETL & visualization and some level of interaction (inside the same module) via event_data. The interaction could be as simple as grabbing the target value or as complex as your example above.
The main point here is module developer should not be worried by risk of name collision,
Worst case, the following code should resides in modules not server.R
output$textmain <- renderPrint({
d <- event_data('plotly_click', source='listenhere')
if(is.null(d)) 'To be populated' else d
})
without this happening, there are two consequences:

  1. the launch pad will have to contain the event_data logic for all modules, which is really inefficient and cause problem to scale up.
  2. inside each module, developer will have to pass in the event data via global session variable so that the next step of interaction could proceed, it's creating another chance for name collision.

Please let me know if I need to clarify further.

@cpsievert
Copy link
Collaborator

could you confirm once crosstalk is more mature it would work in module namespace?

I can't speak for the package author, but considering crosstalk and shiny have the same author, it is very likely ;)

Feel free to experiment by installing the pull request and perusing the examples (as a word of warning though, things will change, but will also become more stable over the coming weeks).

devtools::install_github("rstudio/crosstalk")
devtools::install_github("ropensci/plotly#554")

@happyshows
Copy link
Author

I just wrapped the shiny example into the module.
It seems that the subplot works fine, but interaction between subplot & DT table got disconnected
the d$selection() was not updated in the renderDataTable

library(plotly)
library(DT)
library(shiny)
library(crosstalk)

m <- mtcars[, c("mpg", "wt", "disp")] %>% 
  tibble::rownames_to_column()

Module.testUI <- function(id){
  ns <- NS(id)
  fluidPage(
    plotlyOutput(ns("plots")),
    DT::dataTableOutput(ns("table"))
  )
}

ui <- fluidPage(
  Module.testUI('test')
)

Module.test <- function(input, output, session) {
  ns <- session$ns
  d <- SharedData$new(m, ~rowname)

  output$plots <- renderPlotly({
    subplot(
      qplot(data = d, x = mpg, y = wt),
      qplot(data = d, x = mpg, y = disp),
      titleX = T, titleY = T, margin = 0.03
    )
  })

  output$table <- DT::renderDataTable({
    m2 <- m[d$selection(),]
    dt <- DT::datatable(m)
    if (NROW(m2) == 0) {
      dt
    } else {
      DT::formatStyle(dt, "rowname", target = "row",
                      color = DT::styleEqual(m2$rowname, rep("white", length(m2$rowname))),
                      backgroundColor = DT::styleEqual(m2$rowname, rep("black", length(m2$rowname))))
    }
  })
}

server <- function(input, output) {
  callModule(Module.test,'test')
}

shinyApp(ui = ui, server = server)

@happyshows
Copy link
Author

I'll try to reach Joe on this topic and update you guys later.

@sudburyrob
Copy link

@happyshows -- Did you receive any feedback from Joe on this? We are trying to get this resolved for you and I want to make sure I have all relevant info as to the status.

@happyshows
Copy link
Author

Hi Rob,

Here's the latest comment I received from Joe:
"
OK, there's not a problem with crosstalk and modules per se, but Plotly's crosstalk integration is a little wonky right now (which Carson realizes and understands that he needs to work on). I wouldn't expect the crosstalk- and Shiny event-related APIs in Plotly to stabilize for at least a couple of months, if not longer. But when that happens I would expect this problem to go away, it's basically Plotly's "source" parameter that doesn't really hold up well in a crosstalk and/or modules context, let alone both.

I'm a bit bummed at how long it will take the community to align around crosstalk, even for just rbokeh and plotly, but this is delicate work that requires a bunch of people to share the same mental model, which hasn't quite happened yet.
"

@sudburyrob
Copy link

@happyshows do you have any availability today for a call? I am trying to get one our engineers involved to make sure we are doing everything we can to help you get what you need

@cpsievert
Copy link
Collaborator

cpsievert commented Aug 31, 2016

expectation of using the event_data is to be inside the module

@happyshows rstudio/shiny#1343 should give me the tools I need to make this possible. Until that happens, this would be my suggested way to accessing (namespaced) event data:

library(shiny)
library(plotly)

reusableUI <- function(id = NULL) {
  ns <- NS(id)

  fluidPage(
    plotlyOutput(ns("p")),
    verbatimTextOutput(ns("ev"))
  )
}

reusableScatter <- function(input, output, session, source, event) {
  output$p <- renderPlotly({
    plot_ly(mtcars, x = ~mpg, y = ~disp, 
            key = row.names(mtcars), source = source) %>% add_markers()
  })
  output$ev <- renderPrint({
    event()
  })
}

ui <- fluidPage(
  reusableUI("myLargeApp")
)

server <- function(input, output, session) {
  hover <- reactive(event_data("plotly_hover", source = "A"))
  callModule(
    reusableScatter, "myLargeApp", session = session, source = "A", event = hover
  )
}

shinyApp(ui, server)

@happyshows
Copy link
Author

thanks for the follow up. I'll watch that thread and see how things go. Hopefully the new solution will work with my 'nested module' design - that's also why I prefer not to use the source approach, as each module may be called multiple times with different parameters in the same panel.

You also mentioned earlier about deprecating the event_data, will that still be the goal once the rstudio/shiny#1343 is completed?
My impression on crosstalk is that it's a great vehicle for interaction like linked brushing with many constraints. However, if people simply wants to grab a clicked / hovered value and feed to some widgets outside of plotly, what's the alternative approach in crosstalk for simple features provided by event_data?

@cpsievert
Copy link
Collaborator

cpsievert commented Sep 1, 2016

No, event_data() won't be going away (mainly because there might be scenarios where you want to access event data from a plot that was created without a data frame), and I've actually already added back support in #554 . See #700 for details as to why rstudio/shiny#1343 is needed to get event_data() working within modules.

@sudburyrob
Copy link

@charleyferrari ^^ how does this relate to what Shows at AMD brought up today?

@happyshows
Copy link
Author

The latest dev release closes this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants