Support asynchronous loading tree data

This commit is contained in:
weibangtuo 2016-10-22 00:12:13 +08:00
parent 3fb6c43e19
commit 26e0197365
3 changed files with 63 additions and 19 deletions

View File

@ -38,26 +38,31 @@ Add the component in your vue view.
## Node Options ## Node Options
`[opt]` means optional property.
```javascript ```javascript
{ {
name: 'Node Name', name: 'Node Name',
title: 'Node Tag title attr', title: 'Node Tag title attr',
isParent: true, // isParent: true, // Requested for parent node
isOpen: true, // Control node to fold or unfold isOpen: false, // [opt] Control node to fold or unfold
icon: 'fa fa-folder', // Or other custom icons call by class name icon: 'fa fa-folder', //[opt] Icon class name
openedIcon: 'fa fa-folder-open', // [option] For parent. Show when isOpen == true, show icon if it's null or empty openedIcon: 'fa fa-folder-open', // [opt] For parent. Show when isOpen == true, show icon if it's null or empty
closedIcon: 'fa fa-folder', // [option] For parent. Show when isOpen != true, show icon if it's null or empty closedIcon: 'fa fa-folder', // [opt] For parent. Show when isOpen != true, show icon if it's null or empty
children: [], // for parent node only children: [], // Requested for parent node
buttons: [ // [] buttons: [ // [opt]
{ {
title: 'icon button tag title attr', //[option] title: 'icon button tag title attr', //[opt]
icon: 'fa fa-edit', icon: 'fa fa-edit',
click: function (node) { //[option] click: function (node) { //[opt]
// //
} }
} }
//... //...
] ],
showLoading: false, // [opt] For parent, when `node.showLoading && node._loading` and node is opened then show loading icon
onOpened: function (node) {}, // [opt]
onClosed: function (node) {} // [opt]
} }
``` ```

View File

@ -5,7 +5,6 @@
node.children.push({ node.children.push({
name: 'child node', name: 'child node',
parent: node, parent: node,
isOpen: true,
isParent: true, isParent: true,
children: [], children: [],
buttons: [ buttons: [
@ -78,7 +77,6 @@
}, },
{ {
name: 'Level1 add node', name: 'Level1 add node',
isOpen: true,
isParent: true, isParent: true,
children: [], children: [],
buttons: [ buttons: [
@ -88,7 +86,7 @@
click: function (node) { click: function (node) {
node.isOpen = true; node.isOpen = true;
node.children.push({ node.children.push({
name: 'level2 node', name: 'Level2 node',
parent: node, parent: node,
buttons: [ buttons: [
{ {
@ -105,8 +103,7 @@
] ]
}, },
{ {
name: 'level1-addNode', name: 'Level1-addNode',
isOpen: true,
isParent: true, isParent: true,
children: [], children: [],
buttons: [ buttons: [
@ -116,6 +113,27 @@
click: addNode click: addNode
} }
] ]
},
{
name: 'Level1 Ajax',
isParent: true,
children: [],
showLoading: true, // if (node.showLoading && node._loading) then show loading icon
onOpened: function (node) {
if (!node._loading) {
Vue.set(node, 'children', []); // Clean old data
node._loading = true; // Start Ajax
setTimeout(function () { //
node._loading = false; //Finish Ajax
for (var i = 1; i < 6; i++) {
node.children.push({name: 'Ajax Node ' + i});
}
}, 2000);
}
},
onClosed: function () {
// NOOP
}
} }
] ]
} }

View File

@ -2,11 +2,12 @@
'use strict'; 'use strict';
var VueTreeItem = Vue.extend({ var VueTreeItem = Vue.extend({
template: '<li :class="{parent_li: node.isParent}">' + template: '<li :class="{parent_li: node.isParent}">' +
'<i v-if="node.isParent" v-on:click="click(node)" class="fa icon-open-state" :class=\'{"fa-plus-square-o": !node.isOpen, "fa-minus-square-o": node.isOpen}\'></i>' + '<i v-if="node.isParent" v-on:click="toggle(node)" class="fa icon-open-state" :class=\'{"fa-plus-square-o": !node.isOpen, "fa-minus-square-o": node.isOpen}\'></i>' +
'<span :title="node.title">' + '<span :title="node.title">' +
'<i v-if="showIcon(node)" :class="nodeClass(node)"></i> {{node.name}}</span>' + '<i v-if="showIcon(node)" :class="nodeClass(node)"></i> {{node.name}}</span>' +
'<a v-for="btn in node.buttons" class="ml5" href="javascript:" :title="btn.title" v-on:click="btnClick(btn, node)"><i :class="btn.icon"></i></a>' + '<a v-for="btn in node.buttons" class="ml5" href="javascript:" :title="btn.title" v-on:click="btnClick(btn, node)"><i :class="btn.icon"></i></a>' +
'<ul v-if="node.children && node.children.length" v-show="node.isOpen">' + '<ul v-show="node.isOpen">' +
'<li v-show="node.showLoading && node._loading"><i class="fa fa-spinner fa-pulse"></i></li>' +
'<vue-tree-item v-for="item in node.children" :node="item"></vue-tree-item>' + '<vue-tree-item v-for="item in node.children" :node="item"></vue-tree-item>' +
'</ul>' + '</ul>' +
'</li>', '</li>',
@ -26,14 +27,34 @@
return node.closedIcon || node.icon; return node.closedIcon || node.icon;
} }
}, },
click: function (node) { toggle: function (node) {
node.isOpen = !node.isOpen; if (node.hasOwnProperty('isOpen')) {
node.isOpen = !node.isOpen;
} else {
Vue.set(node, 'isOpen', true);
}
}, },
btnClick: function (btn, node) { btnClick: function (btn, node) {
if (typeof btn.click === 'function') { if (typeof btn.click === 'function') {
btn.click(node); btn.click(node);
} }
} }
},
watch: {
'node.isOpen': function (val) {
if (!this.node.hasOwnProperty('_loading')) {
Vue.set(this.node, '_loading', false);
}
if (val) {
if (typeof this.node.onOpened === 'function') {
this.node.onOpened(this.node);
}
} else {
if (typeof this.node.onClosed === 'function') {
this.node.onClosed(this.node);
}
}
}
} }
}); });
Vue.component('vue-tree-item', VueTreeItem); Vue.component('vue-tree-item', VueTreeItem);