Tree
A simple tree view to display hierarchical data.
Basic usage
By default, the tree expects an object with the following structure as input:
[
{
label: 'Node 1',
children: [
{
label: 'Node 1.1',
children: [...]
},
{
label: 'Node 1.2'
}
]
},
{
label: 'Node 2',
children: [...]
}
]
Here is a live example:
As you see the data are passed using v-model
, which means that the tree is reactive to external changes, in both directions.
You can also pass the data by doing :data="content"
if you don't the host to be updated.
TIP
There is an alternate notation you can use when you only need the labels, that uses an array:
// Single level
['a', 'b', 'c']
// To create a sub level, create sub arrays:
['a', ['b', 'b1', 'b2'], 'c']
// The first item of the sub array is used as label, the reste are sub nodes.
// It works with any number of levels:
[
'a',
['b', 'b1', ['b2', 'b21', ['b22', 'b211', 'b212'], 'b23']],
['c', 'c1', 'c2']
]
If you want to inline the declaration in the template, you can do this:
<bt-tree :data="['a', ['b', 'b1', 'b2'], ['c', 'c2', 'c3']]"></bt-tree>
Which gives:
The tree is highly customizable, as we'll see in the next sections.
Nodes normalization
No matter how the data are given to the tree, they always go through a normalization phase, that converts them into a Node
object that is used internally.
For each node, the component needs to figure out the following data from the input:
- what's the label?
- what's the identifier?
- what are the children?
- is it disabled?
For each of these questions, the answer will depend on the input and the configuration.
1) What's the label?
- If the prop
nodes-label
is a function, it will always be called to get the label. - Else if the input is a , it is used as label.
- Else if the input is an
array
, the first item of the array is used as label. - Else if the input is an
object
, the prop choices-label is expected to contain the name of the attribute to get the label from.
<!-- Tell the tree that the "name" attribute should be used as label. -->
<bt-tree nodes-label="name"></bt-tree>
<!-- OR -->
<!-- Tell the tree to execute a function for each node, that will return the label. -->
<bt-tree :nodes-label="(o) => o.name" :data="[{name: 'A'}, {name: 'B'}]"></bt-tree>
<!-- ALSO -->
<!-- "nodes-label" can contain replaceable segments. -->
<!-- Here "{fullName}" and "{adherentId}" will be replaced by the value in each object. -->
<bt-tree nodes-label="{fullName} (n°{adherentId})" :data="[{fullName: 'John', adherentId: 123}, ...]"></bt-tree>
2) What's the identifier?
- If the prop nodes-identifier is a function, it will always be called to get the identifier.
- Else if the input is a , it is used as identifier.
- Otherwise, nodes-identifier is expected to contain the name of the attribute to get the identifier from.
<!-- Tell the tree that the "id" attribute should be used as identifier. -->
<bt-tree nodes-identifier="id""></bt-tree>
<!-- OR -->
<!-- Tell the tree to execute a function for each item, that will return the identifier. -->
<bt-tree :nodes-identifier="(o) => o.id" :data="[{id: 1, name: 'A'}, {id: 2, name: 'B'}]"></bt-tree>
WARNING
The identifier must be unique for the whole tree.
3) What are the children?
- If the prop nodes-children is a function, it will always be called to get the list of children.
- Else if the input is an
array
, the items from the index1
until the end will be treated as children. - Else, nodes-children is expected to contain the name of the attribute to get the children from.
<!-- Tell the tree that the "sub" attribute should be used to get the child nodes. -->
<bt-tree choices-children="sub" :data="[{name: 'A', sub: [{name: 'A child 1'}, ...]}]"></bt-tree>
<!-- OR -->
<!-- Tell the tree to execute a function for each item, that will return the children. -->
<bt-tree :choices-label="(o) => o.children"></bt-tree>
<!-- OR -->
<!-- Here 'b1' and 'b2' will be added as children of 'b', because the item is an array. -->
<bt-tree :data="['a', ['b', 'b1', 'b2'], 'c']"></bt-tree>
4) Is it disabled?
- If the prop nodes-disabled is a function, it will always be called to get a
boolean
. - Else if the prop nodes-disabled is a
string
AND if the input is anobject
, it is expected to be the name of the attribute that holds a boolean answering the question. - Otherwise, the item will not be disabled.
<!-- The "disabled" attribute should be used to know if the choice is disabled. -->
<bt-tree nodes-disabled="disabled" :data="[{name: 'A', disabled: true}, ...]"></bt-tree>
<!-- OR -->
<!-- Tell the tree to execute a function for each item, that will return a boolean. -->
<bt-tree :nodes-disabled="(o) => !o.enabled" :data="[{name: 'A', enabled: false}, ...]"></bt-tree>
Remote nodes
The content of the tree can be loaded remotely:
NOTE
There are several other remote related props, for which you can find the detail here.
The remote module is fully integrated with Models and Transformers, so you can for example use the endpoint of a model and have a tree of models at your disposal when rendering the tree:
<!-- Will use the endpoint "list_all" defined on the model "Article" to do the request. -->
<!-- The resulting response will contain a list of "Article" objects, used to render the tree. -->
<bt-tree remote-endpoint="list_all" remote-model="Article" :nodes-label="title"></bt-tree>
More on how remotes work here.
Customize
In addition of the theming customization provided by Vue Typescript,
you can customize the look of your tree via the node
slot which is called for each visible node.
Look at the Slots section for more detail about the slots.
Lazy loading
If your tree is too big or hard to iterate to be fetched in one go, you can activate the lazy loading by giving a value to remote-node-url-param
.
When remote-node-url-param
has a value, fetch requests are contextualized per node.
This means that a new request will be done for each opened node, with remote-node-url-param
set to the id of newly opened node.
In the example below, the tree is generated procedurally, and extends to infinity. When you open a node, a random number of children is generated (from 0
to 6
):
WARNING
To use this functionality, your nodes must be uniquely identified. You can define what attribute to use as identifier via the nodes-identifier
prop. More info here.
Props
Method | Description | Default |
---|---|---|
v-model | Bidirectional binding controlling the local data to display in the tree. | null |
Define how to resolve the node's identifier. See Nodes normalization for detail. | null | |
Define how to resolve the node's label. See Nodes normalization for detail. | label | |
Define how to resolve the node's children. See Nodes normalization for detail. | children | |
Define how to resolve the node's children. See Nodes normalization for detail. | disabled | |
Alias of the type of model that is used as nodes. | null | |
Url to call to get the nodes of the tree. | null | |
HTTP method to use to call the server. | 'GET' | |
Name of the Endpoint to use to load the nodes of the tree. If a model type is defined, the endpoint will first try to resolve for this specific model. | null | |
Url parameters to use when building the request. | {} | |
Custom headers to add to the request. | {} |
Slots
Name | Description |
---|---|
node | Called for each visible node to render. TIP You can easily test if the node is the root by testing if it has a parent ( Props:
|
Node
instance
The The object created for each node of the tree. It is passed as prop to the slots.
Click here to see its definition
class Node {
/**
* The text visible to the user.
*/
label: string;
/**
* The value used to check for equality with other items.
*/
identifier: Primitive|undefined;
/**
* `true` if the item is non-selectable by the user.
*/
disabled: boolean ;
/**
* If `false` the children of the item have not been fetched yet.
*/
fetched: boolean;
/**
* Number of non-filtered children.
* It has no link with `childrenVisible`.
*/
childrenVisibleCount: number;
/**
* Status of the remote fetching of children.
*
* Can be:
* - idle (NodeRemoteFetchStatus.Idle),
* - pending (NodeRemoteFetchStatus.Pending),
* - failed (NodeRemoteFetchStatus.Failed).
*/
remoteFetchStatus: NodeRemoteFetchStatus;
/**
* Contains the error message given by the remote module in case a remote fetch has failed.
*/
remoteFetchError: string|null;
/**
* List of child nodes.
*/
children: Node[];
/**
* The raw value the Node instance has been created from.
*/
originalValue: AnyObject;
}
Theming
Below the list of all CSS variables and selectors available to customize the appearance of the component. If you're not familiar on how the theming works, please refer to the VueThemes documentation.
Variables
Variable | Default | Comment |
---|---|---|
itemFontFamily | var(--bt-font-family-base) | - |
itemFontSize | var(--bt-font-size-sm) | - |
itemFontWeight | var(--bt-font-weight-normal) | - |
itemTextColor | var(--bt-text-color-base) | - |
itemTextHoverColor | var(--tag-text-color) | - |
itemBackgroundColor | transparent | - |
itemBackgroundHoverColor | var(--bt-color-gray-50) | - |
itemIconColor | var(--bt-text-color-light) | - |
itemDisabledOpacity | 0.5 | - |
Selectors
Name | Comment | Pseudo classes | Attributes | Combinable with |
---|---|---|---|---|
root | The root container. | - | - | - |
item | The individual container of each node. | Any | Any + Built-in: [disabled] [expanded] [empty] | - |
itemsWrapper | The container a single level of the tree. | - | - | - |
itemTitle | Title of a node. | - | - | item |
itemAddon | The icon that appear on the left of a node. | - | - | item |
Examples:
cssSelectors: {
item: {
background: '...'
},
'item:hover': {
background: '...'
},
'item[expanded]': {
border: '...'
},
'item:hover itemTitle': {
color: '...'
}
}