dijit.Tree not collapsing when reloaded - dojo

I have implemented a dijit tree with checkboxes as per the implementation provided on http://jsfiddle.net/5QcFY/14/ and that is working perfectly fine. I am displaying the tree on dialogue box. There are "categories" as parent nodes and "types" as the children nodes. Initially all the parents nodes are displayed collapsed. Once user selects options from children nodes from tree and closes the dialogue box, the selected items are getting passed to the further processing logic.
Below is my implementation:
typeTreeHandle = new dijit.Tree({
store: dataStore,
id: "docTree",
showRoot: false,
autoExpand: false,
_createTreeNode: function(args) {
var tnode = new dijit._TreeNode(args);
tnode.labelNode.innerHTML = args.label;
//As parent nodes don't need checkboxes
if (!args.isExpandable)
{
var cb = new dijit.form.CheckBox();
cb.set('checked', false);
cb.placeAt(tnode.labelNode, "first");
//To store reference of checkbox object to destroy afterwards.
checkBoxArr.push(cb);
dojo.connect(cb, "onChange", function() {
var treeNode = dijit.getEnclosingWidget(this.domNode.parentNode);
dojo.publish("/checkbox/clicked", [{
"checkbox": this,
"item": treeNode.item}]);
});
}
return tnode;
}
}, "myTree");
Now the issue is: when the user again open the dialog box, the previously expanded categories are still shown expanded. Because of which the loading of elements of tree gets slower. Even if I close the browser window and open again, I can still see the categories previously expanded. I tried destroying the references of the objects created for the checkboxes, but still the problem continues.
Any pointer related to this issue would be really appreciated.

