Slims
Slims

Reputation: 864

ExtJs - Errors in GridPanel using CellEditor after sorting a column

Seen using ExtJs 5.1.0, Chrome 44.0+

I'm having an intermittent issue where when using the CellEditor to modify the contents of a grid panel, errors are thrown when trying to edit cells after sorting one of the columns.

Here is the stack trace:

Uncaught TypeError: Cannot read property 'parentNode' of nullExt.define.onShow @ CellEditor.js?ver=911111541:147
Ext.define.show @ Component.js?ver=911111541:5104
Ext.define.startEdit @ Editor.js?ver=911111541:333
Ext.Base.Base.addMembers.callParent @ Base.js?ver=911111541:1255
Ext.define.startEdit @ CellEditor.js?ver=911111541:132
Ext.define.showEditor @ CellEditing.js?ver=911111541:536
Ext.define.startEdit @ CellEditing.js?ver=911111541:479
Ext.define.onCellClick @ Editing.js?ver=911111541:446
fire @ Event.js?ver=911111541:387
doFireEvent @ Observable.js?ver=911111541:654
prototype.doFireEvent @ EventDomain.js?ver=911111541:293
fireEventArgs @ Observable.js?ver=911111541:587
fireEvent @ Observable.js?ver=911111541:540
Ext.define.processItemEvent @ Table.js?ver=911111541:2445
Ext.define.processUIEvent @ View.js?ver=911111541:619
Ext.define.handleEvent @ View.js?ver=911111541:562
fire @ Event.js?ver=911111541:387
Ext.define.fire @ Dom.js?ver=911111541:367
Ext.define.publish @ Dom.js?ver=911111541:339
Ext.define.doDelegatedEvent @ Dom.js?ver=911111541:398
Ext.define.onDelegatedEvent @ Dom.js?ver=911111541:379
(anonymous function) @ Function.js?ver=911111541:145

The relevant bit of code in the CellEditor's onShow method:

if (me.el.dom.parentNode !== me.renderTo) {
    me.renderTo.appendChild(me.el.dom);
}

The grid itself is empty to start with. It has a toolbar associated with it with buttons that allow you to add/duplicate/delete rows.

Here is a fiddle that represents this setup (unable to reproduce in the fiddle however):

https://fiddle.sencha.com/#fiddle/top

The steps to reproduce are roughly as follows:

In Chrome 44.0 (Not seen in other browsers)

1) Add a new row, fill in the fields
2) Sort on field2
3) Add three new rows
4) Add a value to one of the new rows under column for field 2
5) Sort on field2
6) Try to edit a cell in the field2 column

At this point, around 10% of the time, the error will show up and the grid will be broken. If at step 6 the problem isn't reproduced, you can navigate to another tab in the browser, wait a couple minutes, go back to the grid and try to edit it, and the problem will happen. This is near 100% reproduction rate.

Note that the steps aren't exact. You could, for example, try this using the first column instead. Or add some arbitrary number of rows. The longer you play with the grid and try sorting it, the higher the chance you'll see the error.

If you see the error and then try to edit any cell in the grid again, you will get this error:

Uncaught TypeError: Cannot read property 'style' of nullExt.define.setSize @ CompositeElementLite.js?ver=911111541:1674
Ext.define.setSize @ Component.js?ver=911111541:4922
Ext.define.setWidth @ Component.js?ver=911111541:5033
Ext.define.realign @ CellEditor.js?ver=911111541:282
Ext.define.startEdit @ Editor.js?ver=911111541:334
Ext.Base.Base.addMembers.callParent @ Base.js?ver=911111541:1255
Ext.define.startEdit @ CellEditor.js?ver=911111541:132
Ext.define.showEditor @ CellEditing.js?ver=911111541:536
Ext.define.startEdit @ CellEditing.js?ver=911111541:479
Ext.define.onCellClick @ Editing.js?ver=911111541:446
fire @ Event.js?ver=911111541:387
doFireEvent @ Observable.js?ver=911111541:654
prototype.doFireEvent @ EventDomain.js?ver=911111541:293
fireEventArgs @ Observable.js?ver=911111541:587
fireEvent @ Observable.js?ver=911111541:540
Ext.define.processItemEvent @ Table.js?ver=911111541:2445
Ext.define.processUIEvent @ View.js?ver=911111541:619
Ext.define.handleEvent @ View.js?ver=911111541:562
fire @ Event.js?ver=911111541:387
Ext.define.fire @ Dom.js?ver=911111541:367
Ext.define.publish @ Dom.js?ver=911111541:339
Ext.define.doDelegatedEvent @ Dom.js?ver=911111541:398
Ext.define.onDelegatedEvent @ Dom.js?ver=911111541:379(anonymous function) @ Function.js?ver=911111541:145

