PDA

View Full Version : Obtaining a column's numeric index from its data Index



cesarulo
21 May 2007, 11:53 AM
Hey Forum,

I'm having a little problem with the whole "symbolic column name"/dataIndex vs. "numeric column index"/index duality in the grid and its ColumnModel.

I know that this must be a very recurring problem, but I'm just failing to find a solution to it posted anywhere in the forum. I'm hoping there's a straighforward solution that I'm just missing.

Here's the problem: I have a grid with some editable fields, some not. When someone creates a new record, I want to *force* them to edit the new, default values, which are NO GOOD for sumbission to the server for DB insertion, they're gibberish and MUST be edited.

Now I can do this by hooking up the following function to the add event of the Grid's DataStore:



IndexGrid.prototype.addRecordListener = function(ds, records, index){
index_1st = index;
for(var record in records){
var cm = this.grid.getColumnModel();
var editor = cm.getCellEditor(1, index_1st); //<-- THIS IS THE PROBLEM RIGHT HERE

//this is where I put focus on the 1st cell that needs to be edited, and all the rest

//increment the index counter
index_1st++;
}
}



The problem, as pointed out in the code, is in the call cm.getCellEditor(1, index_1st).
More specifically, with the '1' in its 1st argument. While it is true that the grid, as served initially, has the relevant data in the 2nd column (index 1), by default the grid allows users to move the columns around. I guess I could preclude this, but I don't want to do that: it's perfectly good functionality to have and I don't want to go out of my way and spend time and my boss's money to REMOVE something that's good for user experience.

So let me sum up my question: I know the dataIndex property of the column I need (it's 'name'), is there a way that I can obtain its index number from it? I know that the inverse is possible, by using ColumnModel.getDataIndex(Number col) - woulnd't it make sense to have a method that does the opposite?

If there is, it's not in the online docs, and I saw this very old thread:

http://extjs.com/forum/showthread.php?t=124

where the same problem seems to be addressed, and Jack mentions that it needs a solution, that it was dropped off the list accidentally... that was a while back, last year - do we have a solution now?

If I missed something obvious, as I like to say, a link, or even a couple keywords to orient me will be very gratefully received.

Best, and thanks in advance for your help everybody.

jsakalos
21 May 2007, 12:21 PM
May I reformulate problem? Correct me if I'm wrong.

Do not allow user to save incomplete or invalid data.

Now, the question is when and how do you want to do this validation. Your solution is to "force user to edit", however, what I've seen so far is to validate data just before submit. In Ext we can do seamlessly both client and even background server side validation via XHR requests.

What I would do if I needed strong data validation:

User creates new record with default values.
Saving is still disabled (e.g. save button grayed)
User starts to type - use server side validation if needed .....
All data validated => save enabled.
SaveDon't know if this is feasible but over 10 years of my experiences with stupid users talks for the solution I've outlined.

Hi,
Jozef

cesarulo
21 May 2007, 12:30 PM
Hey Jozef,

thanks for your answer.

Unfortunately, I think I drifted into unrelated terrain and the point of my question was diluted in my blabbering.

Perhaps you're right about the validation problem. However, I'm not worried about it right now. This is just a 1st approximation so I can get the rest of the stuff to work. I'll worry about that later.

My question is specifically about whether I can obtain a cell's numeric index from its dataIndex name. That is, if I know that I have a column named 'name', and that it's initially at position 1, I have no way to reliably identify that column for a call to getCellEditor().

getCellEditor wants a NUMBER as its first argument, which it will use to spot the column. Now if the user shifts that column around, 1 is no longer the right number for it. I need to be able to either:

1) identify the column directly by its dataIndex property, or
2) get the *current* column's numeric index from it symbolic dataIndex name.

I hope that clarifies the point. My apologies about my verboseness. Any thoughts?

jsakalos
21 May 2007, 12:33 PM
My question is specifically about whether I can obtain a cell's numeric index from its dataIndex name. That is, if I know that I have a column named 'name', and that it's initially at position 1, I have no way to reliably identify that column for a call to getCellEditor().
You got me C

cesarulo
21 May 2007, 1:35 PM
Hey community,

after some further source code inspection, I discovered what look like important clues.

I *did* try changing the column position and making my getCellEditor() with the same column number, in the hopes that the index number passed to it wouldn't be determined by current position, but rather by the number that identified it at creation time. But no dice. When the user changes the column's position, it changes its index along with it (as was to be expected). So, as I feared, after moving the column away from position 1, a call to getCellEditor(1, index_1st) now pointed to the column that took position 1 after the move.

