PDA

View Full Version : ColumnTree with radio group



ShatyUT
3 Dec 2009, 9:29 AM
Hi all-

I am trying to customize the ColumnTree example (http://www.extjs.com/deploy/dev/examples/tree/column-tree.html) to have the ability to add a radio group to each leaf node. To this point I have gotten to where I can display any number of radio buttons in each node's group by passing in an array of Strings as the values of each radio. The first time I click on any radio in the group, it selects that radio. Subsequent clicks do not change the radio group at all.

<see attached screenshot of columntree>

The odd thing is though, if I set a break point in the "onClick" event in Firebug, when I click an unchecked radio in the group, it now shows as checked and the other(s) unchecked. If I remove the break point the radios do not toggle anymore. How does setting a break point change the way it functions? :-/

Maybe there's something to the onRadioChange that I attempted to update from the onCheckChange from TreeNodeUI.js.

I believe the necessary code is below.

ColumnNodeRadioUI.js


/*!
* Ext JS Library 3.0.3
* Copyright(c) 2006-2009 Ext JS, LLC
* [email protected]
* http://www.extjs.com/license
*/
Ext.ns('Ext.ux.tree');

/**
* @class Ext.ux.tree.ColumnTree
* @extends Ext.tree.TreePanel
*
* @xtype columntree
*/
Ext.ux.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {
lines : false,
borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
cls : 'x-column-tree',

onRender : function(){
Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);
this.headers = this.header.createChild({cls:'x-tree-headers'});

var cols = this.columns, c;
var totalWidth = 0;
var scrollOffset = 19; // similar to Ext.grid.GridView default

for(var i = 0, len = cols.length; i < len; i++){
c = cols[i];
totalWidth += c.width;
this.headers.createChild({
cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
cn: {
cls:'x-tree-hd-text',
html: c.header
},
style:'width:'+(c.width-this.borderWidth)+'px;'
});
}
this.headers.createChild({cls:'x-clear'});
// prevent floats from wrapping when clipped
this.headers.setWidth(totalWidth+scrollOffset);
this.innerCt.setWidth(totalWidth);
}
});

Ext.reg('columntree', Ext.ux.tree.ColumnTree);

//backwards compat
Ext.tree.ColumnTree = Ext.ux.tree.ColumnTree;


/**
* @class Ext.ux.tree.ColumnNodeUI
* @extends Ext.tree.TreeNodeUI
*/
Ext.ux.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
focus: Ext.emptyFn, // prevent odd scrolling behavior

// private
onDisableChange : function(node, state){
this.disabled = state;
if (this.radioObjs) {
for (var i = 0; i < this.radioObjs.length; i++) {
this.radioObjs[i].disabled = state;
}
}
if(state){
this.addClass("x-tree-node-disabled");
}else{
this.removeClass("x-tree-node-disabled");
}
},

// private
onClick : function(e){
if(this.dropping){
e.stopEvent();
return;
}
if(this.fireEvent("beforeclick", this.node, e) !== false){
var a = e.getTarget('a');
if(!this.disabled && this.node.attributes.href && a){
this.fireEvent("click", this.node, e);
return;
}else if(a && e.ctrlKey){
e.stopEvent();
}
e.preventDefault();
if(this.disabled){
return;
}

if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){
this.node.toggle();
}

this.fireEvent("click", this.node, e);
}else{
e.stopEvent();
}
},

// private
onRadioChange : function(){
for (var i = 0; i < this.radioObjs.length; i++) {
var checked = this.radioObjs[i].checked;
// fix for IE6
this.radioObjs[i].defaultChecked = checked;
this.node.attributes.checked = checked; // TODO ? Not sure what this line is for
}
this.fireEvent('radiochange', this.node, checked);
},

toggleCheck : function(value){
var cb = this.checkbox;
if(cb){
cb.checked = (value === undefined ? !cb.checked : value);
this.onRadioChange();
}
},

renderElements : function(n, a, targetNode, bulkRender){
this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';

var tree = n.getOwnerTree();
var cols = tree.columns;
var bw = tree.borderWidth;
var c = cols[0];
var radio = Ext.isDefined(a.radioValues);
var numRadios = 0;

var radioHTML = '';
// If the configuration calls for radio buttons, then construct the HTML here for each radio
if (radio){
var radioVals = a.radioValues;
numRadios = radioVals.length;
for (var i = 0; i < numRadios; i++) {
radioHTML += '<input class="x-tree-node-cb" name="'+n.id+'radio" type="radio" value="' + radioVals[i] + '" />';
}
}

var buf = [
'<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">',
'<div class="x-tree-col" style="width:',c.width-bw,'px;">',
'<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">',
radioHTML,
'<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ',
a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',
'<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",
"</div>"];
for(var i = 1, len = cols.length; i < len; i++){
c = cols[i];

buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
'<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",
"</div>");
}
buf.push(
'<div class="x-clear"></div></div>',
'<ul class="x-tree-node-ct" style="display:none;"></ul>',
"</li>");

