-
-
Notifications
You must be signed in to change notification settings - Fork 601
ExtTable
About Fancytree table and gridnav extension.
Render tree as a table (aka tree grid) and support keyboard navigation in a grid with embedded input controls.
Options of the ext-table extension:
-
checkboxColumnIdx, type: {integer}, default: null
Render the checkboxes into the 1st column. -
customStatus, type: {boolean}, default: false true: generate renderColumns events for status nodes Deprecated! Use the global tree eventrenderStatusColumns
instead -
indentation, type: {integer}, default: 16
Indent every node level by 16px. -
nodeColumnIdx, type: {integer}, default: 0
Render node expander, icon, and title to column #0
Options of the ext-gridnav extension:
-
autofocusInput, type: {boolean}, default: false
Focus first embedded input if node gets activated. -
handleCursorKeys, type: {boolean}, default: true
Allow UP/DOWN in inputs to move to prev/next node.
-
renderColumns Render table columns for this node's <tr>. Note that the columns defined by
nodeColumnIdx
andcheckboxColumnIdx
have already been handled by default, when this event is triggered.
Note also that static markup (for example<input>
elements) could also be created in thecreateNode
event, thus preventing replacing the content every time a node is redrawn.
See examples below. -
renderStatusColumns Custom rendering callback for status nodes (i.e. 'errors', 'loading...', etc.).
If this option is set to true instead of a callback,renderColumns
is called, even for status nodes.
If this option is set to false, which is the default, standard rendering is performed.
@since v2.15
- n.a.
In addition to jQuery, jQuery UI, and Fancytree, include jquery.fancytree.table.js
and optionally jquery.fancytree.gridnav.js
:
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<link href="skin-win8/ui.fancytree.css" rel="stylesheet">
<script src="js/jquery.fancytree.js"></script>
<script src="js/jquery.fancytree.gridnav.js"></script>
<script src="js/jquery.fancytree.table.js"></script>
We also define an empty table with its headers:
<table id="tree">
<colgroup>
<col width="30px"></col>
<col width="30px"></col>
<col width="*"></col>
<col width="50px"></col>
<col width="30px"></col>
</colgroup>
<thead>
<tr> <th> </th> <th>#</th> <th>Name</th> <th>Custom Data</th> <th>Important</th> </tr>
</thead>
<tbody>
</tbody>
</table>
The tree table extension takes care of rendering the node into one of the columns.
Other columns have to be rendered in the renderColumns
event.
Example:
$("#tree").fancytree({
checkbox: true,
titlesTabbable: true, // Add all node titles to TAB chain
source: SOURCE,
extensions: ["table", "gridnav"],
table: {
checkboxColumnIdx: 0, // render the checkboxes into the this column index (default: nodeColumnIdx)
indentation: 16, // indent every node level by 16px
nodeColumnIdx: 2 // render node expander, icon, and title to this column (default: #0)
},
gridnav: {
autofocusInput: false, // Focus first embedded input if node gets activated
handleCursorKeys: true // Allow UP/DOWN in inputs to move to prev/next node
},
// renderStatusColumns: false, // false: default renderer
// true: generate renderColumns events, even for status nodes
// function: specific callback for status nodes
renderColumns: function(event, data) {
var node = data.node,
$tdList = $(node.tr).find(">td");
// Make the title cell span the remaining columns if it's a folder:
if( node.isFolder() ) {
$tdList.eq(2)
.prop("colspan", 3)
.nextAll().remove();
return;
}
// (Column #0 is rendered by fancytree by adding the checkbox)
// Column #1 should contain the index as plain text, e.g. '2.7.1'
$tdList.eq(1)
.text(node.getIndexHier())
.addClass("alignRight");
// (Column #2 is rendered by fancytree)
// ...otherwise render remaining columns
$tdList.eq(3).text(node.data.myCustomData);
$tdList.eq(4).html("<input name="important" type='checkbox' value='" + node.key + "'>");
}
});
If cell content does not depend on changing node data or status, it may be more
efficient to create this markup on node creation, and let renderColumns
handle the variant rendering only:
// Called when node markup is created for the first time:
createNode: function(event, data) {
var node = data.node,
$tdList = $(node.tr).find(">td");
// Span the remaining columns if it's a folder.
// We can do this in createNode instead of renderColumns, because
// the `isFolder` status is unlikely to change later
if( node.isFolder() ) {
$tdList.eq(2)
.prop("colspan", 6)
.nextAll().remove();
}
$tdList.eq(4).html("<input type='text'>");
},
// Called every time node status is changed, or `node.render()` is called:
renderColumns: function(event, data) {
var node = data.node,
$tdList = $(node.tr).find(">td");
// ...
$tdList.eq(3).text(node.data.myCustomData);
$tdList.eq(4).find("input").val(node.data.userName);
}
An even more performant approach is to define a row template, by providing a
single <tr>
element in the <tbody>
.
Here we can define invariant markup, classes, and other attributes to initialize
every node:
<table id="tree">
<colgroup>
...
</colgroup>
<thead>
<tr> <th></th> <th>#</th> <th></th> <th>Ed1</th> <th>Rb1</th> <th>Cb</th></tr>
</thead>
<tbody>
<!-- Define a row template for invariant markup and attributes: -->
<tr>
<td></td>
<td></td>
<td></td>
<td><input name="input1" type="input"></td>
<td><input name="cb1" type="checkbox"></td>
<td>
<select name="sel1">
<option value="a">A</option>
<option value="b">B</option>
</select>
</td>
</tr>
</tbody>
</table>
Now we only need to update non-static parts:
renderColumns: function(event, data) {
var node = data.node,
$tdList = $(node.tr).find(">td");
$tdList.eq(1).text(node.getIndexHier());
$tdList.eq(4).find("input").val(node.data.userName);
}
TIP: Specific columns can be styled using CSS rules like this:
#tree >thead th:nth-child(4),
#tree >tbody td:nth-child(4) {
text-align: center;
}
Sometimes we need to add class names or attributes to cells of every row, for
example to add responsive support with bootstrap.
This may be done in the renderColumns
callback:
renderColumns: function(event, data) {
...
$tdList.eq(2)
.text(node.data.details)
.addClass("hidden-xs hidden-sm");
}
However a more performant approach is to define this as part of a row template:
<table id="tree">
<thead>
<tr> <th>Key</th> <th>Title</th> <th class="hidden-xs hidden-sm">Details</th></tr>
</thead>
<tbody>
<tr> <td /> <td /> <td class="hidden-xs hidden-sm" /> </tr>
</tbody>
</table>
TODO: describe how to customize status nodes, e.g. use colspan to render across columns, etc.
Sometimes we may want to maintain column-specific meta data for the tree, instead of passing this over and over again with every node.
NOTE: This pattern recommendation is not final yet.
The recommended pattern is
$("#tree").fancytree({
// `columns` is a tree option, that can be used to define shared data per column.
// We can pass it directly as Fancytree option like this:
columns: [
{tooltip: null, addClass: ""},
{tooltip: "Detail info", addClass: "hidden-xs hidden-sm"},
{tooltip: null, addClass: ""},
],
source: { url: "/my/web/service" },
renderColumns: function(event, data) {
...
$tdList.eq(1)
.text(node.data.details)
.addClass(tree.columns[1].addClass)
.attr("title", tree.columns[1].tooltip);
$tdList.eq(2)
.text(node.data.details)
.addClass(tree.columns[2].addClass)
.attr("title", tree.columns[2].tooltip);
}
});
Sometimes it may be more convenient to get the shared columns
info from the server.
The source JSON data can be a list of child nodes or an object with additional meta data.
Here we use the latter format to pass column information as well:
{
"children": [
{"title": "Books", "folder": true, "children": [
{"title": "Little Prince", "price": 1.23},
{"title": "The Hobbit", "price": 2.34}
]},
{"title": "Computers", "folder": true, "children": [
{"title": "PC", "price": 549.85},
{"title": "Mac", "price": 789.00}
]}
],
"columns": [
{"tooltip": null, "addClass": ""},
{"tooltip": "Detail info", "addClass": "hidden-xs hidden-sm"},
{"tooltip": null, "addClass": ""}
]
}
Wrap the table into a scrollable <div>
<div id="scrollParent" style="height: 150px; overflow: auto;">
<table id="tree">
...
</table>
</div>
and tell Fancytree to use this as scrollParent:
$("#tree").fancytree({
extensions: ["table", "gridnav"],
source: SORUCE,
scrollParent: $("#scrollParent"),
table: {
...
},
Alternativley, we can use the browser viewport as scroll parent:
$("#tree").fancytree({
...
scrollParent: $(window),
...
If the table layout was modified, for example by adding or removing a column, we also need to update the template that Fancytree uses to render rows, e.g.:
// For example append a header column
$("#tree thead tr").append("<th>New</th>");
// Update the row template accordingly (available as tree.rowFragment)
$(">tr", tree.rowFragment).append("<td>");
// Redraw all
tree.render(true);
TODO: describe addPagingNode()
etc.
TODO.
Documentation Home - Project Page - Copyright (c) 2008-2022, Martin Wendt (https://wwWendt.de)