PDA

View Full Version : [Solved]Help needed with dynamically adding and removing row from grid.



VATigers
5 Nov 2009, 2:57 PM
Hello guys,

I have been working on a requirement to add and delete rows from grid dynamically. I created a extension called 'ButtonColumn' which would be rendered as buttons. In my requirement, I am using the column to delete the records .

It seems, I am getting the wrong dataindex when I click delete. If I add few records and then delete and add some rows, the rowIndex gets messed up.

Here is the custom button column extension, I created. I am also attaching the html file.




//create the plugins namespace
Ext.fmPlugins = {};
Ext.fmPlugins.parseInt = function (str, defVal) {
var val = parseInt(str);
return isNaN(val) ? defVal : val;
}
Ext.fmPlugins.override(Ext.grid.GridPanel, {
onClick: function (e) {
Ext.grid.GridPanel.prototype.onClick.base.apply(this, arguments);
var target = e.getTarget();
if (target.tagName === "BUTTON" && !target.isDisabled) {
var colIndex = Ext.fmPlugins.parseInt(target.getAttribute("col"), -1);
if (colIndex >= 0) {
var col = this.getColumnModel().columns[colIndex];
var fn = col['processCellClick'];
if (Ext.isFunction(fn)) {
var rowIndex = Ext.fmPlugins.parseInt(target.getAttribute("row"), -1);
fn.call(col, rowIndex, colIndex, e);
}
}
}
}
});
Ext.fmPlugins.ButtonColumn = Ext.extend(Ext.grid.Column, {
buttonClass: '',
constructor: function (config) {
config = config || {};
this.text = config.text;
this.buttonClass = config.buttonClass;
Ext.fmPlugins.ButtonColumn.superclass.constructor.call(this, config);
this.addEvents('buttonColumnInfoNeeded', 'buttonClick');
Ext.util.Observable.apply(this, arguments);
this.renderer = this.buttonColumnRenderer.createDelegate(this);
if (!Ext.fmPlugins.ButtonColumn.enabledBtnTemplate) {
Ext.fmPlugins.ButtonColumn.enabledBtnTemplate = this.createButtonTemplate(true);
}
if (!Ext.fmPlugins.ButtonColumn.disabledBtnTemplate) {
Ext.fmPlugins.ButtonColumn.disabledBtnTemplate = this.createButtonTemplate(false);
}
},
buttonColumnRenderer: function (value, metadata, record, rowIndex, colIndex, store) {
var eventArgs = {
colIndex: colIndex,
rowIndex: rowIndex,
enable: true,
record: record
};
var disabled = this.fireEvent('buttonColumnInfoNeeded', this, eventArgs);
var templateValues = {
cls: this.buttonClass || '',
value: this.text,
rowIndex: rowIndex,
colIndex: colIndex
};
return this.getButtonColumnHTML(disabled, rowIndex, colIndex);
}
,
processCellClick: function (rowIndex, colIndex, e) {
alert(rowIndex);
this.fireEvent('buttonClick', this, rowIndex);
}
,
getButtonColumnHTML: function (disabled, rowIndex, colIndex) {
var templateValues = {
cls: this.buttonClass || '',
value: this.text,
rowIndex: rowIndex,
colIndex: colIndex
};
if (disabled) {
return Ext.fmPlugins.ButtonColumn.disabledBtnTemplate.apply(templateValues);
}
return Ext.fmPlugins.ButtonColumn.enabledBtnTemplate.apply(templateValues);
}
,
createButtonTemplate: function (enabled) {
var templateArray = new Array();
var disabledClass = '';
var disabled = '';
var readOnlyText = '';
if (!enabled) {
var disabledClass = 'x-item-disabled';
var disabled = 'disabled';
var readOnlyText = 'readOnly';
}
var templateStart = String.format('<TABLE style="WIDTH: auto" class="x-btn {cls} x-btn-noicon {0}" {1} cellSpacing=0 {2}>', disabledClass, disabled, readOnlyText);
templateArray.push(templateStart);
templateArray.push('<TBODY class="x-btn-small x-btn-icon-small-left">');
templateArray.push('<TR>');
templateArray.push('<TD class=x-btn-tl><I>&nbsp;</I></TD>');
templateArray.push('<TD class=x-btn-tc></TD>');
templateArray.push('<TD class=x-btn-tr><I>&nbsp;</I></TD>');
templateArray.push('</TR><TR>');
templateArray.push('<TD class=x-btn-ml><I>&nbsp;</I></TD>');
templateArray.push('<TD class=x-btn-mc>');
templateArray.push('<EM unselectable="on">');
templateArray.push('<BUTTON class="x-btn-text" type="button" row="{rowIndex}" col="{colIndex}">{value}</BUTTON>');
templateArray.push('</EM>');
templateArray.push('</TD>');
templateArray.push('<TD class=x-btn-mr><I>&nbsp;</I></TD>');
templateArray.push('</TR> <TR>');
templateArray.push('<TD class=x-btn-bl><I>&nbsp;</I></TD>');
templateArray.push('<TD class=x-btn-bc></TD>');
templateArray.push('<TD class=x-btn-br><I>&nbsp;</I></TD>');
templateArray.push('</TR></TBODY></TABLE>');
var tpl = new Ext.Template(templateArray);
tpl.compile();
return tpl;
}
});
Ext.apply(Ext.fmPlugins.ButtonColumn.prototype, Ext.util.Observable.prototype);
//Registering a xtype with the control.
Ext.grid.Column.types['buttoncolumn'] = Ext.fmPlugins.ButtonColumn;