if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
n.nextSibling.ui.getEl(), buf.join(""));
}else{
this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
}

this.elNode = this.wrap.childNodes[0];
this.ctNode = this.wrap.childNodes[1];
var cs = this.elNode.firstChild.childNodes;
this.indentNode = cs[0];
this.ecNode = cs[1];
this.iconNode = cs[2];
var index = 3;
if (radio) {
this.radioObjs = new Array();
for (var i = 0; i < numRadios; i++) {
this.radioObjs[i] = cs[index++];
}
}
this.anchor = cs[index];
this.textNode = cs[index].firstChild;
}
});

//backwards compat
Ext.tree.ColumnNodeUI = Ext.ux.tree.ColumnNodeUI;
column-tree.js


/*!
* Ext JS Library 3.0.3
* Copyright(c) 2006-2009 Ext JS, LLC
* [email protected]
* http://www.extjs.com/license
*/
function CommentsLinkCell(value, metaData, record, rowIndex, colIndex, store) {
if (value != null)
return "<a href='#' style='text-decoration:underline;color:blue' onClick=displayComments(\"" + value + "\")>Add Comments</a>";
else
return '';
}

function displayComments(text) {
var win = new Ext.Window({
layout:'fit',
title: 'Add Comments',
width:400,
height:250,
modal: true,
plain: true,

items: new Ext.Panel({
deferredRender:false,
border:false,
layout:'border',
items:[{
xtype:'textarea',
region:'center',
id:'comments',
fieldLabel:'Comments',
height:'auto',
width:'auto',
value:text,
anchor:'98%'
}]
}),

buttons: [{
text:'Submit',
disabled:true
},{
text: 'Close',
handler: function(){
win.hide();
}
}]
});
win.show(this);
}

Ext.onReady(function(){
var tree = new Ext.ux.tree.ColumnTree({
width: 550,
height: 200,
rootVisible:false,
autoScroll:true,
title: 'Example Tasks',
renderTo: Ext.getBody(),

columns:[{
header:'Task',
width:330,
dataIndex:'task'
},{
header:'Comments',
width:100,
dataIndex:'comments',
renderer: CommentsLinkCell
},{
header:'Assigned To',
width:100,
dataIndex:'user'
}],

loader: new Ext.tree.TreeLoader({
dataUrl:'column-data.json',
uiProviders:{
'col': Ext.ux.tree.ColumnNodeUI
}
}),

root: new Ext.tree.AsyncTreeNode({
text:'Tasks',
children: [{
task:'NOTE',
comments:null,
user:'',
uiProvider:'col',
cls:'master-task',
iconCls:'task-folder',
expanded: true,
children:[{
task:'Document is missing',
comments:null,
user:'',
uiProvider:'col',
iconCls:'task-folder',
expanded: true,
children:[{
task:'Document is missing',
comments:'Test',
user:'Field',
uiProvider:'col',
leaf:true,
iconCls:'task',
radio:true, // true to display a radio button group
radioValues:['Yes','No'], // Values for radio buttons...implies number of buttons in group
expanded: true
}]
}]
},{
task:'PHOTO ID',
comments:null,
user:'',
uiProvider:'col',
cls:'master-task',
iconCls:'task-folder',
children:[{
task:'Terms do not match system',
comments:null,
user:'',
uiProvider:'col',
iconCls:'task-folder',
expanded: true,
children:[{
task:'Address is incorrect',
comments:'',
user:'Paralender',
uiProvider:'col',
leaf:true,
iconCls:'task',
radio:true, // true to display a radio button group
radioValues:['Yes','No'], // Values for radio buttons...implies number of buttons in group
expanded: true
},{
task:'ID is expired',
comments:'ID shows expired date.',
user:'Paralender',
uiProvider:'col',
leaf:true,
iconCls:'task',
radioValues:['Yes','No'], // Values for radio buttons...implies existence of radio group and number of buttons in group
expanded: true
}]
}]
}]
})
});
});

ShatyUT
4 Dec 2009, 3:04 PM
I will also want to add a header above the leaf node with the radio group. Since there can be multiple leaf nodes under one folder (unlike the screenshot) I guess the header will need to be another leaf node? I want to put something above each radio to denote what they are for such as a "Y" and "N" above them for "Yes" and "No" options. Any pointers on how to ensure those headers line up with each radio would be greatly appreciated.

If I'll need to tackle that in another thread, that is fine. Right now functionality is more important to me than look-and-feel as that can always be tweaked.