Lazy Loading in dynatree - lazy-loading

In this component
http://wwwendt.de/tech/dynatree/index.html
under 5.4 Loading child nodes on demand ('lazy loading')
it seems that the only way to load the tree nodes in a lazy manner is to grab them from web service. What if I want to grab the nodes from a data structure?
such as:
onLazyRead: function(dtnode){
dtnode.appendAjax({
//url: "sample-data2.json"
//grab the nodes here from array
});
}
Any advice?
Another question: when onLazyRead is executed, does the whole tree get re-rendered again?

You could use
dtnode.addChild(dict);
instead of
.appendAjax(...)
In either case, only the changed nodes should get rendered.
dict must be formatted like the JSON response. You can als generate dict from a given tree or node using the toDict() method.

Related

Cytoscape layouts - Handle locked nodes

I am using vue-cytoscape to render a graph and navigate through a tree-like data structure.
My goal is to expand parent nodes and keep their position in the graph. I would like to simply add the new children nodes.
My approach is to lock current nodes, add the children and unlock the nodes.
this.cy.nodes().lock()
for(let d of data){
this.cy.add(d)
}
this.cy.elements().layout(this.config.layout).run()
setTimeout(() => {this.cy.nodes().unlock()}, 2000) // Give some time for the layout to render before unlocking nodes.
The problem is that the layouts do not consider the locked state of the nodes. Only the new nodes are moved around, which is fine. But the layout is not respected. I am under the impression that the layout calculates a new position for all nodes, but then moves only nodes that are unlocked.
According to this GitHub issue, some layout algorithm should handle locked nodes. I am using the following layouts and none seem to consider locked nodes.
Cola
Fcose
Dagre
avsdf
grid
concentric
Please try calling the layout function only on the added nodes:
var eles = cy.add(data); // refer to http://js.cytoscape.org/#cy.add for adding nodes
eles.layout(this.config.layout).run();
If you don't want nodes to move when calling the layout function, you can exclude them from the rendering. While calling cy.add(), the function returns an object with every added element inside (see var eles = ... in the code).

trying to expand out nodes to null parent

I'm using cytoscape.js and cytoscape.js-expand-collapse to create dynamic hierarchies in my graph structure. I would like to be able to dynamically create a collapsed(merged) node that could potentially be expanded out, removed or possibly re-merged with additional nodes. I am having trouble whenever I call nodes.move({parent:null}). They become detached from the graph and I cannot re-attach them to a new parentNode. If I call restore on them I will see errors saying they already exist in the graph.
merge and unmerge by itself works fine in a simple case of no existing collapsed nodes. However calling merge on something that already contains a compound collapsed node breaks things. I would get the resulting merged node without the children of the previously collapsed merge candidate
Update #1 I've realized my problem was returning the wrong moved nodes. Calling .move on a collection returns a new set of nodes. so the unmerge should return those instead.
Update #2 cytoscape-expand-collapse utility does some internal book-keeping during the parent node collapse/expand by tucking away node data in 'originalEnds' data prop on the meta edges. so, if I'm now altering the graph dynamically by moving nodes in/out of parents these originalEnds pointers get out of sync causing infinite loops. I am doing some additional node tracking of my own as a workaround.
function merge(nodes){
//if one of the nodes is a compound itself, first uncollapse, then merge
var collapsed = nodes.filter(function(c){return typeof c.data('collapsedChildren')!=='undefined';});
if (collapsed.length>0) {
// for now just assume collapsed is a length of 1
var unmerged = unmerge(collapsed); //unmerged should be now the former collapsed children
nodes = nodes.subtract(collapsed).union(unmerged.nodes());
}
var parentNode = cy.add({group:'nodes', data: {id: parentID}});
nodes.move({parent: parentID});
collapseApi.collapse(parentNode);
return parentNode;
}
function unmerge(parentNode){
collapseApi.expand(parentNode);
var children = parentNode.children();
var moved = children.move({parent:null});
//at this point children become "detached" from the graph
// .removed() returns true, however, calling restore() logs an error and they are still technically in the graph
parentNode.remove();
return moved;
}
ele.move() has a more convenient implementation in cytoscape>=3.4: The elements are modified in-place instead of creating replacement elements.
Old code that uses the returned collection from ele.move() will still work. New code can be simplified by not having to use the returned collections at all.

Add compound node on demand cytoscape.js

