PDA

View Full Version : Tree Contextmenu Help



mknopp
9 Mar 2008, 7:30 PM
I am trying to emulate the context menu that is highlighted in the "What Ext JavaScript Library is all about" screencast by Scott Walter.

I am not having much luck figuring out how to trigger off of the nodes, which are created by a treeloader. I have searched the forums for information, but am having little luck finding what I am needing.

I have been able to get the contextmenu to trigger for the treepanel, but not just the elements within it. I also have not been able to figure out how to get the id for the node clicked on.

Any help would be much appreciated, even if it is no more then pointing to code where someone has interfaced a tree with a context menu.

Here is my js code for this.



var tree_loader = new Ext.tree.TreeLoader({
dataUrl: 'ext_test.php'
});

// Panel for the west
var nav = new Ext.tree.TreePanel({
title: 'Navigation',
region: 'west',
split: true,
width: 250,
collapsible: true,
margins:'3 0 3 3',
cmargins:'3 3 3 3',
useArrows: true,
autoScroll: true,
animate: true,
enableDD: false,
containerScroll: true,
loader: tree_loader
});

var root = new Ext.tree.AsyncTreeNode({
text: 'NComp Data',
draggable: false,
id: 'source'
});
nav.setRootNode(root);

var win = new Ext.Window({
title: 'Layout Window',
renderTo: 'content',
resizable: false,
x: 10,
y: 10,
constrain: true,
draggable: false,
closable:false ,
width:800,
height:350,
//border:false,
plain:true,
layout: 'border',

items: [nav, tabs]
});

win.show();
root.expand();

var contextMenu = new Ext.menu.Menu({
items:[{
text : 'Item 1',
handler : function(el) {alert(el);}
},{
text : 'Item 2',
handler : function(el) {alert(el);}
}]
});

nav.el.on('contextmenu', function(e, el) {
e.preventDefault();
//alert(e.getXY()+" "+el);
contextMenu.showAt(e.getXY())
});
});

evant
9 Mar 2008, 7:57 PM
Just to confirm, you want to show a context menu when the user right clicks on a node?

mknopp
10 Mar 2008, 4:08 AM
Yes.

I need to be able to right click on a node, retrieve the id of the node, and show a context menu.

Any help would be greatly appreciated.

mabello
10 Mar 2008, 2:23 PM
Ok, I've created an example, but I'm using an extension to load the treeView, but it's not the point.
The complete code is the following:


<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Product View</title>

<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />

<!-- GC -->
<!-- LIBS -->
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<!-- ENDLIBS -->

<script type="text/javascript" src="../../ext-all.js"></script>

<script type="text/javascript" src="panels.js"></script>

<!-- Common Styles for the examples http://extjs.com/forum/showthread.php?t=26428 -->
<link rel="stylesheet" type="text/css" href="../examples.css" />
<link rel="shortcut icon" href="~/Images/favicon.ico" />
</head>
<body>
<div id="tree-ct"></div>
</body>
<script type="text/javascript">

Ext.tree.MyTreeLoader = function(config){

Ext.apply(this, config);
Ext.tree.MyTreeLoader.superclass.constructor.call(this, config);

};

Ext.tree.MyTreeLoader = Ext.extend(Ext.tree.MyTreeLoader, Ext.tree.TreeLoader, {

treeNodeProvider: null,

loading: false,

load : function(node, callback){
if(this.clearOnLoad){
while(node.firstChild){
node.removeChild(node.firstChild);
}
}
if(this.doPreload(node)){ // preloaded json children
if(typeof callback == "function"){
callback();
}
}else if(this.treeNodeProvider){
this.requestData(node, callback);
}
},

requestData : function(node, callback){
if(this.fireEvent("beforeload", this, node, callback) !== false){
this.loading = true;
var nodesToAdd = this.treeNodeProvider.getNodes();
if(nodesToAdd)
this.processResponse(nodesToAdd, node, callback);
this.loading = false;
} else {
// if the load is cancelled, make sure we notify
// the node that we are done
if(typeof callback == "function"){
callback();
}
}
},

isLoading : function(){
return this.loading;
},

abort : function(){
},

processResponse : function(o, node, callback){
try {
node.beginUpdate();
for(var i = 0, len = o.length; i < len; i++){
var n = this.createNode(o[i]);
if(n){
node.appendChild(n);
}
}
node.endUpdate();
if(typeof callback == "function"){
callback(this, node);
}
}catch(e){
this.fireEvent("loadexception", this, node, e);
if(typeof callback == "function"){
callback(this, node);
}
}
},

updateTreeNodeProvider: function(obj){
if(this.treeNodeProvider)
this.treeNodeProvider.setData(obj);
}
});

var flag = true;//To toggle the refresh

