0
votes

community.rstudio cross-post here

I am working with an HTML widget from the development version of visNetwork.

I would like to set an event listener on one of the buttons in that widget. However, just adding that event listener in $(document).ready(function() doesn't work because the button does not exist until after the Shiny server renders the widget output.

I'll add three sections below with my attempted solutions, a reprex, and a more detailed explanation of my problem. Thanks! :)

Attempted Solution

I've tried defining a setEventListener function (in a string), and then running js$startEventListener in the server. Then the question is: where in the server would I put that?

It would need to run right after the visNetwork render function (renderVisNetwork). If I put it in renderVisNetwork, it runs too soon (the button doesn't exist until right after renderVisNetwork finishes running). I tried making a dummy input value that renderVisNetwork increments (and then using observeEvent to run js$startEventListener each time renderVisNetwork terminates), but that somehow put me in an infinite loop, and seemed a bit hacky anyways.

I also thought this google groups post might help, but I tried the session$onFlushed solution and it didn't have any effect.

Reprex

library(shiny)
library(visNetwork) 
# Note: requires devel visNetwork. `devtools::install_github("datastorm-open/visNetwork")`

ui <- fluidPage(
  tags$head(
    tags$script(
      type = "text/javascript",
      '
      $(document).ready(function() {
      
      // Positive control: an alert for a button
      $("#clickme").click(
        function() {
            alert("Here is an alert!");
        });
        
      // This is what I am having trouble with
      $("#editedge-saveButton").click(
        function() {
          alert("Thanks for saving!")
        });
  
      })
      '
    )
  ),
  actionButton("clickme", "Click me for an alert!"),
  visNetworkOutput("mygraph")
)

server <- function(input, output, session) {
  output$mygraph <- renderVisNetwork(
    visNetwork(
      nodes = data.frame(id = "A", label = "A"),
      edges = data.frame(to = "A", from = "A", label = "A to A")
    ) %>%
      visOptions(manipulation = list(
        enabled = TRUE,
        editEdgeCols = c("label")
      ))
  )
}

shinyApp(ui, server)

More details

In visNetwork, you can "edit" an edge, like so:

I want to call a function whenever the user presses the "save" button.

That button has an id:

enter image description here

I can try to add an event listener to that id like so:

$("#editedge-saveButton").click(
    function() {
        alert("Thanks for saving!")
    });

But it doesn't work, even though a similar event listener for a test button does work:

Note that, if I add that event listener in the console after I load the app, it works just fine:

This meshes with my idea that there's nothing wrong with my event listener code, it's just that the visNetwork widget doesn't exist at the time that the main JS code is executed.

1

1 Answers

1
votes

Here is the classical way to deal with such a situation:

$("body").on("click", "#editedge-saveButton", 
  function() {
    alert("Thanks for saving!")
  }
);

In this way the click event handler is attached to the body element, but it is triggered only when the click occurs on the "#editedge-saveButton" element.

Another way specific to htmlwidgets consists in using the onRender function:

visNetwork(
  nodes = data.frame(id = "A", label = "A"),
  edges = data.frame(to = "A", from = "A", label = "A to A")
) %>%
  visOptions(manipulation = list(
    enabled = TRUE,
    editEdgeCols = c("label")
  )) %>% 
  onRender(c(
    "function(el, x){",
    "  $('#editedge-saveButton').on('click', function(){"
    "    ......",
    "  });",
    "}"))

(load the htmlwidgets package for the onRender function).