I was wondering how feasible is to add some compound elements (nodes) "on the go".
Scenario:
I have a network with nodes from n1...n10 and edges. Depending on the button the user clicks, it redraws my network including nodes inside a compound node.
Example:
When you open the graph, you have n1..n10, without compounds, but when you click on a pre-defined button, my new graph now would be:
A new compound node with n1:n5 inside (parent), and the rest n6:n10 would stay the same (outside compound).
How feasible is it ?
I've tried :
cy.batchData({
"5": {
parent: "n0" // new element I added earlier
}});
to update my element id=5 to have n0 as parent, but it haven't worked.
The main idea is to represent data (graph) with biological insight, where the "new compound area" would be a pathway or a metabolic path (or whatever I want to represent there), one by one, so the visualization won't be a mess.
Thank you.
You can't change the parent field; it's immutable. You may have to remove the elements and add the second (resultant) graph via eles.remove() and cy.add() respectively.

ExtJS 4: Add children tree nodes to a non-Root node dynamically?

I am trying to add children tree nodes to a non-Root node dynamically when it's selected.
This is how I implemented it:
First, I populate my tree with a TreeStore then to dynamically add children tree nodes, I handle it on the itemexpand event in tree Controller.
//pThis is a NodeInterface obj of the selected node
itemexpand: function (pThis, pEOpts) {
//type is an attribute that I added so that I know what type of node that got selected
if (pThis.raw.type === 'Staff' && !pThis.hasChildNodes()) {
var staffNodeAry = [{text:"Teachers", type:"ChildStaff", leaf: false},
{text:"Principals", type:"ChildStaff", leaf: false}];
pThis.appendChild(staffNodeAry);
}
}
This worked quite good because I am able to see both "Teachers" and "Principals" node under "Staff" type but there are 2 issues that I found so far:
1) There's this error on the console:
Uncaught TypeError: Cannot read property 'internalId' of undefined
I don't know why but if anyone could enlighten that would be great.
2) When I expand either "Teachers" and "Principals", the NodeInterface obj that's returned by itemexpand has an undefined raw object.
So I couldn't do this: pThis.raw.type
From my understanding, I don't need to create a new TreeStore to add any new children nodes, I thought that by creating an array of objects that adhere to NodeInterface should be enough but I might be wrong.
If anyone could point me to the right direction, that would be great.
Handi
More info:
What I did was actually following the ExtJs 4 documentation so I believe this is a bug on ExtJs 4 framework.
I also tested this with Extjs-4.1.0-rc3 and it also produced the same error.
I have resolved this issue changing the event itemexpand with the event afteritemexpand. I have no documentation to justify this. The debugger says to me that the view listen for the itemexpand event and do something that goes wrong if add a node manually...
For the question number 2: you have no raw object because it is created by the reader of the store after the load operation. So if you add a node by hand the raw object does not exists.
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.Model-property-raw
The documentation for .appendChild() functions says:
node : Ext.data.NodeInterface/Ext.data.NodeInterface[]
The node or Array of nodes to append
And you are trying to append an array of 2 objects, but they are not "NodeInterface" instances. So you should create them properly like so:
var nodeToAppend1 = pThis.createNode({text:"Teachers", type:"ChildStaff", leaf: false});
pThis.appendChild(nodeToAppend1);
I remember making the same mistake a year ago, so I hope this helps.
I think it happens because your Ext.TreePanel working asyncroniously. So add process is:
Append Child, add virtual child leaf to your tree, with red mark in corner
Call store's create api
Receive response, if it success=true, change with virtual
The goal of changing is set internakId property, so you can add new child only after this change happens. I think better is write some recursive function and bind it to store's added event
tree.getStore().on('update', addChild);
or
tree.getStore().load({
success: function() {
addChild()
});

libxml - looping through all children of a child

I am new to libxml. I would like to write 1 loop to loop through all the children of a child node etc. e.g.
<par>i want to <bold>loop <italic>through </italic> all</bold> children in this node</par>
At the moment my looping code look as follows but I only get the "bold" node and not the "italic" child.
if (xmlStrEqual(node->name, BAD_CAST "p")) {
xmlNodePtr child = node->children;
while (child != NULL) {
child = child->next;
}
}
It is conceivable that the node structure could grow to 4-5 elements, so I need a solution which is more robust that putting while loops within while loops. Any help would be appreciated please.
The xmlTree interface you're using is heirarchical. <par> has 3 children - the text before <bold>, <bold> itself, and the text after. <bold> has children of its own, which includes <italic>. The node->children list only contains the immediate children of a node.
There are a couple ways to get the behavior that you want.
While processing child, you can recursively process child->children to get to <bold>'s child, which includes <italics>
If you're looking SPECIFICALLY for that <itallic> (or for a set of specific nodes), you can use an XPath expression, such as "par//italic" to find any italic node anywhere beneath par.
http://www.xmlsoft.org/examples/index.html#XPath
You could look at libxml2's xmlTextReader interface rather than this xmlTree. It provides the serial "read through" of the document that you're expecting.
http://xmlsoft.org/xmlreader.html
Each method has its own advantages/disadvantages, depending on what all you're trying to do with your application.