Thank you for reporting this bug. We will make it our priority to review this report.
  1. #1
    Ext JS Premium Member
    Join Date
    Jan 2008
    Posts
    315
    Vote Rating
    0
    josh803316 is on a distinguished road

      0  

    Default [FIXED-1181] Row Editor 'afteredit' doesn't always fire when existing row is edited

    [FIXED-1181] Row Editor 'afteredit' doesn't always fire when existing row is edited


    Ext version tested:
    • Ext 3.2 rev 2

    Adapter used:
    • ext

    css used:
    • only default ext-all.css


    Browser versions tested against:
    • FF3 (firebug 1.5.4 installed)

    Operating System:
    • Fedora Core 12

    Description:
    • I have a 2 column row editor and I have noticed that sometimes when I edit one of the existing rows and hit save the 'afteredit' event doesn't fire. The example I have included with the test case is this. Add a row to the grid with some text in column one but not in column 2. Then, double click the row to edit the text and change it. Then hit tab so that the cursor switches to the next column. Then click save. You will notice that no 'afteredit' event is fired and therefore no ajax request is generated.

    Test Case:

    Code:
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
            <title id='title'>Title</title>
     
            <!-- ** CSS ** -->
            <!-- base library -->
            <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
     
            <!-- overrides to base library -->
     
     
            <!-- ** Javascript ** -->
            <!-- base library -->
            <script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
            <script type="text/javascript" src="../../ext-all-debug.js"></script>
     		<script type="text/javascript" src="../../examples/ux/RowEditor.js"></script>
     
            <!-- overrides to base library -->
     
            <!-- extensions -->
     		<script type="text/javascript">
    		Ext.namespace('jnms');
    		Ext.namespace('jnms.xtypes.stores');
    		Ext.namespace('jnms.xtypes.forms');
    		Ext.namespace('jnms.xtypes.forms.textareas');
    		Ext.namespace('jnms.xtypes.grids');
    		Ext.namespace('jnms.xtypes.toolbars');
    		Ext.namespace('jnms.xtypes.panels');
    		Ext.namespace('jnms.viewport');
    		
    		jnms.xtypes.toolbars.topEditGridToolbar =  Ext.extend(Ext.Toolbar, {
    				 initComponent:function() {
    						var config = {
    						 	autoDestroy: true,
    							closable: true
    							
    						}; // eo config object
    						  
    						// apply config
    						Ext.apply(this, Ext.apply(this.initialConfig, config));
    				  		
    						
    				 		// call parent
    				 		jnms.xtypes.toolbars.topEditGridToolbar.superclass.initComponent.apply(this, arguments);
    		
    		 		}, // eo function initComponent
    		 		afterRender: function(){
    					jnms.xtypes.toolbars.topEditGridToolbar.superclass.afterRender.call(this);
    					
    					//this.findByType('button')[0].on('click', this.onAdd, this);
    					//this.findByType('button')[1].on('click', this.onDelete, this);
    				},
    		 		onRender: function(){
    		 			jnms.xtypes.toolbars.topEditGridToolbar.superclass.onRender.apply(this, arguments);
    		 		}
    		});
    		  
    		Ext.reg('topeditgridtoolbar', jnms.xtypes.toolbars.topEditGridToolbar); 
    		
    		
    		jnms.xtypes.stores.manageStore   = Ext.extend(Ext.data.JsonStore, {
    			constructor: function(config) {
    		        jnms.xtypes.stores.manageStore.superclass.constructor.call(this, Ext.apply({
    		            url: config.url,
    					id: config.id,
    					reader: new Ext.data.JsonReader({
    		        		totalProperty: 'total', 
    		        		fields: config.fields,
    						root: config.root
    		    		}),
    					baseParams: config.baseParams
    					//autoLoad: {params:{start: 0, limit: 25}}
    					//scope: this
    					
    		        }, config));
    				this.on('load', this.showSuccess, {
    					scope: this
    				});
    				//this.on('beforeload', this.beforeLoad, {
    				//	scope: this
    				//});
    				this.on('exception', this.showError, this);
    		    },
    			/**
    			* Shows Message Box with error
    			* @param {String} msg Message to show
    			* @param {String} title Optional. Title for message box (defaults to Error)
    			* @private
    			*/
    			showError:function(store,options,response,e) {
    				 //console.dir(response);
    				 title = 'Error';
    				 Ext.Msg.show({
    					 title:title
    					 ,msg:"Load exception for JSON store " + response.responseText 
    					 ,modal:true
    					 ,icon:Ext.Msg.ERROR
    					 ,buttons:Ext.Msg.OK
    				 }); //eo Msg.show
    			},
    			/**
    			* Shows Message Box with error
    			* @param {String} msg Message to show
    			* @param {String} title Optional. Title for message box (defaults to Error)
    			* @private
    			*/
    			beforeLoad:function(store,options,response,e) {
    				 
    				 
    			},
    		    /**
    			* Shows Message Box with error
    			* @param {String} msg Message to show
    			* @param {String} title Optional. Title for message box (defaults to Error)
    			* @private
    			*/
    			showSuccess:function(store, records, options) {
    				 //var obj = Ext.decode(response.responseText);
    				 var jsonData = store.reader.jsonData.data;
    				 //console.info(records);
    				 if (jsonData.failure === 'true') {
    				 	Ext.dropDownMessage.msg('Error','{0}','Request Failed:<br />' + jsonData.result,5);
    				 	//Ext.Msg.alert('Error', jsonData.result);
    				 }
    				 
    			}
    		    // }}}
    		    // any other added/overrided methods
    		}); // eo extend
    		 
    		// register an xtype with this class
    		Ext.reg('managestore', jnms.xtypes.stores.manageStore);
    		
    		jnms.xtypes.forms.textareas.editArea = Ext.extend(Ext.form.TextArea, {
    
    		    height: 100,
    		    initComponent:function() {
    		    	var config = {
    					selectOnFocus: true
    				    ,enableKeyEvents:true
    					
    		    	};	
    		        Ext.apply(this, Ext.apply(this.initialConfig, config));
    		        
    		        jnms.xtypes.forms.textareas.editArea.superclass.initComponent.apply(this, arguments);
    		
    		    } 
    			,initEvents: function(){
    				jnms.xtypes.forms.textareas.editArea.superclass.initEvents.call(this);
    				
    			}
    		    ,onRender:function() {
    		        jnms.xtypes.forms.textareas.editArea.superclass.onRender.apply(this, arguments);
    				//this.on('keyup',this.onFilter, this, {buffer: 500});
    		    }
    		   
    		}); // end of extend
    		
    		Ext.reg('texteditarea', jnms.xtypes.forms.textareas.editArea);
    		
    
    		// Edit grid 
    		jnms.xtypes.grids.editGrid =  Ext.extend(Ext.grid.EditorGridPanel, {
    				 
    				 initComponent:function() {
    						var config = {
    							 //closable: true,
    						 	 sm: new Ext.grid.RowSelectionModel({
    							 	singleSelect: true
    							 }),
    							 header:false,
    						     stripeRows: true
    							 
    						}; // eo config object
    						  
    						// apply config
    						Ext.apply(this, Ext.apply(this.initialConfig, config));
    				  		
    						
    				 		// call parent
    				 		jnms.xtypes.grids.editGrid.superclass.initComponent.apply(this, arguments);
    		
    		 		}, // eo function initComponent
    		 		initEvents: function(){
    					jnms.xtypes.grids.editGrid.superclass.initEvents.call(this);
    				},
    		 		onRender: function(){
    		 			this.store.baseParams.CLASS = this.Class;
    		 			jnms.xtypes.grids.editGrid.superclass.onRender.apply(this, arguments);
    					
    					//this.dz = new jnms.xtypes.grids.GridDropZone(this, {ddGroup:this.ddGroup || 'GridDD'});
    		 		},
    				/**
    				* Called when records are dropped on this grid
    		 		* @param {Ext.grid.GridPanel} srcGrid The source grid
    		 		* @param {Array} records Array of dropped records
    		 		*/
    				onRecordsDrop:Ext.emptyFn,
    				onSave: function(e){
    					
    				},
    				validateEdit: function(e){
    					//console.dir(e);
    					//console.log("Something was validated");
    				},
    				addBlankRecord: function() {
    			        var store = this.getStore();
    					//console.dir(store);
    			        var orec, row;
    			        if(store.recordType) {
    						//console.log("There is a record type");
    			            orec = new store.recordType();
    			            orec.data = {};
    			            orec.fields.each(function(field) {
    			                orec.data[field.name] = field.defaultValue;
    			            });
    			            orec.data.newRecord = true;
    			            orec.commit();
    			            store.add(orec);
    			
    			            row = store.indexOf(orec);
    			            if(row && undefined !== this.editOnAddCol) {
    			                this.startEditing(row, this.editOnAddCol);
    			            }
    			        }
    			    }
    		 		
    		 });
    		 
    		 Ext.reg('editgrid', jnms.xtypes.grids.editGrid); 
    		
    		
    		 
    		 jnms.xtypes.panels.managePanel   = Ext.extend(Ext.Panel, {
    		 
    			//id: 'managePanel',
    			title: this.title,
    			
    		    initComponent:function() {
    		        // {{{
    		        // hard coded (cannot be changed from outside)
    				
    		        var config = {
    					frame: true,
    					//id: this.id,
    					width: this.width,
    					height: this.height,
    					layout: 'border',
    					region: 'center',
    					items: [{
    							region: 'center',
    							xtype: 'editgrid',
    						 	header: true,
    						 	title: "Create/Update Command Templates",
    						 	height: 600,
    						 	store: {
    						 		xtype: 'managestore',
    						 		url: '/jnms/jnms/cgi/admin/control_comm.cgi',
    						 		root: 'data',
    						 		fields: ['mtCommandTemplate', 'mtResultTemplate', 'fk_ConnectionType_iConnectionTypeId', 'fk_Version_biVersionId', 'fk_TemplateType_biTemplateTypeId', 'mtTemplateAttributes'],
    						 		baseParams: {
    						 			CLASS: 'Jnms::Database::CommandResultTemplate',
    						 			TASK: 'statement',
    						 			TASKTYPE: 'immediate',
    						 			ACTION: 'list_all',
    						 			PREFIX: 'Database',
    						 			ROOT: 'data',
    						 			TYPE: 'standard'
    						 		}
    						 	},
    						 	Class: 'Jnms::Database::CommandResultTemplate',
    							viewConfig: {
    								forceFit: true
    							},
    							loadMask: true,
    							plugins: [new Ext.ux.grid.RowEditor({
    								saveText: 'Save',
    								listeners: {
    										'afteredit': this.onRowEditorAfterEdit,
    										'beforeedit': this.onRowEditorBeforeEdit
    												
    								}
    							})],
    							
    							columns: [{
    								header: "Commands",
    								width: 100,
    								height: 50,
    								dataIndex: 'mtCommandTemplate',
    								editor: {
    									xtype: 'texteditarea',
    									listeners: {
    										focus: function(){
    											this.ownerCt.ownerCt.activeId = this.id;
    										},
    										blur: function(area){
    											this.ownerCt.ownerCt.activeId = this.id;
    										}
    									},
    									height: 50
    								}
    							}, {
    								header: "Results",
    								width: 100,
    								height: 50,
    								dataIndex: 'mtResultTemplate',
    								editor: {
    									xtype: 'texteditarea',
    									listeners: {
    										focus: function(){
    											this.ownerCt.ownerCt.activeId = this.id;
    										},
    										blur: function(area){
    											this.ownerCt.ownerCt.activeId = this.id;
    										}
    									},
    									height: 50
    								}
    							}],
    							
    							tbar: { 
    									xtype: 'topeditgridtoolbar',
    									items: [
    												{
    													text: 'Save Template',
    													xtype: 'button',
    													iconCls: 'icon-save',
    													listeners: {
    													'click': function(){	
    													}
    												}
    												},				
    												'->',				
    												{
    													text:'Add'
    													,xtype: 'button'
    													,iconCls:'add'
    													,listeners: {
    														'click': function(){
    															var grid = this.findParentByType('editgrid');
    															
    															if (grid) {
    																var u = new grid.store.recordType({
    																	mtCommandTemplate: '',
    																	mtResultTemplate: ''
    																});
    																grid.plugins[0].stopEditing();
    																var index = grid.store.getCount();
    																grid.store.insert(index, u);
    																grid.getView().refresh();
    																grid.getSelectionModel().selectRow(index);
    																//
    																grid.plugins[0].startEditing(index,index);
    																
    																
    															}
    															//grid.addBlankRecord();
    														}
    													}
    												},
    												'-',{
    													text:'Remove'
    													,xtype: 'button'
    													,iconCls:'del'
    													,listeners: {
    														'click': function(){
    															
    															var grid = this.findParentByType('editgrid');
    															
    															grid.plugins[0].stopEditing();
    													        var rec = grid.getSelectionModel().getSelected();
    													        if (!rec) {
    													            return false;
    													        }
    													        grid.store.remove(rec);
    															grid.getView().refresh();
    															grid.plugins[0].fireEvent('afteredit',this);
    															
    														}
    													}
    												}
    											]
    									}
    						}]
    				};
    		        
    		        // apply config
    		        Ext.apply(this, config);
    		        Ext.apply(this.initialConfig, config);
    		
    		        // }}}
    		 
    		        // call parent
    		        jnms.xtypes.panels.managePanel.superclass.initComponent.apply(this, arguments);
    		        // after parent code here, e.g. install event handlers
    		 
    		    }, // eo function initComponent
    			initEvents: function(){
    				jnms.xtypes.panels.managePanel.superclass.initEvents.call(this);
    			},
    			
    		    onRender:function() {
    		        // before parent code
    		        // call parent
    		        jnms.xtypes.panels.managePanel.superclass.onRender.apply(this, arguments);
    		        
    		 
    		    } // eo function onRender
    		    ,onRowEditorAfterEdit: function(e){
    				console.info("After Edit");
    				
    				Ext.Ajax.request({
    		            url:  '/tasc/control_comm',
    		            scope: this,
    		            params: {
    		                CLASS:  'Test',
    		            },
    		            success: function(response, opts){
    		                var responseObject = Ext.decode(response.responseText);
    		            },
    		            failure: function(response,opts){ }
    		        });
    			}
    			,onRowEditorBeforeEdit: function(e){
    				console.info("Before Edit");
    			}
    		    // }}}
    		    // any other added/overrided methods
    		}); // eo extend
    		 
    		// register an xtype with this class
    		Ext.reg('managepanel', jnms.xtypes.panels.managePanel);
    		
    		jnms.xtypes.viewport = Ext.extend(Ext.Viewport, {
      
    			 // configurables
    			 // anything that is here can be configured from outside
    			 layout:'border'
    			 
    			 ,style:'padding:5px;'
    			 ,initComponent:function() {
    			 
    				 var config = {
    				 	 id: 'jnmsViewport',
    				 	 layout: 'border',
    				 	 style: { 'padding-top' : '10px','padding-bottom' : '10px' },
    					 
    					 items: [{
    					 	xtype: 'managepanel'
    					 }]
    				 };
    			 
    			 
    				 // apply config
    				 Ext.apply(this, Ext.apply(this.initialConfig, config));
    				  
    				 // call parent
    				 jnms.xtypes.viewport.superclass.initComponent.apply(this, arguments);
    			 
    			 /* add handler to the links in the "JNMS CONTROL" menu on left */
    			
    			 } // e/o function initComponent
    			
    		     ,afterRender: function(){
    				jnms.xtypes.viewport.superclass.afterRender.apply(this, arguments);
    	
    			 }
    		  
    		}); // e/o extend
    		
    		
    		Ext.reg('jnmsviewport', jnms.xtypes.viewport);
    
    		</script>
            <!-- page specific -->
     
            <script type="text/javascript">
            Ext.BLANK_IMAGE_URL = '../../resources/images/default/s.gif';
    
                Ext.namespace('jnms');
    
    			// create application
    			jnms.app = function() {
    			      // do NOT access DOM from here; elements don't exist yet
    			      // private variables
    			      // private functions
    			      // public space
    			      return {
    			      // public properties, e.g. strings to translate
    			      // public methods
    			            init: function() {
    			                      
    								   var jnmsViewport = new jnms.xtypes.viewport({});
    			         				
    			         	}
    			        }
          
    			}(); // end of app
            </script>
     
        </head>
        <body>
        	<script type="text/javascript">
            	Ext.onReady(jnms.app.init, jnms.app);
        	</script>
        </body>
    </html>
    See this URL : http://


    Steps to reproduce the problem:
    • Make sure to turn on firebug
    • Add a row and enter text into the first column and first row (ddddd for example), then click save. All is fine as the afteredit event fires and the ajax request is generated
    • Then edit the text and change it and hit tab. Then click save. You will notice no afteredit event fires and no ajax request is generated.

    The result that was expected:
    • afteredit should always fire even if I start editing the row and enter no text and then click save. My assumption is that whenever I go into edit mode, whether I change a row of text or leave it, the after edit should always fire when I click save because I was originally in editing mode and then stopped.

    The result that occurs instead:
    • Sometimes 'afteredit' doesn't fire when I edit an existing row and click save.

    Screenshot or Video:
    • attached

    Debugging already done:
    • looks like it might be related to the comparison of the old value and the new value. Sometimes oldValue = this.record is already changed to the new value, so when we get to the if comparison there is no change and the logic is skipped

    Possible fix:
    • Fix the logic so that old value never = new value if there is any difference at all between the two, and make sure that tabbing to another column works as well.

  2. #2
    Sencha User Jamie Avins's Avatar
    Join Date
    Mar 2007
    Location
    Redwood City, California
    Posts
    3,661
    Vote Rating
    18
    Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough

      0  

    Default


    Thank you for the report.

  3. #3
    Sencha User
    Join Date
    May 2009
    Posts
    6
    Vote Rating
    0
    BenJenkin is on a distinguished road

      0  

    Default Same problem

    Same problem


    Been having it for over 6 months now. The problem is this error is very difficult to reproduce as it sometimes works without problem. Often editing the text 3 or 4 times will cause it to work for my code (Different than here). After it starts to work it will then work for all records.

    Pressing the return key instead of clicking the update button sometimes works when clicking does not. However, recently when the return key does not work I receive this error message in firebug.

    The 'charCode' property of a keyup event should not be used. The value is meaningless.

    Again after re-editing a few times it will start to work and will then work for all records.

  4. #4
    Sencha Premium Member
    Join Date
    Sep 2010
    Posts
    129
    Vote Rating
    6
    Tod is on a distinguished road

      0  

    Default


    I am experiencing a similar problem. I have two grids. If I edit data in one grid and then click a cell on the other grid, the afterEdit event never fires for the first grid. For me, this is 100% repeatable. There is a cell select on the first grid. Capturing all events shows that the afterEdit does not fire -- this is IE only. Works fine in Firefox.
    -Tod

  5. #5
    Ext JS Premium Member
    Join Date
    Jan 2008
    Posts
    315
    Vote Rating
    0
    josh803316 is on a distinguished road

      0  

    Default


    The workaround we have been using since this hasn't been fixed yet is to set hasChange = true (line 199). This basically forces the afteredit event to get fired. It's the only reliable workaround even though it isn't an elegant one.

  6. #6
    Sencha Premium Member
    Join Date
    Sep 2010
    Posts
    129
    Vote Rating
    6
    Tod is on a distinguished road

      0  

    Default


    line 199 of what file? Sorry, perhaps I'm missing the obvious...fighting IE vs. Firefox all day long.
    -Tod

  7. #7
    Ext JS Premium Member
    Join Date
    Jan 2008
    Posts
    315
    Vote Rating
    0
    josh803316 is on a distinguished road

      0  

    Default


    the row editor file:
    examples/ux/RowEditor.js

  8. #8
    Sencha Premium Member
    Join Date
    Sep 2010
    Posts
    129
    Vote Rating
    6
    Tod is on a distinguished road

      0  

    Default


    Thanks.
    -Tod

  9. #9
    Sencha - Community Support Team Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,246
    Vote Rating
    97
    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 Condor has much to be proud of

      0  

    Default


    I don't agree. If no changes were made it should fire a canceledit event and not an afteredit event.

    Code:
    Ext.override(Ext.ux.grid.RowEditor, {
        stopEditing : function(saveChanges){
            this.editing = false;
            if(!this.isVisible()){
                return;
            }
            if(saveChanges === false || !this.isValid()){
                this.hide();
                this.fireEvent('canceledit', this, saveChanges === false);
                return;
            }
            var changes = {},
                r = this.record,
                hasChange = false,
                cm = this.grid.colModel,
                fields = this.items.items;
            for(var i = 0, len = cm.getColumnCount(); i < len; i++){
                if(!cm.isHidden(i)){
                    var dindex = cm.getDataIndex(i);
                    if(!Ext.isEmpty(dindex)){
                        var oldValue = r.data[dindex],
                            value = this.postEditValue(fields[i].getValue(), oldValue, r, dindex);
                        if(String(oldValue) !== String(value)){
                            changes[dindex] = value;
                            hasChange = true;
                        }
                    }
                }
            }
            if(hasChange && this.fireEvent('validateedit', this, changes, r, this.rowIndex) !== false){
                r.beginEdit();
                Ext.iterate(changes, function(name, value){
                    r.set(name, value);
                });
                r.endEdit();
                this.fireEvent('afteredit', this, changes, r, this.rowIndex);
            } else {
                this.fireEvent('canceledit', this, false);
            }
            this.hide();
        }
    });

  10. #10
    Ext JS Premium Member
    Join Date
    Jan 2008
    Posts
    315
    Vote Rating
    0
    josh803316 is on a distinguished road

      0  

    Default


    Right, I agree as well......I think this has already been fixed in the latest release though hasn't it?

Similar Threads

  1. Replies: 1
    Last Post: 15 Sep 2009, 6:47 PM
  2. Replies: 1
    Last Post: 26 Aug 2009, 11:41 PM
  3. [FIXED][3.0rc1] row editor doesn't work in IE Quirks mode
    By wopenonline111 in forum Ext 3.x: Bugs
    Replies: 8
    Last Post: 4 Jun 2009, 4:31 AM
  4. [FIXED] [3.0rc1] row editor doesn't work in IE Quirks mode
    By wopenonline111 in forum Ext 2.x: Bugs
    Replies: 2
    Last Post: 15 Apr 2009, 1:34 PM

Thread Participants: 6