Skip to content

Commit e510c72

Browse files
committed
Add asynchronous loading for root nodes
1 parent a1d6e5a commit e510c72

File tree

10 files changed

+234
-127
lines changed

10 files changed

+234
-127
lines changed

docsrc/demos.md

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ The convenience method `getSelected` is exposed on the tree component to make it
835835
<!--- -------------------------------------------------------------------------------------- --->
836836
### Slots
837837

838-
A treeview has slots available for replacing specific types of nodes. The `text`, `checkbox`, `radio`, and `loading` slots replace the correpsonding types of nodes. For more info, see [the docs](./#slots).
838+
A treeview has slots available for replacing specific types of nodes. The `text`, `checkbox`, `radio`, `loading-root` and `loading` slots replace the correpsonding types of nodes. For more info, see [the docs](./#slots).
839839

840840
```{=html5}
841841
<details>
@@ -852,7 +852,10 @@ A treeview has slots available for replacing specific types of nodes. The `text`
852852
<template v-slot:radio="{ model, customClasses, inputId, inputModel, radioChangeHandler }">
853853
content
854854
</template>
855-
<template v-slot:text="{ model, customClasses }">
855+
<template v-slot:loading="{ model, customClasses }">
856+
content
857+
</template>
858+
<template v-slot:loading-root>
856859
content
857860
</template>
858861
</tree>
@@ -1054,22 +1057,26 @@ A treeview has slots available for replacing specific types of nodes. The `text`
10541057
<!--- -------------------------------------------------------------------------------------- --->
10551058
### Asynchronous Loading
10561059
1057-
Child nodes can be loaded asynchronously by providing a function to the `loadChildrenAsync` property in a node's `treeNodeSpec` (or use `modelDefaults` to use the same method for all nodes). The `loadChildrenAsync` can take the parent node's model data as an argument, and should return a Promise that resolves to an array of model data to add as children.
1060+
Two types of asynchronous loading are available. The first loads the root data for the treeview itself, and the second asynchronously loads child data when a node is expanded.
1061+
1062+
You can load root nodes asynchronously by providing a function to the `loadNodesAsync` property of the treeview. The function should return a Promise that resolves to an array of model data to add as root nodes.
1063+
1064+
You can load child nodes asynchronously by providing a function to the `loadChildrenAsync` property in a node's `treeNodeSpec` (or use `modelDefaults` to use the same method for all nodes). The function can take the parent node's model data as an argument, and should return a Promise that resolves to an array of model data to add as children.
10581065

10591066
```{=html5}
10601067
<details>
10611068
<summary>
10621069
```
10631070
```html
1064-
<tree id="customtree-async" :initial-model="model" :model-defaults="modelDefaults"></tree>
1071+
<tree id="customtree-async" :load-nodes-async="loadNodesAsync" :model-defaults="modelDefaults"></tree>
10651072
```
10661073
```{=html5}
10671074
</summary>
10681075
```
10691076
<!--- The leading spaces are to render the html aligned correctly --->
10701077
```html
10711078
<div id="app-async" class="demo-tree">
1072-
<tree id="customtree-async" :initial-model="model" :model-defaults="modelDefaults"></tree>
1079+
<tree id="customtree-async" :load-nodes-async="loadNodesAsync" :model-defaults="modelDefaults"></tree>
10731080
</div>
10741081
<script type='module'>
10751082
import TreeView from "@grapoza/vue-tree"
@@ -1079,12 +1086,6 @@ Child nodes can be loaded asynchronously by providing a function to the `loadChi
10791086
},
10801087
data() {
10811088
return {
1082-
model: [
1083-
{
1084-
id: "async-rootnode",
1085-
label: "Root Node"
1086-
}
1087-
],
10881089
modelDefaults: {
10891090
loadChildrenAsync: this.loadChildrenAsync,
10901091
deleteTitle: 'Delete this node',
@@ -1093,7 +1094,7 @@ Child nodes can be loaded asynchronously by providing a function to the `loadChi
10931094
};
10941095
},
10951096
methods: {
1096-
loadChildrenAsync(parentModel) {
1097+
async loadChildrenAsync(parentModel) {
10971098
const id = Date.now();
10981099
return new Promise(resolve => setTimeout(resolve.bind(null, [
10991100
{
@@ -1106,6 +1107,14 @@ Child nodes can be loaded asynchronously by providing a function to the `loadChi
11061107
treeNodeSpec: { deletable: true }
11071108
}
11081109
]), 1000));
1110+
},
1111+
async loadNodesAsync() {
1112+
return new Promise(resolve => setTimeout(resolve.bind(null, [
1113+
{
1114+
id: "async-rootnode",
1115+
label: "Root Node"
1116+
}
1117+
]), 1000));
11091118
}
11101119
}
11111120
}).$mount('#app-async');
@@ -1117,7 +1126,7 @@ Child nodes can be loaded asynchronously by providing a function to the `loadChi
11171126
11181127
```{=html5}
11191128
<div id="app-async" class="demo-tree">
1120-
<tree id="customtree-async" :initial-model="model" :model-defaults="modelDefaults"></tree>
1129+
<tree id="customtree-async" :load-nodes-async="loadNodesAsync" :model-defaults="modelDefaults"></tree>
11211130
</div>
11221131
<script type='module'>
11231132
new Vue({
@@ -1126,13 +1135,6 @@ Child nodes can be loaded asynchronously by providing a function to the `loadChi
11261135
},
11271136
data() {
11281137
return {
1129-
childCounter: 0,
1130-
model: [
1131-
{
1132-
id: "async-rootnode",
1133-
label: "Root Node"
1134-
}
1135-
],
11361138
modelDefaults: {
11371139
loadChildrenAsync: this.loadChildrenAsync,
11381140
deleteTitle: 'Delete this node',
@@ -1141,7 +1143,7 @@ Child nodes can be loaded asynchronously by providing a function to the `loadChi
11411143
};
11421144
},
11431145
methods: {
1144-
loadChildrenAsync(parentModel) {
1146+
async loadChildrenAsync(parentModel) {
11451147
const id = Date.now();
11461148
return new Promise(resolve => setTimeout(resolve.bind(null, [
11471149
{
@@ -1154,6 +1156,14 @@ Child nodes can be loaded asynchronously by providing a function to the `loadChi
11541156
treeNodeSpec: { deletable: true }
11551157
}
11561158
]), 1000));
1159+
},
1160+
async loadNodesAsync() {
1161+
return new Promise(resolve => setTimeout(resolve.bind(null, [
1162+
{
1163+
id: "async-rootnode",
1164+
label: "Root Node"
1165+
}
1166+
]), 1000));
11571167
}
11581168
}
11591169
}).$mount('#app-async');

docsrc/index.md

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,12 @@ To see it in action, try out the [demos](demos.html).
9797

9898
| Prop | Type | Description | Default value | Required |
9999
|:------------------|:---------|:---------------------------------------------------------------------------------------------------|:----------------------------------|:---------|
100-
| initialModel | Array | The data model containing [model data](#model-data) | - | Yes |
100+
| initialModel | Array | The data model containing [model data](#model-data) | `[]` | |
101101
| customAriaKeyMap | Object | An object, the properties of which are arrays to keyCodes for various actions | See [Aria](#setting-key-bindings) | |
102+
| loadNodesAsync | Function | A function that is called on mount to asynchronously load [model data](#model-data) | `null` | |
102103
| modelDefaults | Object | An object containing defaults for all nodes that do not specify the given properties | `{}` | |
103104
| selectionMode | String | How selection should operate (see [Selection Mode](#selection-mode)) | `null` (cannot select nodes) | |
104-
| skinClass | String | A class name to apply to the tree that specifies a skin to use (see [Skins](#skins)) | `"grtv-default-skin"` | |
105+
| skinClass | String | A class name to apply to the tree that specifies a skin to use (see [Skins](#skins)) | `"grtv-default-skin"` | |
105106

106107
## Selection Mode
107108

@@ -116,7 +117,9 @@ When clicking on a node, it is only selected if the click target was not interac
116117

117118
## Model Data
118119

119-
The data passed to the treeview's `initialModel` prop should be an array of nodes, where each node should have:
120+
Model data can be loaded either synchronously through the `initialModel` property or asynchronously through the `loadNodesAsync` property. If both are specified then the data from `initialModel` is overwritten when the `loadNodesAsync` function returns data.
121+
122+
The data passed to the treeview's `initialModel` prop or returned from `loadNodesAsync` should be an array of nodes, where each node should have:
120123

121124
* Required: A property with a value that will be used as the node's ID (by default the node looks for a property named `id`)
122125
* Required: A property with a value that will be used as the node's label (by default the node looks for a property named `label`)
@@ -266,14 +269,17 @@ If specified, the `modelDefaults` property of the treeview will be merged with n
266269
| treeViewNodeExpandedChange | Emitted when a node is expanded or collapsed | `target` The model of the target node <br/> `event` The original event |
267270
| treeViewNodeSelectedChange | Emitted when a node is selected or deselected | `target` The model of the target node <br/> `event` The original event |
268271
| treeViewNodeChildrenLoad | Emitted when a node's children are done loading asynchronously | `target` The model of the target node <br/> `event` The original event |
272+
| treeViewRootNodesLoad | Emitted when the root nodes are done loading asynchronously | |
269273

270274
## CSS Classes
271275

272276
The display of the treeview can be customized via CSS using the following classes. Class names are organized in a hierarchy, so a containing node's class is the prefix of its child classes.
273277

274278
| Class | Affects |
275279
|:---------------------------------------|:---------------------------------------------------------------------------------|
276-
| `grtv` | The top-level tree view list |
280+
| `grtv` | The top-level treeview list |
281+
| `grtv-wrapper` | The wrapper div around the list of root nodes and the loading placeholder |
282+
| `grtv-loading` | The placeholder shown when root nodes are loading asynchronously |
277283
| `grtvn` | A single node's list item |
278284
| `grtvn-self-selected` | A selected node |
279285
| `grtvn-self` | The div containing the current node's UI |
@@ -330,7 +336,7 @@ A customizations object may have the following properties:
330336
| classes.treeViewNodeSelfDeleteIcon | String | Classes to add to the `<i>` element containing the delete icon |
331337
| classes.treeViewNodeChildrenWrapper | String | Classes to add to the wrapper div around the list of child nodes and the loading placeholder |
332338
| classes.treeViewNodeChildren | String | Classes to add to the list of child nodes |
333-
| classes.treeViewNodeLoading | String | Classes to add to the loading placeholder |
339+
| classes.treeViewNodeLoading | String | Classes to add to the node children loading placeholder |
334340

335341
### Skins
336342

@@ -340,27 +346,28 @@ If adding classes isn't enough, the entire default styles of the TreeView can be
340346

341347
Sometimes the entire content of a node (_e.g._, the checkbox or text) needs customization beyond what is available through classes. In this case, some slots are available in the TreeView to allow this customization.
342348

343-
| Slot Name | Description | Props |
344-
|:----------|:------------------------------------------------------------|:---------------------------------------------------------------------------------------------------|
345-
| text | Replaces the span used for non-input content | model - The TreeViewNode's model |
346-
| | | customClasses - Any custom classes specified in `treeNodeSpec.customizations` |
347-
| checkbox | Replaces the label and content used for checkboxes | model - The TreeViewNode's model |
348-
| | | customClasses - Any custom classes specified in `treeNodeSpec.customizations` |
349-
| | | inputId - The ID for the input (as generated by the TreeViewNode) |
350-
| | | checkboxChangeHandler - The handler for checkbox change events. You should fire this on `change`. |
351-
| radio | Replaces the label and content used for radio buttons | model - The TreeViewNode's model |
352-
| | | customClasses - Any custom classes specified in `treeNodeSpec.customizations` |
353-
| | | inputId - The ID for the input (as generated by the TreeViewNode) |
354-
| | | radioChangeHandler - The handler for radio button change events. You should fire this on `change`. |
355-
| loading | Replaces the span used when loading children asynchronously | model - The TreeViewNode's model |
356-
| | | customClasses - Any custom classes specified in `treeNodeSpec.customizations` |
349+
| Slot Name | Description | Props |
350+
|:-------------|:------------------------------------------------------------|:---------------------------------------------------------------------------------------------------|
351+
| loading-root | Replaces the span used when loading children asynchronously | |
352+
| text | Replaces the span used for non-input content | model - The TreeViewNode's model |
353+
| | | customClasses - Any custom classes specified in `treeNodeSpec.customizations` |
354+
| checkbox | Replaces the label and content used for checkboxes | model - The TreeViewNode's model |
355+
| | | customClasses - Any custom classes specified in `treeNodeSpec.customizations` |
356+
| | | inputId - The ID for the input (as generated by the TreeViewNode) |
357+
| | | checkboxChangeHandler - The handler for checkbox change events. You should fire this on `change`. |
358+
| radio | Replaces the label and content used for radio buttons | model - The TreeViewNode's model |
359+
| | | customClasses - Any custom classes specified in `treeNodeSpec.customizations` |
360+
| | | inputId - The ID for the input (as generated by the TreeViewNode) |
361+
| | | radioChangeHandler - The handler for radio button change events. You should fire this on `change`. |
362+
| loading | Replaces the span used when loading children asynchronously | model - The TreeViewNode's model |
363+
| | | customClasses - Any custom classes specified in `treeNodeSpec.customizations` |
357364

358365
Example usage:
359366

360367
```html
361368
<tree-view id="customtree" :initial-model="model">
362369
<template #text="{ model, customClasses }">
363-
<!-- The tree view node's model is available, and built-in classes and overrides are available -->
370+
<!-- The treeview node's model is available, and built-in classes and overrides are available -->
364371
<marquee :title="model.treeNodeSpec.title"
365372
class="grtvn-self-text"
366373
:class="customClasses.treeViewNodeSelfText">

0 commit comments

Comments
 (0)