Guys, I am really tired working with this. Would really appreciate any advice.
Thanks in advance.

jsakalos
5 Nov 2009, 3:12 PM
Hmmm, row addition/deletion/insertion is as simple as adding/deleting/inserting the underlying store record(s).

VATigers
5 Nov 2009, 5:49 PM
Thanks Saki.

My issue is something different. I have wired the grid's click event to the click event of the button in button column. If I add records and delete records, I do not get the correct rowIndex.

VATigers
5 Nov 2009, 6:52 PM
Ok, I feel I have got the problem.
I am including the rowIndex in the button itself. When I delete a row, this rowIndex gets returned. The rowIndixes of the other rows should adjust themselves.



templateArray.push('<BUTTON class="x-btn-text" type="button" row="{rowIndex}" col="{colIndex}">{value}</BUTTON>');



In my example, I already have 2 records. If I add 2 more records, they get added with rowIndex = 2 and rowIndex =3.
Now if I were to delete the 3rd record (rowIndex = 2), the number of records in store become 3, however the next record does not adjust its rowIndex. When I try to delete that it would return a rowIndex of 3 as against 2.

I hope I have explained the problem. :-?

Don't know how to solve it. 8-|

ValterBorges
5 Nov 2009, 7:05 PM
Any reason why you wouldn't just use the selection model to control 1 or many selections and add one delete button to a toolbar for the entire grid and look thru selected?

Is it just to save an extra click?

ValterBorges
5 Nov 2009, 7:08 PM
After reading closer I understand the problem now, it is because you setting the position of the button to match the record, but what you need to do instead is figure out which rowid was clicked dynamically because once rows are removed or added the button row value no longer line up.

VATigers
5 Nov 2009, 7:13 PM
Thanks for the reply Valter,

You have understood my issue.
How do I figure out which rowId was clicked ? Any hints....

ValterBorges
5 Nov 2009, 7:14 PM
You might be able to read the record for that row and grab it's id in the custom renderer instead or if your row has a key you could search for it upon clicking in your store.

The object you want to look up in the docs are record and store, and look up these methods and properties.

The code will go something like this:




//store this in the renderer
var buttonrecordid = record.id






//call this upon button click event.
var deleterecord = store.getByid(buttonrecordid)

store.remove(deleterecord)

