1. #1
    Ext JS Premium Member Event Horizon's Avatar
    Join Date
    May 2008
    Location
    Melbourne, Australia
    Posts
    42
    Vote Rating
    0
    Event Horizon is on a distinguished road

      0  

    Default EditorGridPanel: data entered is lost on failed validation

    EditorGridPanel: data entered is lost on failed validation


    I want to implement an EditorGridPanel that has 2 features:

    1. The fields in it can take advantage of the usual Editor Field config options "allowBlank: false" or "maxLength: 3", etc

    2. The user does not lose the text they've entered if what they enter fails such Editor validation

    In both the afteredit and validateedit events in the sample code below I can get at the existing text in the field and the new text that is entered so I can ensure that whatever the user enters in the case of failed validation is not lost, but I've discovered that these events don't fire at all when the users input fails Editor validation like "maxLength: 3".

    In my application I don't want users to type in some long value that then disappears simply because it failed some validation in the config.

    I can't find sample code that seems to address this problem. I'd value any pointers.

    Code:
    Ext.onReady(function(){
        Ext.QuickTips.init();
        var fm = Ext.form;
        var cm = new Ext.grid.ColumnModel([{
               id:'name',
               header: "Name",
               dataIndex: 'name',
               width: 180,
               editor: new fm.TextField({
                   allowBlank: false,
                   maxLength: 3
               })
            },{
               header: "Price",
               dataIndex: 'price',
               width: 70,
               align: 'right',
               renderer: 'usMoney',
               editor: new fm.NumberField({
                   allowBlank: false,
                   allowNegative: false
               })
            }
        ]);
        cm.defaultSortable = true;
    
        var record = Ext.data.Record.create([
               {name: 'name', type: 'string'},
               {name: 'price', type: 'float'}
          ]);
    
        var store = new Ext.data.SimpleStore({
            fields: [ 'name', 'price' ],
            data: [ [ 'aa', '12.50' ], [ 'bb', '9.00' ], [ 'cc', '18.90' ] ],
            sortInfo:{field:'name', direction:'ASC'}
        });
    
        var grid = new Ext.grid.EditorGridPanel({
            store: store,
            cm: cm,
            renderTo: 'editor-grid',
            width:300,
            height:200,
            autoExpandColumn:'name',
            title:'Edit Records',
            frame:true,
            clicksToEdit:1,
    
            tbar: [{
                text: 'Add Record',
                handler : function(){
                    var p = new record({
                        name: 'New Record 1',
                        price: 0
                    });
                    grid.stopEditing();
                    store.insert(0, p);
                    grid.startEditing(0, 0);
                }
            }]
        });
    
        grid.on({
            validateedit: function(e) {
                alert('v: Original value = ' + e.originalValue 
                      + ' New value: ' + e.value 
                      + ' Row: ' + e.row 
                      + ' Col: ' + e.column);
                return true;
            },
            afterEdit: function(e) {
                alert('a: Original value = ' + e.originalValue 
                      + ' New value: ' + e.value 
                      + ' Row: ' + e.row 
                      + ' Col: ' + e.column);
                return true;
            }
        });
    
    
    });

  2. #2
    Ext JS Premium Member Event Horizon's Avatar
    Join Date
    May 2008
    Location
    Melbourne, Australia
    Posts
    42
    Vote Rating
    0
    Event Horizon is on a distinguished road

      0  

    Default


    Bump.

    What I'm describing above is, I think, the default behavior of an EditorGridPanel. I'm looking for tips on the best practice way of handling this.

    Picture a simple editable grid. When the page is loaded, each row in the grid is preloaded with data previously entered.

    When the user enters text into a grid field, that text is simply deleted if it fails validation.

    I want to be able to use something like afteredit to give the user the chance to modify their new data, and not have to laboriously type it in again. But I can't use this event because it doesn't fire if the text in the field fails validation.

    This seems like a bug to me, but I'm sure it's designed to be that way. So I'm suspicious that there is a standard way of handling this quandary and that I am missing the wood for the trees.

  3. #3
    Sencha - Community Support Team Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,246
    Vote Rating
    91
    Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of

      0  

    Default


    I was having the same problem and I finally just stuck with the default behaviour.

    But this post started me thinking and this is what I came up with:

    Code:
    <script type="text/javascript">
    Ext.onReady(function(){
    	new Ext.Viewport({
    		layout: 'fit',
    		items: [{
    			id: 'grid',
    			xtype: 'editorgrid',
    			store: new Ext.data.SimpleStore({
    				fields: ['text'],
    				data: ['ABC', 'DEF', 'GHI'],
    				expandData: true
    			}),
    			columns: [{
    				header: 'Text',
    				dataIndex: 'text',
    				renderer: function(value, metadata, record, rowIndex, colIndex, store) {
    					if (record._valid && (record._valid[colIndex] === false)) {
    						metadata.css = 'x-grid3-invalid-cell';
    					}
    					return value;
    				},
    				editor: new Ext.grid.GridEditor(new Ext.form.TextField({
    					allowBlank: false,
    					maxLength: 3,
    					validateValue: function(v) {
    						this.record._valid[this.col] = Ext.form.TextField.prototype.validateValue.call(this, v);
    						return true;
    					}
    				}), {
    					listeners: {
    						beforestartedit: function(e) {
    							if (!e.record._valid) {
    								e.record._valid = [];
    							}
    							e.field.record = e.record;
    							e.field.col = e.col;
    						}
    					}
    				})
    			}]
    		}]
    	});
    });
    </script>
    <style type="text/css">
    .x-grid3-invalid-cell {
    	background: transparent url(resources/images/default/grid/invalid_line.gif) repeat-x bottom;
    }
    </style>
    This accepts all invalid input and only highlights the cell.

  4. #4
    Ext JS Premium Member Event Horizon's Avatar
    Join Date
    May 2008
    Location
    Melbourne, Australia
    Posts
    42
    Vote Rating
    0
    Event Horizon is on a distinguished road

      0  

    Default


    Quote Originally Posted by Condor View Post
    I was having the same problem and I finally just stuck with the default behaviour.

    But this post started me thinking and this is what I came up with:

    ...

    This accepts all invalid input and only highlights the cell.
    That's perfect. Thanks for going to the trouble to post this code, it's much appreciated.

    In your solution, the columns are defined with a renderer and and editor:

    Code:
                columns: [{
                    header: 'Text',
                    dataIndex: 'text',
                    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
                        ...
                    },
                    editor: new Ext.grid.GridEditor(new Ext.form.TextField({
                        ...
                    })
    If I want to generalize that into an xtype, which class should I extend?

  5. #5
    Ext User
    Join Date
    Jul 2007
    Location
    Florida
    Posts
    9,996
    Vote Rating
    5
    mjlecomte will become famous soon enough mjlecomte will become famous soon enough

      0  

    Default


    I think a config property should be added so user could control whether to
    1. nullify the entry or
    2. mark the entry as invalid

  6. #6
    Sencha - Community Support Team Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,246
    Vote Rating
    91
    Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of

      0  

    Default


    @Event Horizon:
    Column definitions in a ColumnModel aren't classes. You should extend ColumnModel and assign it to the cm config option (and not to columns) of the grid.

    @mjlecomte:
    That would be nice, but currently EditorGridPanel doesn't support marking cells as invalid.

  7. #7
    Ext JS Premium Member Event Horizon's Avatar
    Join Date
    May 2008
    Location
    Melbourne, Australia
    Posts
    42
    Vote Rating
    0
    Event Horizon is on a distinguished road

      0  

    Default


    Quote Originally Posted by Condor View Post
    @Event Horizon:
    Column definitions in a ColumnModel aren't classes. You should extend ColumnModel and assign it to the cm config option (and not to columns) of the grid.
    Thanks for that. I'm unclear where in the extension code I should apply the 'renderer' and 'editor' attributes. I have extended Ext.grid.ColumnModel and registered an xtype.

    What I've tried so far produces a grid with 1) non-editable fields and 2) an empty header.

    Do I need to register an xtype as I have done below?

    Code:
    Ext.onReady(function(){
    
        Ext.ux.ColumnModelEdit = Ext.extend(Ext.grid.ColumnModel, {
    
            initComponent: function(){
                Ext.apply(this, [{
                    header: 'text',
                    dataIndex: 'text',
                    width: 600,
                    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
                        if (record._valid && (record._valid[colIndex] === false)) {
                            metadata.css = 'x-grid3-invalid-cell';
                        }
                        return value;
                    },
                    editor: new Ext.grid.GridEditor(new Ext.form.TextField({
                        allowBlank: false,
                        maxLength: 3,
                        validateValue: function(v) {
                            this.record._valid[this.col] = Ext.form.TextField.prototype.validateValue.call(this, v);
                            return true;
                        }
                    }), {
                        listeners: {
                            beforestartedit: function(e) {
                                if (!e.record._valid) {
                                    e.record._valid = [];
                                }
                                e.field.record = e.record;
                                e.field.col = e.col;
                            }
                        }
                     })
                }]);
                Ext.ux.ColumnModelEdit.superclass.initComponent.call(this);
            },
        });
    
        Ext.reg('cmedit', Ext.ux.ColumnModelEdit);
    
        var colModel = new Ext.grid.ColumnModel([
        {
            xtype: 'cmedit',
        }
        ]);
    
        new Ext.Viewport({
            layout: 'fit',
            items: [{
                id: 'grid',
                xtype: 'editorgrid',
                store: new Ext.data.SimpleStore({
                    fields: ['text'],
                    data: ['ABC', 'DEF', 'GHI'],
                    expandData: true
                }),
                cm: colModel
            }]
        });
    });

  8. #8
    Sencha - Community Support Team Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,246
    Vote Rating
    91
    Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of

      0  

    Default


    I would use:
    Code:
    <script type="text/javascript">
    Ext.grid.ColumnModelEdit = function(config){
    	var columns = config.columns || config;
    	Ext.each(columns, function(column) {
    		if (column.editor) {
    			Ext.apply(column, {
    				renderer: function(value, metadata, record, rowIndex, colIndex, store) {
    					if (record._valid && (record._valid[colIndex] === false)) {
    						metadata.css = 'x-grid3-invalid-cell';
    					}
    					return value;
    				},
    				editor: new Ext.grid.GridEditor(Ext.apply(column.editor, {
    					validateValue: function(v) {
    						this.record._valid[this.col] = Ext.form.TextField.prototype.validateValue.call(this, v);
    						return true;
    					}
    				}), {
    					listeners: {
    						beforestartedit: function(e) {
    							if (!e.record._valid) {
    							   e.record._valid = [];
    						   }
    						   e.field.record = e.record;
    						   e.field.col = e.col;
    					   }
    				   }
    				})
    			});
    		}
    	});
    	Ext.grid.ColumnModelEdit.superclass.constructor.call(this, config);
    }
    Ext.extend(Ext.grid.ColumnModelEdit, Ext.grid.ColumnModel);
    
    Ext.onReady(function(){
    	new Ext.Viewport({
    		layout: 'fit',
    		items: [{
    			id: 'grid',
    			xtype: 'editorgrid',
    			store: new Ext.data.SimpleStore({
    				fields: ['text'],
    				data: ['ABC', 'DEF', 'GHI'],
    				expandData: true
    			}),
    			cm: new Ext.grid.ColumnModelEdit([{
    				header: 'Text',
    				dataIndex: 'text',
    				editor: new Ext.form.TextField({
    					allowBlank: false,
    					maxLength: 3
    				})
    			}])
    		}]
    	});
    });
    </script>
    <style type="text/css">
    .x-grid3-invalid-cell {
    	background: transparent url(resources/images/default/grid/invalid_line.gif) repeat-x bottom;
    }
    </style>
    (better reusable)

  9. #9
    Ext JS Premium Member Event Horizon's Avatar
    Join Date
    May 2008
    Location
    Melbourne, Australia
    Posts
    42
    Vote Rating
    0
    Event Horizon is on a distinguished road

      0  

    Default SOLVED: EditorGridPanel: data entered is lost on failed validation

    SOLVED: EditorGridPanel: data entered is lost on failed validation


    Thanks for your help Condor.

    Your code resolves the problem very nicely. I hope that other people wanting to solve this problem in the future can find this thread.

  10. #10
    Ext User Blackhand's Avatar
    Join Date
    May 2008
    Posts
    42
    Vote Rating
    0
    Blackhand is on a distinguished road

      0  

    Default


    I wanted similar behavior a while back and simply just decided to go with the default after wasting too much time trying to get it to work correctly.

    Thanks Condor, this is going to be useful when the need crops up again.