PDA

View Full Version : Editing two fields of an Ext.grid.Panel with one editor?



sacha
30 Oct 2011, 2:22 PM
I have a grid panel where a pair of fields are related:


grid = Ext.create(
'Ext.grid.Panel',
{
store: {
type: 'array',
fields: [ 'id', 'name', ...more fields... ]
},
...


The 'id' and 'name' fields represent two fields of the same underlying model (a Tag). I've implemented an extension of Ext.form.field.Picker which lets me browse the Tags and select one, returning as its value the id. I use this as the editor for the relevant grid column:


columns: [
{
text: 'Tag',
dataIndex: 'id',
field: {
xtype: 'mypicker',
allowBlank: false
}
},
...more columns...


The problem I have is that while this will populate the 'id' field, I also need to populate the 'name' field for display, which I would display using a renderer function.

My ExtJS Tag model is configured to have a REST proxy, so in theory I could look up the name via the model, but that would incur an AJAX request per id which would be bonkers.

Is there a standard way of doing this kind of thing?

I feel like what I really want is for one of my grid's fields to be an instance of the Tag model, and have the editor return Tag instances too.

zombeerose
31 Oct 2011, 4:46 PM
Assuming you are using either the CellEditing or RowEditing plugins, then you can attach to 3 events (http://docs.sencha.com/ext-js/4-0/#!/api/Ext.grid.plugin.CellEditing-event-edit) (beforeedit, edit, validateedit) on the plugin (or the grid because they are relayed). One of the event params contains the record involved. Just be warned that the API docs are not always accurate regarding param order - best to check the code.

sacha
19 Nov 2011, 2:59 PM
In the end I wasn't really able to find an elegant solution to this. I settled for making my editor return the two fields (name and id) concatenated as the value ("name:id"), and then using the CellEditor's edit event (thanks zombeerose) to split them apart and set them into the record:



plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1,
listeners: {
edit: function (editor, data) {
var cpos = data.value.lastIndexOf(':');
var id = data.value.substr(cpos+1);
var name = data.value.substr(0, cpos);
data.record.set('id', id);
data.record.set('name', name);
}
}
})
],


It's ugly on various levels:

my editor class, which is a specialisation of a regular form field and so should just return the value to be submitted in a hypothetical form (the id), now has to include a display string in the value (the name)
the concatenated value is visible in the cell editor until the focus leaves the cell (which triggers the edit event)
the edit handler is setting the field which is being edited, so I bet it's actually being set twice

But I can live with all that. Grid editing seems to be fundamentally based on editing individual fields so I doubt there is any substantially neater answer to this issue.

flanders
20 Nov 2011, 2:48 AM
Why not just return the id and retrieve the corresponding value (name) from editor.store in the edit-event-handler?

That would make your picker follow the general pattern, prevent your cosmetic problems and make sure the value is set and displayed?

Or am I missing something here?

sacha
20 Nov 2011, 8:22 AM
That was my first instinct too. However, the 'editor' in the 'edit' event is an instance of Ext.grid.plug.CellEditing, whereas my actual editor (as bound to the column via its 'field' config property, see the first post) is an instance of Ext.form.field.Picker:



Ext.define('My.TagPicker', {
extend: 'Ext.form.field.Picker',
alias: ['widget.mypicker'],
matchFieldWidth: false,

createPicker: function () {
var t = this;
var config = {
pickerField: t,
ownerCt: t.ownerCt,
renderTo: Ext.getBody(),
floating: true,
focusOnShow: true,
height: 250,
width: 250
};

var picker = Ext.create('My.TagPanel', config);

picker.on(
'select',
function (rowmodel, rec) {
t.setValue(rec.get('id'));
t.collapse();
}
);

return picker;
}
});


So the store where I could get hold of the name is actually in the picker (TagPanel), and I couldn't see an API route that would get me there from CellEditing's 'edit' event -- or indeed to the instance of TagPicker (if I could, I could amend the picker's 'select' listener to stash the 'name' field somewhere in 'this').

flanders
20 Nov 2011, 11:41 AM
This is adapted from a piece of code of ourselves. It should work, but if I'm not mistaken the columns property of grid is not defined in the docs so it might break on 'internal changes' of Ext.




Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 2,
listeners: {
edit: function(plugin, e) {
var editor = e.grid.columns[e.colIdx].getEditor();

}
}
});

sacha
20 Nov 2011, 12:06 PM
I've been experimenting a bit more too and found similar private code to get hold of the Field:




Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 2,
listeners: {
edit: function(plugin, e) {
// getColumnField is private, so this is fragile
var field = plugin.getColumnField(e.column);
...
}
}
});