Ext.onReady(function(){
//Data to load in the tree
var treeData1 = [
{
name: "Folder1",
files:['File1', 'File2', 'File3']
},{
name: "Folder2",
files:['Picture1', 'Picture2']
}
];
//Data to load in the tree
var treeData2 = [
{
name: "Pictures",
files:['Doc1.txt', 'Doc2.txt']
},{
name: "Documents",
files:['xxx.txt', 'yyy.txt']
}
];
//Create your treeNodeProvider; it is an object that must honored an "interface" with these 2 methods:
//setData(value) used internally by the instance of MyTreeLoader "class"
//getNodes(), returning the right structure that the treePanel can elaborate to refresh itself
var saveAction = new Ext.Action({
text: "Save",
handler: function(){
alert("Saving node of id->" + saveAction.selectedNode.attributes.id);
}
});
var userNodeInterceptor = {

click: function(node, eventObject){
var nodeId = node.attributes.id;
alert("Click on node of id->" + nodeId);
},

contextmenu : function(node, e){
if(!this.menu){ // create context menu on first right click
this.menu = new Ext.menu.Menu({
id:'feeds-ctx',
items: [saveAction]
});
this.menu.on('hide', onContextHide, this);
}
if(this.ctxNode){
this.ctxNode.ui.removeClass('x-node-ctx');
this.ctxNode = null;
}
//if(node.isLeaf()){
this.ctxNode = node;
this.ctxNode.ui.addClass('x-node-ctx');
this.ctxNode.select();//Select the node
saveAction.selectedNode = node;

this.menu.showAt(e.getXY());
//}
}
};
//userNodeInterceptor.click = userNodeInterceptor.click.createDelegate(this);
//userNodeInterceptor.contextmenu = userNodeInterceptor.contextmenu.createDelegate(this);

var onContextHide = function(scope){
if(this.ctxNode){
this.ctxNode.ui.removeClass('x-node-ctx');
this.ctxNode = null;
}
}

var treeNodeProvider = {
data: [],//Property in which are set the data to elaborate

getNodes: function() { //Here you process your data

var nodeArray = [];//The tree structure used to refresh the TreePanel
for(var i = 0; i < this.data.length; i++){
var folder = this.data[i];
var filesInFolder = folder.files;
//Create the parent node
var node = {
id: folder.name,
text: folder.name,
leaf: false,
children: [],
listeners: userNodeInterceptor
}
//Create the children node
for(var j = 0; j < filesInFolder.length; j++) {
var fileName = filesInFolder[j];
var childNode = {
id: fileName,
text: fileName,
leaf: true,
listeners: userNodeInterceptor
}
//Set the children to the parent node
node.children.push(childNode);
}
//Add the parent node to the nodeArray
nodeArray.push(node);
}
//return the tree structure here
return nodeArray;
},
setData: function(data) {//Called internally by Ext.tree.MyTreeLoader by the method updateTreeNodeProvider
this.data = data;
},
scope: this//Could be useful to use when you elaborates data to switch the context...not used in this example and it's not required
};

var myTreeLoader = new Ext.tree.MyTreeLoader({
treeNodeProvider: treeNodeProvider
});
//myTreeLoader.updateTreeNodeProvider(treeData1);//if you want to "preload" the TreePanel with this data

var basePanelCfg = {
title: 'TreePanel',
preloadChildren: true,
lines: false,
clearOnLoad: true,
rootVisible: false,
containerScroll: true,
frame: false,
collapsible: false,
animate: true,
loader: myTreeLoader,
tbar:[{
text: "Click here to Refresh",
handler: function(){
var rootNode = treePanel.getRootNode();//get the rootnode
var loader = treePanel.getLoader();//Get the loader, note that is of type MyTreeLoader
//Simulate an ajax call to the server to download your data that you need to visualize in the tree
if(flag)
loader.updateTreeNodeProvider(treeData2);//update the treeNodeProvider associated to the loader
else
loader.updateTreeNodeProvider(treeData1);//update the treeNodeProvider associated to the loader
//Actually refresh the tree UI
loader.load(rootNode);
flag = !flag
}
}]
};

var treePanel = new Ext.tree.TreePanel(basePanelCfg);

var root = new Ext.tree.AsyncTreeNode({
text: 'Root',
draggable: false
});

treePanel.setRootNode(root);
treePanel.render('tree-ct');
});
</script>
</html>


The part you are interested is: (you can do the same without the MyTreeLoader extension, but I had already the code to show the tree, you need to click the refresh button to populate the tree in the example above):

1)Create a litener for your nodes to show the context menu:


var userNodeInterceptor = {

click: function(node, eventObject){
var nodeId = node.attributes.id;
alert("Click on node of id->" + nodeId);
},

contextmenu : function(node, e){
if(!this.menu){ // create context menu on first right click
this.menu = new Ext.menu.Menu({
id:'feeds-ctx',
items: [saveAction]
});
this.menu.on('hide', onContextHide, this);
}
if(this.ctxNode){
this.ctxNode.ui.removeClass('x-node-ctx');
this.ctxNode = null;
}
//if(node.isLeaf()){
this.ctxNode = node;
this.ctxNode.ui.addClass('x-node-ctx');
this.ctxNode.select();//Select the node
saveAction.selectedNode = node;

this.menu.showAt(e.getXY());
//}
}
};
//userNodeInterceptor.click = userNodeInterceptor.click.createDelegate(this);
//userNodeInterceptor.contextmenu = userNodeInterceptor.contextmenu.createDelegate(this);

var onContextHide = function(scope){
if(this.ctxNode){
this.ctxNode.ui.removeClass('x-node-ctx');
this.ctxNode = null;
}
}


2)Attach the listener to your node (note that you can have different listeners for different node of course)



var node = {
id: folder.name,
text: folder.name,
leaf: false,
children: [],
listeners: userNodeInterceptor
}

listeners: userNodeInterceptor is the property you can use for your node

I hope this help

mknopp
10 Mar 2008, 8:20 PM
mabello, this was helpful.

Thank you.