5

I have a networkd3 Sankey diagram in my R/Shiny application, which pulls data from a MySQL connection. I am trying to find a way to 'drill-down' into the chart - click on a node and this will return the name of the node. I will then use this to pass another query to the DB which will populate a table in the application.

Here's what I have -

san <- sankeyNetwork(Links = sanData, Nodes = nodes, Source = "source",
    Target = "target", Value = "value")

This plots the chart which works just fine.

Here's what I've tried so far -
Use R package htmlwidgets, and run the followning JS script using onRender on my sankey chart.

Placeholder for the JS script:

clickFun <- 'd3.selectAll(".link").on("click",function(d) {alert(d.value);})'

In my sankey output:
onRender(san, clickFun)

This returns the value of the link as an alert. I tried using ".node" and d.data.name as suggested by some other examples, but I was never able to get this to work and no alert pops up.

Here are my questions:
1. How can I retrieve the name of the node from the sankey plot on mouse click?
2. Assuming I've achieved the above, how can I use the value returned by my JS script in my shiny application? I will change the alert() bit to return(), but I am at a loss as to how I can use this in my server application.

Rangarajan
  • 53
  • 4
  • 1
    Hi Rangarajan, it would be really helpful if you could update your question with a [reproducible example](https://stackoverflow.com/questions/48343080/how-to-create-a-good-shiny-reproducible-example/48343110#48343110). That makes it a lot easier for others to help you tackle your issue. – Florian Jan 26 '18 at 06:54

1 Answers1

5

The reason your click event works on the links, but not on the nodes is because the nodes also have a drag behavior attached to them. The easiest way to get around that is to nullify the drag behavior and then add the click behavior, like this...

library(networkD3)
library(htmlwidgets)

URL <- paste0('https://cdn.rawgit.com/christophergandrud/networkD3/',
              'master/JSONdata/energy.json')
energy <- jsonlite::fromJSON(URL)

san <- 
  sankeyNetwork(Links = energy$links, Nodes = energy$nodes, Source = 'source',
              Target = 'target', Value = 'value', NodeID = 'name',
              units = 'TWh', fontSize = 12, nodeWidth = 30)

clickFun <- 
  'function() { 
      d3.selectAll(".node").on("mousedown.drag", null);
      d3.selectAll(".node").on("click",function(d) { alert(d.name); })
   }'

onRender(san, clickFun)

here's a working example where the onclick event returns the name of the clicked node, which is then passed to an R function that process an SQL command and returns the result to a datatable command, which displays the links that follow from that node...

library(shiny)
library(networkD3)
library(DT)
library(sqldf)
library(htmlwidgets)

URL <- paste0('https://cdn.rawgit.com/christophergandrud/networkD3/',
              'master/JSONdata/energy.json')
energy <- jsonlite::fromJSON(URL)

energy$links$name <- energy$nodes$name[energy$links$source + 1]
df <- energy$links

funct <-
  function (n) {
    isp <- sprintf("Select * From df Where df.name='%s';", n)
    isd <- sqldf::sqldf(isp)
    return(isd)
  }

ui <- shinyUI(fluidPage(
  fluidRow(
    column(4, sankeyNetworkOutput("sankey")),
    column(4, DT::dataTableOutput("table"))
  )
))

server <- shinyServer(function(input, output, session) { 
  session$onSessionEnded(stopApp)
  output$sankey <- renderSankeyNetwork({
    san <- 
      sankeyNetwork(Links = energy$links, Nodes = energy$nodes, Source = 'source',
                    Target = 'target', Value = 'value', NodeID = 'name',
                    units = 'TWh', fontSize = 12, nodeWidth = 30)

    clickFun <- 
      'function() { 
          d3.selectAll(".node").on("mousedown.drag", null);
          d3.selectAll(".node").on("click",function(d) { Shiny.onInputChange("id", d.name); });
        }'

    onRender(san, clickFun)
  })

  output$table <- DT::renderDataTable(DT::datatable(funct(input$id)))
})

shinyApp(ui = ui, server = server)
CJ Yetman
  • 8,373
  • 2
  • 24
  • 56
  • Thanks, this works like a charm. How can I use the value returned by the javascript function in the application though? I would like to pass it in the _where_ clause in my SQL query. – Rangarajan Jan 28 '18 at 01:04
  • I am not sure how it does, maybe I'm missing something. Is there a way to directly use what is returned by the javascript script (clickFun, in my case) in the form of a string? – Rangarajan Feb 01 '18 at 04:31
  • Look closer at what happens with the `Shiny.onInput` bit – CJ Yetman Feb 01 '18 at 08:17
  • try the new example I added above, which is extremely similar to the example in the other SO post that I pointed you to – CJ Yetman Feb 01 '18 at 08:44
  • Awesome. the Shiny.onInputChange and how it works with the whole thing was what I was missing. I was trying out with return() in my JS script. Added `Shiny.onInputChange("id", d.name);` to the javascript, and `input$id` allows me to access this. This gives me enough to start working up from here. Thanks! – Rangarajan Feb 01 '18 at 21:51