ExtPersist - mar10/fancytree GitHub Wiki
About Fancytree persistence extension.
- Status: production
- example
- Discussion
Store and restore tree status using local/session storage or cookies:
- Store key of active and focused node as cookie.
- Store key list of expanded and selected nodes as cookie.
- Restore tree state from cookies when page is reloaded.
Persistence stores node keys, so the source data should contain reproducible
node.key
values.
Some notes about selection persistence
We currently store only the key of selected top nodes using
tree.getSelectedNodes(true)
and call fixSelection3AfterClick()
for those
nodes when they are restored.
Also, the overrideSource
option only considers nodes that have been
selected by the user and thus are persisted in the cookie.
In this case, a node will be selected in the reloaded tree, even if the Ajax
response contains selected: false
.
However, if the user deselects a previously selected node, this fact is not
stored in the cookie: the deselected keys are simply removed from the list, and
cannot be distinguisehd from nodes that where initially deselected.
Persistence for selection will work best, if the Ajax data does not contain
preselected nodes.
-
cookieDelimiter, type: {string}, default: '~'
Character used to join key strings. -
cookiePrefix, type: {string}, default: 'fancytree-<treeId>-'
Used to generate storage keys. -
cookie, type: {object}, default: use a session cookie
Options passed to$.cookie
plugin (only if cookies are used; see also 'store' option). -
expandLazy, type: {boolean}, default: false
true: recursively expand and load lazy nodes. -
expandOpts, type: {object}, default: {noAnimation: false, noEvents: false}
Optionalopts
argument passed to setExpanded(true, opts). -
fireActivate, type: {boolean}, default: true
false: suppressactivate
event after active node was restored. -
overrideSource, type: {boolean}, default: true
true: persisted information will be used, even if source data is set totrue
orfalse
.
false: persisted information will only be used if source data isundefined
. -
store, type: {string|object}, default: 'auto'
Storage type
'local': localStorage,
'session': sessionStorage,
'cookie': use js-cookie (or legacy jquery-cookie ) plugin.
'auto': use localStorage if available, fallback to 'cookie'.
Use 'local' (or 'cookie' with expiration settings) to store status over sessions.
Pass an object to define a custom storage provider. The provider must implement three methods. This example shows how the 'session' provider is implemented:store: { get: function(key){ return window.sessionStorage.getItem(key); }, set: function(key, value){ window.sessionStorage.setItem(key, value); }, remove: function(key){ window.sessionStorage.removeItem(key); } }
-
types, type: {string}, default: 'active expanded focus selected'
Which status types to store, separated by space.
-
beforeRestore
Fired before tree status is restored. Returnfalse
to prevent default processing. -
restore
Fired after tree status is restored.
-
{void} tree.clearCookies(types)
Deprecated. UseclearPersistData(types)
instead. -
{void} tree.clearPersistData(types)
Reset persistence data store.
{string} [types='active expanded focus selected'] -
{object} tree.getPersistData()
Return persistence information from storage.
In addition to jQuery, jQueryUI and Fancytree, include
js-cookie
(or
jquery-cookie
) and jquery.fancytree.persist.js
:
<script src="//code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/js-cookie/2.0.1/js.cookie.min.js"></script>
<link href="skin-win8/ui.fancytree.css" rel="stylesheet">
<script src="js/jquery-ui-dependencies/jquery.fancytree.ui-deps.js"></script>
<script src="js/jquery.fancytree.js"></script>
<script src="js/jquery.fancytree.persist.js"></script>
Enable persist
extension and pass options:
$("#tree").fancytree({
extensions: ["persist"],
checkbox: true,
persist: {
// Available options with their default:
cookieDelimiter: "~", // character used to join key strings
cookiePrefix: undefined, // 'fancytree-<treeId>-' by default
cookie: { // settings passed to jquery.cookie plugin
raw: false,
expires: "",
path: "",
domain: "",
secure: false
},
expandLazy: false, // true: recursively expand and load lazy nodes
expandOpts: undefined, // optional `opts` argument passed to setExpanded()
overrideSource: true, // true: cookie takes precedence over `source` data attributes.
store: "auto", // 'cookie': use cookie, 'local': use localStore, 'session': use sessionStore
types: "active expanded focus selected" // which status types to store
},
[...]
});
Clear all status types:
$.ui.fancytree.getTree("#tree").clearCookies();
// only selection data:
$.ui.fancytree.getTree("#tree").clearCookies("selected");
If the tree uses lazy loading, things may get complicated.
For example it may not be possible to re-activate a node on page reload, if this
node is not part of the initial tree.
In this case we have to load all parent nodes before.
Two options are available:
- Use the
expandLazy
option to re-load nested nodes. - Let the server evaluate the list of expanded nodes and return data accordingly.
Note This option is great way for lazy programmers to flood a server with
Ajax requests ;-), so also have a look at the next section.
Note This option requires that all nodes (includng the lazy responses) have
unique, reproducible keys.
$("#tree").fancytree({
extensions: ["persist"],
persist: {
expandLazy: true, // true: recursively expand and load lazy nodes
overrideSource: true, // true: cookie takes precedence over `source` data attributes.
...
},
[...]
});
Given this tree
$("#tree").fancytree({
extensions: ["persist"],
persist: {
expandLazy: false,
overrideSource: false,
store: "cookie", // force using cookies!
...
},
source: {
url: "getTreeNodes",
data: {rootKey: "root"}
},
lazyLoad: {
url: "getTreeNodes",
data: {rootKey: data.node.key}
},
[...]
});
we could let the server handle persistence (pseudo code, based on Python and CherryPy).
The server reads the key list of expanded nodes from the cookie and adds these child nodes to the response:
def get_child_data_list(node_key, expanded_key_list):
"""Return an array of child definitions for a given parent key."""
# Read `node` objects from our database
child_node_list = get_child_nodes(node_key)
# Convert nodes to an array of Fancytree data dicts
result_list = []
for node in child_node_list:
data = {"title": node.name,
"key": node.guid,
"lazy": True}
# If a node is listed in the cookie as expanded, also load its children
# recursively
if node.guid in expanded_key_list:
data["expanded"] = True
data["children"] = get_child_data_list(node.guid, expanded_key_list)
result_list.append(data)
return result_list
@cherrypy.exposed
def getTreeNodes(rootKey):
"""This is the web service handler, called by Fancytree's `lazyLoad`."""
if rootKey == "root":
# Client requests initial top level nodes: also deliver expanded children
expanded_key_list = get_cookie_value("fancytree-1-expanded").split("~")
else:
# Client requests children of a lazy node
expanded_key_list = []
# Get the (potentially nested) array of child node definitions
result_list = get_child_data_list(rootKey, expanded_key_list)
# and return the result as JSON
return to_json(result_list)