Now here's my ray of hope: upon ColumnModel() constructor call, a 'lookup' property is created. Inspection of it with FireBug revealed that it's a numeric array, indexed by column position. But it turns out that it's actually indexed by each Column's 'id' property, and never touched again. If no explicit id property is given with the ColumnModel config object upon constructor invocation, it just puts uses the position index as the id.

But after the user shifts a column, id's are not revised to keep them in synch with positional indexes.

More interestingly, it seems that if one passes 'id' attribs along with the ColModel spec to the constructor, they get used instead of the positional indexes, like so:

instead of


var colModel = new Ext.grid.ColumnModel([
{header: "Ticker", dataIndex: 'ticker', width: 60, sortable: true, locked: true},
{header: "Company Name", dataIndex: 'compName', width: 150, sortable: true},
{header: "Market Cap.", dataIndex: 'marketCap', width: 100, sortable: true},
{header: "$ Sales", dataIndex: 'salesWorth', width: 100, sortable: true, renderer: money},
{header: "Employees", dataIndex: 'employees', width: 100, sortable: true, resizable: false}
]);


you'd say



var colModel = new Ext.grid.ColumnModel([
{header: "Ticker", dataIndex: 'ticker', width: 60, sortable: true, locked: true, id: 'ticker_col'},
{header: "Company Name", dataIndex: 'compName', width: 150, sortable: true, id: 'compName_col'},
{header: "Market Cap.", dataIndex: 'marketCap', width: 100, sortable: true, id: 'marketCap_col'},
{header: "$ Sales", dataIndex: 'salesWorth', width: 100, sortable: true, renderer: money, id: 'salesWorth_col'},
{header: "Employees", dataIndex: 'employees', width: 100, sortable: true, resizable: false, id: 'employees_col'}
]);



Notice the 'id' attribute appended to each col's definition.

After doing that, you can safely pinpoint the column... by using the *undocumented* function getColumnById().

But now you have no access to its position in the grid from there, which leaves you unable to make your call to getCellEditor. So I'm back where I started... or am I?

Turns out inspection of the live object with FireBug shows that there's an editor property on that Column - there's my guy. I should be able to force edition of my field or whatever I want from here.

The only problem that I have with this solution is that it strongly smells of a HACK. I'm using an undocumented method and properties which by the looks of it are supposed be private.

Can someone help me out? Am I really the 1st person to run into this problem? Am I just looking at this from the wrong angle? IF not, there HAS to be a simpler solution. I just want to be able to get the GridEditor object for a column whose position I don't know (because the user can change it, which is GREAT), but whose dataIndex I *do* know (and isn't THIS precisely the whole point of having a dataIndex property?)

Any help will be gratefully welcome.

Thanks again,

cesarulo
21 May 2007, 5:53 PM
Hey everybody.

Please don't think I'm being impatient. Just thought it's possible that I might be losing readers' attention quickly because I've done a very poor job of explaining myself.

Anyway, enough with the disclaimer. Here goes my polished explanation of the problem:


My question is specifically about whether I can obtain a grid cell's numeric column index from its dataIndex name. That is, if I know that I have a column with dataIndex property set to, say, 'Address', do I have a way to reliably identify that column for a call to getCellEditor()? The problem is that getCellEditor's signature is



public function getCellEditor(Number colIndex, Number rowIndex)


It wants a NUMBER for each of col and row. So it'd expect something like getCellEditor(1, 5), to return the editor for cell 1, row 5. The problem is that the fact that columns are movable makes numbers completely useless to safely identify a column by its meaning to the application rather than by its circumstantial position in a visual component.

So my question can be reduced to this: I can't go getCellEditor('Address', 5). It has to be getCellEditor(1, 5), and then if the user moves the Adress column to the 7th position the code will misidentify the column, which is clearly not acceptable. Given this reality...

IS THERE A method anywhere in the entire API that will return the current column's position if I give it the column's dataIndex property value? That is, something like column.getColByDataIndex('Address') which returns the last position where the user might have decided to put the column, which I can't possibly know at coding time?

OR failing that, is there any other way to get a cell's GridEditor object if I have its dataIndex and a row number? Is that somehow not enough data to locate the cell and its editor? Perhaps there's a property OTHER than dataIndex that I should use to identify my columns?

