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
exprshould be evaluated.- quoted
Is
expra quoted expression (withquote())? 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.exprthen 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:
stopenedTRUEto show the node expanded.stselectedTRUEto select it.stcheckedTRUEto tick its checkbox whentie_selection = FALSE. With the defaulttie_selection = TRUEa ticked box is a selection, so usestselectedinstead.stdisabledTRUEto disable it.sticonan 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 forfluidPage(); any other icon set works if the app loads its stylesheet.sttypethe node's type, for the
typesargument ofshinyTree.sttooltiptext shown when the pointer rests on the node's label. It is shown as plain text, not HTML.
stcssclassone 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 withli.myclass > a. Unlikesttype, 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 = TRUEmarks 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
TRUEorFALSEis treated asFALSE, with a warning.function(path, node)The function may take an argument named
node: thest*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 usenode$stidto tell them apart. Only an argument callednodereceives it.stpathis 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
stselectedandstopened; an opened child's children are fetched at once.- Reactivity
expris 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_erroris set to a list with the node'spathand themessage. Withoptions(shiny.sanitize.errors = TRUE)the message is replaced by a generic one, as for other outputs, unless it is asafeError.- 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, returnlist(children = <function>, search = <function>)instead, wheresearchis 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$treeholds the nodes fetched so far. Nodes can only be selected or ticked once they are fetched, so
get_selected()andget_checked()are complete – except that ticking a branch that has never been opened reports the branch, not the children it has not fetched, soleaves_only = TRUEcannot 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("..", "..")– andnodeis 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 whatexprreturns to rebuild the tree; andexpandTree(), 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.
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)
}