rshinyigraphvisnetwork

Shiny visNetwork conditional layout - getting around incompatible options


I'm building a Shiny app displaying interactive networks with visNetwork and I'd like to give users the option to choose between two igraph layouts. The problem is the additional argument required for one layout is an unused argument for the other and I can't seem to get rid of it conditionally.

library(igraph)
library(shiny)
library(visNetwork)

# generate data
iGr <- make_graph("Meredith")
visGr <- toVisNetworkData(iGr)

# ui
ui <- fluidPage(
  # layout options
  radioButtons(
    inputId="grLayout",
    label="View in:",
    choices=list(
      "Graph layout"="layout_with_graphopt",
      "Ellipse layout"="layout_in_circle"
    ),
    selected="layout_with_graphopt"
  ),
  visNetworkOutput("netPlot")
)

# server
server <- function(input, output){
  output$netPlot <- renderVisNetwork({
    visNetwork(visGr$nodes, visGr$edges) %>%
      visIgraphLayout(physics=FALSE, type="square", layout=input$grLayout,
                      charge=0.1 # <- here's my problem
                      )
  })  
}

shinyApp(ui=ui, server=server)

I need the charge argument for the layout_with_graphopt option with my real data to make the the visualization readable, but it throws an "unused argument (charge = 0.1)" error with the layout_in_circle option. I've tried the two variations without success.

This one won't run at all and throws an 'argument "graph" is missing, with no default' error:

  output$netPlot <- renderVisNetwork({
    visNetwork(visGr$nodes, visGr$edges) %>%
      {if (input$grLayout=="layout_with_graphopt") {
        visIgraphLayout(physics=FALSE, type="square", layout=input$grLayout,
                        charge=0.1)
      } else {
        visIgraphLayout(physics=FALSE, type="square", layout=input$grLayout)
      }}
  })

This one throws an "unused argument (charge = NULL)" error for the layout_in_circle option. (I get something similar if I try NA):

  output$netPlot <- renderVisNetwork({
    chargeVal <- if (input$grLayout=="layout_with_graphopt") {0.1} else {NULL}
    visNetwork(visGr$nodes, visGr$edges) %>%
      visIgraphLayout(physics=FALSE, type="square", layout=input$grLayout,
                      charge=chargeVal)
  })

I'd rather not do an if else on the whole renderVisNetwork block because the real one is quite lengthy with a fair number of bells and whistles, and the charge argument would be the only difference between them.


Solution

  • I figured it out. The trick is to stick everything except the visIgraphLayout lines into a visNetwork object, then pipe conditional versions of visIgraphLayout onto the back of it:

      output$netPlot <- renderVisNetwork({
        plot1 <- visNetwork(visGr$nodes, visGr$edges)
        # can add whatever other bells and whistles needed before if/else if
        # conditional layout:
        if (input$grLayout=="layout_with_graphopt"){
          plot2 <- plot1 %>%
            visIgraphLayout(physics=FALSE, type="square", layout=input$grLayout,
                            charge=0.1)
        } else if (input$grLayout=="layout_in_circle") {
          plot2 <- plot1 %>%
            visIgraphLayout(physics=FALSE, type="square", layout=input$grLayout)
        }
        return(plot2)
      })