And, just in case I'm really not seeing a very simple, obvious approach, my final objective with this is just to give focus to the GridEditor for the cell in question to indicate to the user that they should type something: as in "fill in the address of the new record before sending it to the server for insertion". Forget about validation, that's a separate problem which I will tackle later. For now assume sending it "as is" is also okay and try to focus on the "round peg in the square hole" kind of situation that I think we're seeing here.

I hope that clarifies the point. My apologies about my verboseness. Any thoughts?

Animal
21 May 2007, 11:51 PM
The ColumnModel with it's numeric column indices references Fields from Records in the Store using each column's dataIndex property.

Should be as simple as:



Ext.override(Ext.grid.ColumnModel, {
getColumnIndex: function(dataIndex) {
for (var i = 0; i < config.length; i++) {
if (config[i].dataIndex == dataIndex) return i;
}
}
});


Remember that the parameter passes is the dataIndex which is the "name" property that you gave to the field in the Record definition. If you didn't use a "name" property, then it will be the numeric index of the field item in the Record definition.

cesarulo
22 May 2007, 7:14 AM
Hey Animal,

as usual, thanks for your help. I imagined I was going to have to do my own search routine. Just wasn't sure about using undocumented methods and private properties but this answers all my pleas.

Problem solved! Fantastic, thanks.

Just a suggestion: shouldn't some way of identifying the column by its name, like this method you provide here, be part of the API? Is my problem a rare one?

Anyway, that's all I need for now, this is perfect. Cheers.

cesarulo
22 May 2007, 9:00 AM
Hey forum,

just found an undocumented funtion in ext-all-debug.js, in the AbstractView definition, called getColumnIndexByDataIndex, which seems to do exactly what we were discussing here. Am I wrong? If not, can I rely on this function being in future releases and preserving its semantics, or is it a private function that I should never use under any circumstances?

Here's its code:



getColumnIndexByDataIndex : function(dataIndex){
if(!this.indexMap){
this.indexMap = this.buildIndexMap();
}
return this.indexMap.dataToCol[dataIndex];
},

cesarulo
22 May 2007, 9:25 AM
Hi Team,

I'm confused. I thought getColumnIndexByDataIndex() would be the answer, specially since it's called from updateCell(), but when I call it I get "buildIndexMap is not a function"... looks to me like something is seriously wrong, either with Ext, or with my head. Can anyone help? What happened to the buildIndexMap function?

cesarulo
22 May 2007, 9:41 AM
Hey Animal,

a slight correction to the method you gave me:



Ext.override(Ext.grid.ColumnModel, {
getColumnIndex: function(dataIndex) {
for (var i = 0; i < this.config.length; i++) {
if (this.config[i].dataIndex == dataIndex) return i;
}
}
});


notice the addition of 'this' to all references to 'config' :)

Animal
22 May 2007, 11:53 PM
Good catch! It's nice to see someone take a suggestion, run it, debug it and fix it themselves!

cesarulo
23 May 2007, 4:38 AM
Hey Animal,

thanks for your appreciation! Makes a n00b feel all important inside :D

Anyway, just to close this topic, adding that function Animal provides, with my corrections, to the ColumnModel prototoype directly or by using Ext.override, SOLVES the problem. DO NOT attempt to use getColumnIndexByDataIndex(), it barfs on this.buildIndexMap(); it's not defined.

Perhaps it would be interesting to find out what that function does, and why buildIndexMap() is not defined, the getColumnIndexByDataIndex() seems a pretty important one and one that's used by the GridView.updateCell() method (which itself doesn't seem to be used from anywhere in ext-all-debug.js, or be a public GridView method - perhaps something that was left half-done?)

Another thing, Animal in case you're still reading, or whoever: what's with this Ext.override method? It's a mystery. It's not in ext-all-debug.js, and it's not in the docs. Can someone offer some background on what's going on with that?

Thanks a lot everybody!

CESAR

cesarulo
23 May 2007, 4:55 AM
Me again. Sorry! :)

Found where override is defined - it's in source/core/Ext.js and minified in adapter/yui/ext-yui-adapter.js.

Still can't find it in the docs. Is it because it's originally a yui thing? It would be nice to have it in the docs somewhere, or at least an explanation as to why it's not there if no plans are in place to ever include it.

Might sound hard to believe, but these small fundamental details help me understand a library much better than bags of 'tricks' or things like that.

Again, thanks everybody.

JasonMichael
31 May 2007, 12:55 PM
Thanks guys, answers some questions I had about this, also.

Animal
1 Jun 2007, 1:34 AM
Someone has just kindly added some docs for Ext.override in SVN. The next push should see much improved documentation.