rshinyshinydashboarddiagrammer

How to refer to multiple DiagrammeR GraphViz graphs in a shinydashboard


I have a shiny dashboard with multiple tabs that are logically linked, so I've created a DiagrammeR diagram using GraphViz to show how they relate. I want to be able to trigger a change to a new tab when a click event is registered on one of the nodes in the GraphViz Diagram.

The diagram will be at the top of each tab, so there are multiple diagrams that need to be clickable.

I have got most of it working as shown in this reprex:

library(shinydashboard)
library(DiagrammeR)
library(shinyjs)

ui <- dashboardPage(
  dashboardHeader(title = "DiagrammeR buttons"),
  dashboardSidebar(
    sidebarMenu(id = "tabs",
                menuItem("Tab 1", tabName = "one", icon = icon("dice-one")),
                menuItem("Tab 2", tabName = "two", icon = icon("dice-two"))
    )
  ),
  dashboardBody(
    useShinyjs(),
    tabItems(
      tabItem(tabName = "one",
        fluidRow(box(grVizOutput("diagram_a", width = "100%", height = "100%"), width = 12))
        ),
      tabItem(tabName = "two",
        fluidRow(box(grVizOutput("diagram_b", width = "100%", height = "100%"), width = 12))
        )
      )
    )
  )

server <- function(input, output, session) {
  
  output$diagram_a <- renderGrViz({grViz("digraph {
  graph[rankdir = LR]
  node [style = filled]
  n1_1 -> n1_2
  }")})
  
  output$diagram_b <- renderGrViz({grViz("digraph {
  graph[rankdir = LR]
  node [style = filled]
  n2_1 -> n2_2
  }")})
  
  observeEvent(eventExpr = input$clickednode, {
    updateTabItems(session, "tabs", input$clickednode)
  })

  observe({
    local({
      clicked = "Shiny.onInputChange('clickednode', 'one')"
      shinyjs::onclick("node1",runjs(clicked))
      })
    local({
      clicked = "Shiny.onInputChange('clickednode', 'two')"
      shinyjs::onclick("node2",runjs(clicked))
      })
  })
}

shinyApp(ui, server)

The only problem is that only the first graph in the dashboard registers click events. The second tab in this example is just a static diagram that can't be interacted with.

I need some help making sure I refer to the specific node1 and node2 that I am interested in, rather than just the first node1 or node2 that appears in the dashboard. I'm not sure how to do that in JavaScript.

Note: nodeX where X is a numeral is the id that shiny automatically gives to the nodes in every graphViz diagram. This can be found by inspecting the dashboard html.


Solution

    1. Use unique IDs for each node. In HTML, having repeated IDs should be strongly avoided. Even if you change the text to be n1_1 and n2_1 for each plot, the default IDs are always node1, node2, node3 ... You need to manually change it.
    2. suggestion: use setInputValue instead of onInputChange.

    Following is how you can set unique IDs for each node in GraphViz. Take a look at GraphViz grammar will be helpful.

    library(shinydashboard)
    library(DiagrammeR)
    library(shinyjs)
    
    ui <- dashboardPage(
      dashboardHeader(title = "DiagrammeR buttons"),
      dashboardSidebar(
        sidebarMenu(id = "tabs",
                    menuItem("Tab 1", tabName = "one", icon = icon("dice-one")),
                    menuItem("Tab 2", tabName = "two", icon = icon("dice-two"))
        )
      ),
      dashboardBody(
        useShinyjs(),
        tabItems(
          tabItem(tabName = "one",
                  fluidRow(box(grVizOutput("diagram_a", width = "100%", height = "100%"), width = 12))
          ),
          tabItem(tabName = "two",
                  fluidRow(box(grVizOutput("diagram_b", width = "100%", height = "100%"), width = 12))
          )
        )
      )
    )
    
    server <- function(input, output, session) {
      
      output$diagram_a <- renderGrViz({grViz('digraph {
      graph[rankdir = LR]
      node [style = filled]
      n1_1 -> n1_2
      n1_1[id="d1_n1"]
      n1_2[id="d1_n2"]
      }')})
      
      output$diagram_b <- renderGrViz({grViz('digraph {
      graph[rankdir = LR]
      node [style = filled]
      n2_1 -> n2_2
      n2_1[id="d2_n1"]
      n2_2[id="d2_n2"]
      }')})
      
      observeEvent(input$clickednode, {
        updateTabItems(session, "tabs", input$clickednode)
      })
      
      observe({
        local({
          clicked = "Shiny.setInputValue('clickednode', 'one')"
          shinyjs::onclick("d1_n1",runjs(clicked)) 
          shinyjs::onclick("d2_n1",runjs(clicked))
        })
        local({
          clicked = "Shiny.setInputValue('clickednode', 'two')"
          shinyjs::onclick("d1_n2",runjs(clicked)) 
          shinyjs::onclick("d2_n2",runjs(clicked))
        })
      })
    }
    
    shinyApp(ui, server)