jsakalos
6 Nov 2009, 3:20 AM
If you use row selection model then you can call grid.getSelectionModel().getSelections() (http://www.extjs.com/deploy/dev/docs/?class=Ext.grid.RowSelectionModel&member=getSelections). That will return array of selected records.

VATigers
6 Nov 2009, 8:47 AM
thanks Valter and Saki.

Saki, The rowSelectionModel won't work because the buttonclick is not the same as cellclick or rowselection.

Heres what I did. I provide an id to each of the button and store this ids in an array. When the button is clicked, I get the id of the target, look up the array for the id. The array index is the rowIndex for me.

This works for me. The issue is when, I edit the value in the grid, 'buttonColumnRenderer' function of my plugins gets fired again. This again creates an id, adds to the array.

Any advice ?



Ext.fmPlugins.ButtonColumn = Ext.extend(Ext.grid.Column, {
buttonClass: '',
constructor: function (config) {
config = config || {};
this.text = config.text;
this.buttonClass = config.buttonClass;
this.buttonIdArray = new Array();
Ext.fmPlugins.ButtonColumn.superclass.constructor.call(this, config);
this.addEvents('buttonColumnInfoNeeded', 'buttonClick');
Ext.util.Observable.apply(this, arguments);
this.tpl = this.createButtonTemplate();
this.renderer = this.buttonColumnRenderer.createDelegate(this);
if (!Ext.fmPlugins.ButtonColumn.tpl) {
Ext.fmPlugins.ButtonColumn.tpl = this.createButtonTemplate();
}
},
buttonColumnRenderer: function (value, metadata, record, rowIndex, colIndex, store) {
var eventArgs = {
colIndex: colIndex,
rowIndex: rowIndex,
enable: true,
record: record
};
var disabled = this.fireEvent('buttonColumnInfoNeeded', this, eventArgs);
var id = Ext.id();
var btn = Ext.fmPlugins.ButtonColumn.tpl.apply({
cls: this.buttonClass || 'x-btn x-btn-noicon',
id: id,
value: this.text,
rowIndex: rowIndex,
colIndex: colIndex
});
this.buttonIdArray.push(id);
return btn;
}
,
processCellClick: function (rowIndex, colIndex, e) {
var buttonId = e.target.id;
rowIndex = this.buttonIdArray.indexOf(buttonId);
this.buttonIdArray.remove(buttonId)
this.fireEvent('buttonClick', this, rowIndex, e);
}
,
createButtonTemplate: function (enabled) {
var tpl = new Ext.Template('<TABLE style="WIDTH: auto" class="{cls}" cellSpacing=0>', '<TBODY class="x-btn-small x-btn-icon-small-left">', '<TR>', '<TD class=x-btn-tl><I>&nbsp;</I></TD>', '<TD class=x-btn-tc></TD>', '<TD class=x-btn-tr><I>&nbsp;</I></TD>', '</TR>', '<TR>', '<TD class=x-btn-ml><I>&nbsp;</I></TD>', '<TD class=x-btn-mc>', '<EM unselectable="on">', '<BUTTON class="x-btn-text" type="button" id={id} row="{rowIndex}" col="{colIndex}">{value}</BUTTON>', '</EM>', '</TD>', '<TD class=x-btn-mr><I>&nbsp;</I></TD>', '</TR>', '<TR>', '<TD class=x-btn-bl><I>&nbsp;</I></TD>', '<TD class=x-btn-bc></TD>', '<TD class=x-btn-br><I>&nbsp;</I></TD>', '</TR>', '</TBODY>', '</TABLE>'
);
tpl.compile();
return tpl;
}
});
Ext.apply(Ext.fmPlugins.ButtonColumn.prototype, Ext.util.Observable.prototype);
//Registering a xtype with the control.
Ext.grid.Column.types['buttoncolumn'] = Ext.fmPlugins.ButtonColumn;

jsakalos
7 Nov 2009, 6:57 AM
Have you seen RowActions (http://rowactions.extjs.eu)? There's everything you need except the removal/insertion routine itself.

VATigers
10 Nov 2009, 8:09 AM
Ok, I have got this working. I am using the recordId to identify the record.

Thanks Valger and Saki for the advice.

Here is the complete code if anyone is interested :-

Overriding the click event handler of the grid.


Ext.override(Ext.grid.GridPanel, {
onClick: function (e) {
Ext.grid.GridPanel.prototype.onClick.base.apply(this, arguments);
var target = e.getTarget();
if (target.tagName === "BUTTON" && !target.isDisabled) {
var colId = target.getAttribute("colId");
if (colId !== '') {
var colModel = this.getColumnModel();
var col = colModel.getColumnById(colId);
var colIndex = colModel.getIndexById(colId);
var fn = col['processCellClick'];
if (Ext.isFunction(fn)) {
var recId = target.getAttribute("recId");
var rowIndex = this.getStore().indexOfId(recId);
fn.call(col, rowIndex, colIndex, e);
}
}
}
}
});


The gridButtonColumn extension


Ext.fmPlugins.ButtonColumn = Ext.extend(Ext.grid.Column, {
constructor: function (config) {
config = config || {};
this.text = config.text;
this.buttonClass = config.buttonClass;
this.id = config.id || Ext.id(this, 'ext-col');
Ext.fmPlugins.ButtonColumn.superclass.constructor.call(this, config);
this.addEvents('buttonColumnInfoNeeded', 'buttonClick');
Ext.util.Observable.apply(this, arguments);
this.tpl = this.createButtonTemplate();
this.renderer = this.buttonColumnRenderer.createDelegate(this);
if (!Ext.fmPlugins.ButtonColumn.tpl) {
Ext.fmPlugins.ButtonColumn.tpl = this.createButtonTemplate();
}
},
buttonColumnRenderer: function (value, metadata, record, rowIndex, colIndex, store) {
var eventArgs = {
colIndex: colIndex,
rowIndex: rowIndex,
disableButton: false,
buttonClass: this.buttonClass,
visible: true,
record: record
};
var result = this.fireEvent('buttonColumnInfoNeeded', this, eventArgs);
var btn = Ext.fmPlugins.ButtonColumn.tpl.apply({
id: record.id,
value: this.text || '',
colId: this.id,
cls: eventArgs.buttonClass || '',
disabledClass: eventArgs.disableButton ? 'x-item-disabled' : '',
disabledText: eventArgs.disableButton ? 'disabled' : '',
readOnlyText: eventArgs.disableButton ? 'readOnly' : '',
display: eventArgs.visible ? 'block' : 'none'
});
return btn;
}
,
processCellClick: function (rowIndex, colIndex, e) {
this.fireEvent('buttonClick', this, rowIndex, colIndex, e);
}
,
createButtonTemplate: function () {
var tpl = new Ext.Template('<TABLE style="WIDTH: auto;display:{display};" class=" x-btn {cls} x-btn-noicon {disabledClass}" {disabledText} cellSpacing=0 {readOnlyText}>', '<TBODY class="x-btn-small x-btn-icon-small-left">', '<TR>', '<TD class=x-btn-tl><I>&nbsp;</I></TD>', '<TD class=x-btn-tc></TD>', '<TD class=x-btn-tr><I>&nbsp;</I></TD>', '</TR>', '<TR>', '<TD class=x-btn-ml><I>&nbsp;</I></TD>', '<TD class=x-btn-mc>', '<EM unselectable="on">', '<BUTTON class="x-btn-text" type="button" recId={id} colId="{colId}">{value}</BUTTON>', '</EM>', '</TD>', '<TD class=x-btn-mr><I>&nbsp;</I></TD>', '</TR>', '<TR>', '<TD class=x-btn-bl><I>&nbsp;</I></TD>', '<TD class=x-btn-bc></TD>', '<TD class=x-btn-br><I>&nbsp;</I></TD>', '</TR>', '</TBODY>', '</TABLE>'
);
tpl.compile();
return tpl;
}
});
Ext.apply(Ext.fmPlugins.ButtonColumn.prototype, Ext.util.Observable.prototype);
//Registering a xtype with the control.
Ext.grid.Column.types['buttoncolumn'] = Ext.fmPlugins.ButtonColumn;