I’m trying to send R data from the server to the client using JavaScript using Shiny's onInputChange
and addCustomMessageHandler
functionality described here:
https://ryouready.wordpress.com/2013/11/20/sending-data-from-client-to-server-and-back-using-shiny/
My goal is to add tooltips to the links in a forceNetwork diagram in Shiny using data that will be stored in an R variable passed to the ui through JavaScript. My application is supposed to accept 2 CSV files (one with nodes data, one with links data) and then plot it in a forceNetwork with tooltips for the links. I would need to retrieve the tooltip column stripped from the nodes and links data when forceNetwork makes the forceNetwork object. Everything works fine except for the tooltip functionality. What’s stumping me, is
- how to pass the subsetted data with just tooltip information to the ui without getting an error for exposing a reactive value in the server, and
- how to use that data and javascript to make tooltips for the forceNetwork links.
If this were not a reactive graph, I would just append the tooltip column to the fn
forceNetwork object after creating it. However, that doesn’t seem to make it into the graph. I’m looking at instead passing tooltip data to a tag in the ui and then assigning it to display as the tooltip for the links.
Here is the code:
library(shiny)
library(networkD3)
server <- function(input, output, session) {
# User uploads CSV for nodes (file has name, group, tooltip columns)
mydata_n <- reactive({
req(input$file_n)
inFile <- input$file_n
df <- read.csv(inFile$datapath)
return(df)
})
# User uploads CSV for links (file has source, target, value, tooltip columns)
mydata_l <- reactive({
req(input$file_l)
inFile <- input$file_l
df <- read.csv(inFile$datapath)
# The source and target columns have names rather than zero-indexed row numbers as forceNetwork requires, so fix them using nodes file as reference
df$source <- match(df$source, mydata_n()$name)
df$target <- match(df$target, mydata_n()$name)
df[1:2] <- df[1:2]-1
return(df)
})
# Render tables showing content of uploaded files
output$table_n <- renderTable({
mydata_n()
})
output$table_l <- renderTable({
mydata_l()
})
# make network with data
output$net <- renderForceNetwork({
fn <- forceNetwork(
Links = mydata_l(), Nodes = mydata_n(), Source = "source",
Target = "target", Value = "value", NodeID = "name",
Group = "group", opacity = 1, zoom = FALSE, bounded = F, linkWidth = 1, linkColour = "#939393", charge = -80
)
}
)
# This part is broken. When a links file is uploaded, subset it to make a linkTooltips df with just tooltip data and pass it to the browser using myCallbackHandler
observe({
input$file_l
linkTooltips <- mydata_l()["tooltip"]
session$sendCustomMessage(type = "myCallbackHandler", linkTooltips)
})
# Show table output
}
ui <- fluidPage(
# This is where the linkTooltips data should be assigned to display as a tooltip, but I'm not sure how to access that R variable in javascript and assign each tooltip to the appropriate link. My start (based on an answer to a previous question) is below.
tags$head( tags$script('Shiny.addCustomMessageHandler("myCallbackHandler",
function(linkTooltips) {
d3.selectAll(".link")
.attr("title", "linkTooltips");
});
')
),
titlePanel("ForceNetD3"),
mainPanel(forceNetworkOutput("net"),
# start input
fluidRow(column( 12, wellPanel( h3("Upload a file"),
fileInput('file_n', 'Choose CSV File for Nodes', accept=c('text/csv', 'text/comma-separated-values,text/plain', '.csv')),
fileInput('file_l', 'Choose CSV File for Links', accept=c('text/csv', 'text/comma-separated-values,text/plain', '.csv'))
)
)),
fluidRow(
tabsetPanel(
tabPanel( "Nodes Data", tableOutput(outputId = "table_n")),
tabPanel( "Links Data", tableOutput(outputId = "table_l"))
)
# end input
))
)
shinyApp(ui = ui, server = server)
I'd really appreciate if someone can point me in the right direction.