Selecting row when clicking on a node of a tree

69 views Asked by At

I'm trying to build a datatable which would react when clicking on a node of a tree. Especially, I would like the corresponding row of the table to be selected (given id stored in both datatable column and tree). How can I achieve that? Here is a sample code:

library(shiny)
library(DT)
library(jsTreeR)

nodes <- list(
  list(
    text = "Root",
    state = list(opened = TRUE),
    children = list(
      list(
        text = "Alice",
        state = list(opened = TRUE),
        li_attr = list(id = "1")),
      list(
        text = "Bob",
        state = list(opened = TRUE),
        li_attr = list(id = "2"))
    )
  )
)

dat <- data.frame(
  ID    = c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"),
  Name  = c("Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Hank", "Ivy", "Jack"),
  Age   = c(25, 32, 45, 28, 40, 52, 34, 29, 37, 48)
)

ui <- fluidPage(
  br(),
  fluidRow(
    column(
      3,
      jstreeOutput("tree")
    ),
    column(
      9,
      DTOutput("dtable")
    )
  )
)

server <- function(input, output, session) {
  
  output[["tree"]] <- renderJstree(
    jstree(nodes)
  )
  
  output[["dtable"]] <- renderDT({
    datatable(
      dat, selection = "single", rownames = FALSE
    )
  })
}

shinyApp(ui, server)
1

There are 1 answers

1
thothal On

You can use selectRows to select rows and input$tree_selected to get the selected nodes from the tree. With these 2 ingredients you can implement this feature as follows:

server <- function(input, output, session) {
   
   output$tree <- renderJstree(
      jstree(nodes)
   )
   
   output$dtable <- renderDT({
      datatable(
         dat, selection = "single", rownames = FALSE
      )
   })
   
   observeEvent(input$tree_selected, {
      req(length(input$tree_selected) > 0)
      ## get a vector of all selected ids
      sel <- do.call(rbind, input$tree_selected)[, "text"]
      ## get the corresponding indices and select them
      idx <- which(dat$Name %in% sel)
      dataTableProxy("dtable") %>% 
         selectRows(idx)
   })
}

This approach uses the text which may not be necessarily unique, to circumvent this you could add the id as data to the tree, as tree_selected also returns the data attribute (no need to store the id on the HTML though (via li_attr) since we ae using a shiny only solution and do not need to fallback to JavaScript:

nodes <- list(
   list(
      text = "Root",
      state = list(opened = TRUE),
      children = list(
         list(
            text = "Alice",
            state = list(opened = TRUE),
            data = list(id = "1")),
         list(
            text = "Bob",
            state = list(opened = TRUE),
            data = list(id = "2"))
      )
   )
)

# [...]

server <- function(input, output, session) {
   
   output[["tree"]] <- renderJstree(
      jstree(nodes)
   )
   
   output[["dtable"]] <- renderDT({
      datatable(
         dat, selection = "single", rownames = FALSE
      )
   })
   
   observeEvent(input$tree_selected, {
      req(length(input$tree_selected) > 0)
      sel <- do.call(rbind, input$tree_selected)[, "data"] %>% 
         unlist()
      idx <- which(dat$ID %in% sel)
      dataTableProxy("dtable") %>% 
         selectRows(idx)
   })
}