Skip to contents

Fills a shinyTree with the tree an expression returns, as a nested list (see Node attributes) or as JSON in jsTree's format, such as treeToJSON gives.

Usage

renderTree(expr, env = parent.frame(), quoted = FALSE, lazy = FALSE)

Arguments

expr

An expression that returns the tree: a named nested list, or a JSON string. With lazy = TRUE, a function instead.

env

The environment in which expr should be evaluated.

quoted

Is expr a quoted expression (with quote())? This is useful if you want to save an expression in a variable.

lazy

If TRUE, the tree's nodes are fetched as they are opened rather than all at once, for trees too large to send whole. expr then returns a function giving a node's children; see Lazy loading.

Value

A render function, to assign to output$<id> for a shinyTree with that id.

Note

A tree's contents are pushed to the client rather than returned as an output value, so Shiny has nothing cached to replay if the element is removed from the page and later added back – inside a modalDialog that is dismissed and reopened, for example, or a conditional panel. The client reports whenever it binds an empty tree and the contents are resent, so such a tree repopulates without the app having to re-trigger the render itself.

Node attributes

Each node is an element of the list, named by its label. Attributes set on it with structure or attr() control how it is shown:

stopened

TRUE to show the node expanded.

stselected

TRUE to select it.

stchecked

TRUE to tick its checkbox when tie_selection = FALSE. With the default tie_selection = TRUE a ticked box is a selection, so use stselected instead.

stdisabled

TRUE to disable it.

sticon

an icon: Font Awesome classes such as "fa fa-file" or "fa-solid fa-folder", or the URL or path of an image. A name on its own, "file" or "fa-file", is expanded to "fa fa-file", and "glyphicon-file" to "glyphicon glyphicon-file". Glyphicons need Bootstrap 3, the default for fluidPage(); any other icon set works if the app loads its stylesheet.

sttype

the node's type, for the types argument of shinyTree.

sttooltip

text shown when the pointer rests on the node's label. It is shown as plain text, not HTML.

stcssclass

one or more CSS classes for the node, as a character vector or a space-separated string, so the app's own CSS can style it. They go on the node's <li>, so target its label with li.myclass > a. Unlike sttype, any number can be given.

Any other attribute is passed through as data and comes back on the node in input$tree, apart from those R itself gives a node, such as a data frame's class and row.names. get_selected and get_checked keep those whose names start with st, so that is the convention to follow. That includes stclass, which despite its name is only data – it is what get_selected(format = "classid") reports – and is not applied as a CSS class; use stcssclass for that.

Every node needs a name, and names are shown as HTML, so a name read from a file or a database can contain markup. Siblings may share a name.

Lazy loading

With lazy = TRUE, expr returns a function that is given a node's path – the names from the root down to it, character(0) for the top level – and returns that node's children as a named list, in the same form renderTree() takes. Only the top level is sent when the tree is shown; each node's children are fetched when the user opens it.


output$tree <- renderTree(lazy = TRUE, {
  root <- input$folder          # reactive: a change reloads the whole tree
  function(path) {
    dir <- do.call(file.path, as.list(c(root, path)))
    subdirs <- list.dirs(dir, full.names = FALSE, recursive = FALSE)
    # A symbolic link can point back up the tree; show it as a leaf.
    links <- nzchar(Sys.readlink(file.path(dir, subdirs)))
    files <- setdiff(list.files(dir), subdirs[!links])
    c(setNames(lapply(subdirs[!links], function(d) structure("", stchildren = TRUE)),
               subdirs[!links]),
      setNames(lapply(files, function(f) ""), files))
  }
})
stchildren = TRUE

marks a child that has children of its own, so it shows as closed and is fetched when opened. A child given as a non-empty list is sent with its children straight away instead. Any value other than TRUE or FALSE is treated as FALSE, with a warning.

function(path, node)

The function may take an argument named node: the st* attributes of the node being opened, as a list. Siblings with the same name have the same path, so give each an id, structure("", stchildren = TRUE, stid = 42), and use node$stid to tell them apart. Only an argument called node receives it. stpath is used by shinyTree for the path a node was fetched under, so do not set it yourself.

Node attributes

All the attributes in Node attributes above work on the children, including stselected and stopened; an opened child's children are fetched at once.

Reactivity

expr is evaluated as usual, so anything reactive it reads rebuilds the tree when it changes, which starts it again from the top level. The children function can read reactive values too, and gets their current value, but reading one there does not rebuild the tree.

Errors

An error in the children function is shown in the R console, the node it was opening stays closed so opening it again retries, and input$<outputId>_lazy_error is set to a list with the node's path and the message. With options(shiny.sanitize.errors = TRUE) the message is replaced by a generic one, as for other outputs, unless it is a safeError.

Search

With shinyTree(search = TRUE) the whole tree is searched, not just the nodes fetched so far: the server finds the matches, and the branches leading to them are fetched and opened. By default this walks the whole tree with the children function, matching names case-insensitively, and gives up with an error after 10,000 calls or 100,000 nodes or 5 seconds. A branch whose children function fails is skipped. For anything larger, or slow, return list(children = <function>, search = <function>) instead, where search is given the search text and returns the paths of the matching nodes as a list of character vectors, e.g. from a database. At most 1,000 matches are shown.

input$tree

holds the nodes fetched so far. Nodes can only be selected or ticked once they are fetched, so get_selected() and get_checked() are complete – except that ticking a branch that has never been opened reports the branch, not the children it has not fetched, so leaves_only = TRUE cannot list them.

State

With shinyTree(state = TRUE), the branches that were open, and the parents of the nodes that were selected or ticked, are fetched and reopened after a reload. Of several siblings with the same name, the same one is reopened, by its position among them.

Renaming and moving

A node keeps the path it was fetched under, so after the user renames or moves it with the context menu or drag and drop, opening it still asks for its children by its original path.

Speed

One R process answers every user of the app, so a children function that takes a second makes everyone wait a second. Keep it fast: query only the one level asked for, and cache expensive results.

Trust

The server only asks the children function for branches it has sent to that browser – never for a leaf, and never for a path it made up, such as c("..", "..") – and node is always the data the server sent with that branch, never what the browser says it is. It cannot know what a name means to the app, though: if the app turns names into file paths or database keys, it is still up to the app not to offer a branch, such as a symbolic link, that leads somewhere it should not.

Not available

bindCache(); updateTree(), which stops with an error – change what expr returns to rebuild the tree; and expandTree(), which stops with an error since it would have to fetch the whole tree.

Several branches that need fetching at once – on a search or a state restore – are fetched in a single request. The 27-lazy example shows a million-node tree with a search function, and a folder browser.

Caching

renderTree can be wrapped in bindCache, in which case expr is evaluated only for a cache key that has not been seen before and the cached tree is sent otherwise. It works with bindEvent as well.

See also

Examples

if (interactive()) {
  library(shiny)
  ui <- fluidPage(shinyTree("tree"), verbatimTextOutput("picked"))
  server <- function(input, output, session) {
    output$tree <- renderTree(list(
      Fruit = structure(list(Apple = "", Pear = structure("", stselected = TRUE)),
                        stopened = TRUE, sticon = "fa fa-folder"),
      Vegetables = list(Leek = "")
    ))
    output$picked <- renderPrint(get_selected(input$tree))
  }
  shinyApp(ui, server)
}