Here's some of my code which may or may not be relevant:

In the initComponent function, I first create the cellediting plugin:

this._cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
  clicksToEdit: 1,
disabled: this.getDisableEdit()
});

This will be assigned to the plugin config item later for the grid itself:

plugins: [
  this._cellEditing
]

Here's the add button's logic:

// Create a record instance through the ModelManager
var newRow = new BidPackageSchedulesGridPanel.Model();
this.getStore().add(newRow);
if (moveToRow) {
  this._cellEditing.startEdit(newRow, 1);
}

And the model itself:

Ext.define('BidPackageSchedulesGridPanel.Model', {
  extend: "Ext.data.Model",
fields: [
    FieldIds.FIELD_1,
FieldIds.FIELD_2,
FieldIds.FIELD_3,
// Must specify a default value of "" in order to display null values correctly in IE.
{
      name: FieldIds.QUANTITY,
type: 'string',
defaultValue: ""
}

  ],
validators: {
    FIELD_1: [presenceValidator, 'length'],
FIELD_2: 'length',
FIELD_3: 'length',
QUANTITY: presenceValidator
  },
identifier: 'sequential'
});

And where the store is initialized in the config for the grid panel:

store:  {
  model: Ext.create("BidPackageSchedulesGridPanel.Model"),
data: []
},

We use a data provider to load the store data.

The columns for the first three fields are created using this logic:

_createColumn: function(label, dataIndex) {
  var column = {
    text: Ext.String.htmlEncode(label),
dataIndex: dataIndex,
sortable: this.isJobSchedule(),
draggable: false,
flex: 1
};

if (this.isJobSchedule() || dataIndex == FieldIds.FIELD_3) {
    Ext.apply(column, {
      editor: {
        xtype: 'textfield'
}
    });
}
  return column;
},

Let me know if I can provide any additional code. I suspect this is a bug in Ext, and I've filed a bug report with them, but a workaround would be sufficient for now if necessary.

Thanks!

Upvotes: 1

Views: 1233

Answers (1)

Slims
Slims

Reputation: 864

I have fixed this issue, however, it may have unforeseen consequences. I noticed this comment in CellEditor.js, in the setGridMethod:

        // On view refresh, we need to copy our DOM into the detached body to prevent it from being garbage collected.
        view.on(viewListeners);

And indeed, I put a breakpoint in the GarbageCollector and saw that cell editor node was being garbage collected improperly. I set the garbage collector interval (In GarbageCollector.js) to 10 seconds. After adding data to the grid and sorting, and the ten second window passing, the problem was reproducible 100% of the time. It was being garbage collected because its parent node was null (see the AND condition in Ext.isGarbage).

In the CellEditor.onViewRefresh() function, for the component that was being garbage collected, this method was not giving the component a parent node, and then it was being garbage collected.

I changed

    } else if (!sorting) {
        Ext.getDetachedBody().dom.appendChild(dom);
    }

to

    } else {
        Ext.getDetachedBody().dom.appendChild(dom);
    }

and the problem has gone away. I've tested the grid editing functionality and nothing appears to be broken.

Upvotes: 1

Related Questions