The persist property is what you are looking for, it seems:
Enables/disables use of cookies for state saving.
By default, a Tree will remember which branches were opened/closed. To
use this feature you must specify an id for the Tree. To disable the
feature, set the “persist” parameter to false.
http://dojotoolkit.org/reference-guide/1.10/dijit/Tree.html#persistence
Somewhat confusingly, the API docs have the following to say about the autoExpand property (which I see you've tried already):
Fully expand the tree on load. Overrides persist.
It appears this is only true for autoExpand: true. Using persist: true (which is the default) will override autoExpand: false too, as you have seen.
http://jsfiddle.net/5QcFY/336/

Related

Cytoscape.js move nodes to allow for pasted nodes

I have to provide a copy/paste functionality using the dagre layout. The idea is the user copies a node and where ever they decide to "paste" it, the hierarchy of nodes copied, will be created there. This would mean that all nodes in the way would have to move.
I first thought maybe I could call layout again but that doesn't "fit" them in.
I'm still learning cytoscape.js so if this is a simple question, please excuse me.
This would be something quite hard to achieve, so hear me out:
First step:
Cytoscape has a extension called context-menus (demo)
Another extension is called clipboard (demo)
make yourself familiar with these two
Second step:
create your graph with these two extensions and a dagre graph
when you copy these nodes, make the copy function behave like this:
add the nodes with their edges in the right hierarchy
when you right click on a node, add a insert into hierarchy function, which adds the copied nodes into the graph
Code examples:
var options1 = {
// List of initial menu items
menuItems: [
{
id: 'addToHierarchy',
content: 'Add to hierarchy',
tooltipText: 'Add nodes to hierarchy here',
selector: 'node',
onClickFunction: function () {
// your handlerFunction
},
disabled: false
}
]
};
var instance = cy.contextMenus( options1 );
var options2 = {
clipboardSize: 0,
// The following 4 options allow the user to provide custom behavior to
// the extension. They can be used to maintain consistency of some data
// when elements are duplicated.
// These 4 options are set to null by default. The function prototypes
// are provided below for explanation purpose only.
// Function executed on the collection of elements being copied, before
// they are serialized in the clipboard
beforeCopy: function(eles) {},
// Function executed on the clipboard just after the elements are copied.
// clipboard is of the form: {nodes: json, edges: json}
afterCopy: function(clipboard) {},
// Function executed on the clipboard right before elements are pasted,
// when they are still in the clipboard.
beforePaste: function(clipboard) {},
// Function executed on the collection of pasted elements, after they
// are pasted.
afterPaste: function(eles) {}
};
var clipboard = cy.clipboard(options2);

How to enable openOnClick for a Dojo/Dijit Tree and still call onClick function

I want to be able to both select the folder node label to expand the tree while also calling the onClick function for the tree. I've set the openOnClick property of the tree equal to true which will enable the tree to expand when the node label is selected however then the onClick function is never called.
My code looks like this...
tree = new Tree({
model: treeModel,
openOnClick : true, // This prevents onClick function from firing
onClick: function(item, node, event){
// this function is never called because openOnClick is enabled
alert("This message will never appear");
},
}, "tree");
I need both openOnClick and onClick to work. I am using dojo 1.9
Any ideas???
After digging through the dijit/Tree.js source code here was the solution I came up with.
tree = new Tree({
model: treeModel,
// openOnClick : false, // Don't set openOnClick equal to true
onClick: function(item, node, event){
alert("This message will NOW appear!!!");
this._onExpandoClick({node: node}); // This will expand the node
},
}, "tree");
The logic in the Tree.js file checks the openOnClick value and based on its value will either expand the node or call the onClick function. I'm still not sure why the two functions are exclusive from one another.
Does anyone know why this is?

Dynatree onDragOver and onDrop work perfectly in IE9, but only intermittently in Chrome

I am trying to implement the drag and drop funcationality with the Dynatree plugin. It works completely as expected in IE 9. In Chrome, however, the functionality is intermittent. I have downloaded the latest .js files from the Dynatree web site, and also played around with the versions of jquery.js that I am referencing.
At first, I thought that in Chrome the onDragOver and onDrop functions that I had declared were simply not getting called. However, when I inserted console log statements, then launched the MVC project in which the Dynatree object was being rendered, I could see in the Chrome developer tools console monitor that the onDragStart function was always called without fail, but when I would drag the mouse around with the "attached" node, I could only get the onDragOver function to fire in what seemed like random spots. When I got the onDragOver to fire and actually "reference" or highlight one of the other nodes in the tree, the onDrop function would also work.
Ironically, this code below works perfectly in IE9. I can get it to work in Chrome, but only if I use it in a WebForms ASP.NET project. Now, I'm sure you're thinking, "Why not just use it in a WebForms project?". That remains a possibility (last resort), but with what we need to accomplish with saving changes to our database, the MVC will work much better for the other aspects of the site. The code below fairly closely follows one of the examples provided on the Dynatree home website.
Thanks for any help you can provide.
Update: I have discovered that it isn't that the onDragOver and onDrop functions are not being called. When viewed in Chrome using MVC, the dynatree is just very picky and sensitve to exactly where you click. This could entirely be a product of my json string, or some settings on Chrome. I just don't know yet. If I click to the left of the node, I can select it every time, and move it to where I want. I have to be very specific with where I drag it and drop it. In Chrome, in the web forms model, it seems as though the "areas" within which you can operate are simply much larger. ?
$(document).ready(function () {
$("#OrgTree").dynatree({
initAjax: {
url: $('#Path').val()
},
dnd: {
onDragStart: function (node) {
/** This function MUST be defined to enable dragging for the tree.
* Return false to cancel dragging of node.
*/
//alert(node);
logMsg("tree.onDragStart(%o)", node);
return true;
},
onDragOver: function (node, sourceNode, hitMode) {
/** Return false to disallow dropping this node. * */
logMsg("tree.onDragOver(%o, %o, %o)", node, sourceNode, hitMode);
// Prevent dropping a parent below it's own child
if (node.isDescendantOf(sourceNode)) {
return false;
}
// Prohibit creating children in non-folders (only sorting allowed)
//if (!node.data.isFolder && hitMode === "over") { return "after"; }
},
onDrop: function (node, sourceNode, hitMode, ui, draggable) {
/** This function MUST be defined to enable dropping of items on
* the tree.
*/
logMsg("tree.onDrop(%o, %o, %o)", node, sourceNode, hitMode);
if (node.isDescendantOf(sourceNode)) {
return false;
}
//alert(node, sourceNode, hitMode);
//sourceNode.expand();
sourceNode.move(node, hitMode);
},
onDragEnter: function (node, sourceNode) {
/** sourceNode may be null for non-dynatree droppables.
* Return false to disallow dropping on node. In this case
* onDragOver and onDragLeave are not called.
* Return 'over', 'before, or 'after' to force a hitMode.
* Return ['before', 'after'] to restrict available hitModes.
* Any other return value will calc the hitMode from the cursor position.
*/
// Prevent dropping a parent below another parent (only sort
// nodes under the same parent)
// Allowing dropping *over* a node will create a child of that node
if (node.isDescendantOf(sourceNode)) {
return false;
}
return ["before", "after", "over"];
}
}
});
})
I have found that the issue lies with the references to icon files and an older version of the jquery library being used. I'm still not sure why it wasn't an issue for IE, but was for chrome.
My theory on why the incorrect reference to the icon file was causing a problem was that when no icons were rendered on the Dynatree object, Chrome did not insert any "pixel address" on which to click. I realize that is not the correct term, but it is my theory. In IE, it did not seem to be a problem.
When I rendered the page after correcting the reference to the icon file, all worked as expected.

Preload all children for dojo dgrid

So I'm a first time user of dgrid, and I'm currently building my tree grid like this:
var SelectionGrid = new declare([OnDemandGrid, Selection, Keyboard]);
var myGrid = new SelectionGrid({
store: myStore,
selectionMode: "single",
style: {
width: '99%',
height: '99%'
},
columns: columns
});
My problem is, the grid shows metadata for features I'm drawing in openlayers. I've written code in openlayers so that whenever I click on a feature on the map, it fires an event to scroll to that item in the grid and select it. The grid, however, doesn't pre-load the children for each parent, it only fetches the children when the parent row is expanded.
Currently I'm programmatically expanding every row and then reclosing them to force it to fetch, but it's terribly slow and ends up causing browser warnings about javascript taking too long to run, etc.
Is this a side effect of using OnDemandGrid? Is there anyway to just have all the data loaded so it's all available when the grid is rendered?
I had another issue and wanted to post another answer in case anybody has the same problem. When not using the Tree plugin and just using the OnDemandGrid, I couldn't programattically scroll to items in the grid that hadn't been loaded because the element for the row was null.
I found a property that tells the OnDemandGrid the minimum number of items to load from the store, and just set it to the length of my data, so it loads everything at once. I know that defeats the purpose/benefit of the OnDemandGrid, but it's the functionality I need. Here's the code:
var SelectionGrid = new declare([OnDemandGrid, Selection, Keyboard, DijitRegistry]);
var grid = new SelectionGrid({
store: store,
selectionMode: "single",
columns: columns,
minRowsPerPage: data.length,
farOffRemoval: 100000
});
Here's the reference for minRowsPerPage and farOffRemoval:
http://dojofoundation.org/packages/dgrid/tutorials/grids_and_stores/
I wasn't able to preload all my data, but I just discovered the glory of dojo/aspect. Now I find the parent item that needs expanded and I just build some code inside an aspect/after block that gets executed as soon as that row is finished expanding/loading:
aspect.after(grid, "expand", function (target, expand) {
var row = grid.row(id);
console.info("found row: ", row)
grid.bodyNode.scrollTop = row.element.offsetTop;
grid.clearSelection();
grid.select(row);
},
true);
grid.expand(item.id, true);

Dojo Tree Refresh

Using Dojo 1.3, after adding a child (i.e. folder or item) to a tree, is there a way to have it reflected immediately via refresh or some other method?
From the official Dojo manual
Updating a Tree
People often ask:
how do I update a tree (adding or
deleting items?)
You can't update the
tree directly, but rather you need to
update the model. Usually the model is
connected to a data store and in that
case you need to update the data
store. Thus, you need to use a data
store that allows updates (through
it's official API), like
dojo.data.ItemFileWriteStore.
how do I refresh a Tree from the
store?
This isn't supported. The store
needs to notify the tree of any
changes to the data. Currently this is
really only supported (out of the box)
by dojo.data.ItemFileWriteStore, as
setting up a client-server dojo.data
source where the server notifies the
client whenever the data has changed
is quite complicated, and beyond the
scope of dojo, which is a client-only
solution.
Say, if your model has the query `{type:'continent'} - meaning any items with this property are top-level-items, then the following will model extension will monitor changes and refresh the tree's view
var dataStore = new ItemFileWriteStore( { ... });
new Tree({
store: dataStore,
model: new ForestModel({
onNewItem: function(item, parentInfo){
if(this.store.getValue(item, 'type') == 'continent'){
this._requeryTop();
}
this.inherited(arguments);
}
}
});
This should in turn call childrenChanged in the tree and update it every time a new item is added.
See model reference
As an addition, if the item added is not a toplevel item, an immediate update should be doable with this statement. parent is the treenode which has had an item added to its children.
tree._collapseNode(parent);
parent.state = 'UNCHECKED';
tree._expandNode(parent);
A more or less 'standard' refresh of the tree can be achieved by following. Reason for it not being added to base implementation, i think is because it will break the linkage with DnD features on a tree
dojo.declare("My.Tree", [dijit.Tree], {
// Close the store? (So that the store will do a new fetch()).
reloadStoreOnRefresh : true,
update: function() {
this.model.store.clearOnClose = this.reloadStoreOnRefresh;
this.model.store.close();
// Completely delete every node from the dijit.Tree
delete this._itemNodesMap;
this._itemNodesMap = {};
this.rootNode.state = "UNCHECKED";
delete this.model.root.children;
this.model.root.children = null;
// Destroy the widget
this.rootNode.destroyRecursive();
// Recreate the model, (with the model again)
this.model.constructor(this.model)
// Rebuild the tree
this.postMixInProperties();
this._load();
}
}
);
I've solved this WITHOUT needing the refresh.
_refreshNodeMapping: function (newNodeData) {
if(!this._itemNodesMap[newNodeData.identity]) return;
var nodeMapToRefresh = this._itemNodesMap[newNodeData.identity][0].item;
var domNode = this._itemNodesMap[newNodeData.identity][0].domNode;
//For every updated value, reset the old ones
for(var val in newNodeData)
{
nodeMapToRefresh[val] = newNodeData[val];
if(val == 'label')
{
domNode.innerHTML = newNodeData[val];
}
}
}