You found a bug! We've classified it as EXTJS-10203 . We encourage you to continue the discussion and to find an acceptable workaround while we work on a permanent fix.
  1. #1
    Sencha Premium Member
    Join Date
    May 2009
    Posts
    3
    Vote Rating
    2
    borcherspm is on a distinguished road

      0  

    Default store editing with grid selection model cannot be edited multiple times

    REQUIRED INFORMATION


    Ext version tested:
    • Ext 4.2.1
    Browser versions tested against:
    • IE9
    • Firefox 17
    • Chrome
    • phantomjs
    Description:
    • I'm trying to edit a store record by using the selection model. The second time I try to edit the record from the selectionmodel it does not update the store.
    Steps to reproduce the problem:
    1. load a store attached to a grid
    2. grab record using grid.getSelectionModel().getSelection()
    3. edit and save the record to a server
    4. on write event of the store, reload store
    5. try #2-3 again. store will not save to the server
    The result that was expected:
    • I should be able to keep editing a record, reload store, and edit that row again without having to select a different row and reselect the row I want to edit.
    Test Case:

    Code:
    var server = new MockHttpServer();
    server.start();
    
    
    server.handle = function (request) {
        request.setResponseHeader("Content-Type", "application/json");
        
        if(request.urlParts.path==='/dris/data/test.json'){
            json = {
                    rows :  [{
                        id : 3,
                        field1: 10,
                        field2: 'testdata'
                    }],
                    success: true,
                    total : 1
            };
            request.receive(200, Ext.encode(json) );
        }
        else if(request.urlParts.path=== '/dris/data/update.json'){
            json = {
                    rows :  [{
                        id : 3,
                        field1: 14,
                        field2: 'testdata2'
                    }],
                    success: true,
                    total : 1
            };
            request.receive(200, Ext.encode(json) );
        }
    };
    
    
    Ext.onReady(function(){
        Ext.create('Ext.data.Store',{
            autoLoad: true,
            autoSync: true,
            storeId: 'teststore',
            model: Ext.define('testmodel',{
                extend: 'Ext.data.Model',
                fields: [{
                    name:"id",
                    type:"int"
                },{
                    name:"field1",
                    type:"int"
                },{
                    name:"field2",
                    type:"string"
                }],
                proxy : {
                    type : 'ajax',
                    api: {
                        read: '/dris/data/test.json',
                        update: '/dris/data/update.json'
                    },
                    reader : {
                        type : 'json',
                        root: 'rows'
                    },
                    writer: {
                        type: 'json'
                    }
                }
            })
        });
        
        Ext.create('Ext.panel.Panel',{
            renderTo: Ext.get('center'),
            items: [{
                xtype: 'grid',
                id : 'testgrid',
                title : 'Data',
                region: 'center',
                enableColumnMove: false,
                columns : [{
                    header : 'ID',
                    dataIndex: 'id',
                    width: 100
                },{
                    header : 'Number',
                    dataIndex: 'field1',
                    width: 100
                },{
                    header : 'String',
                    dataIndex: 'field2',
                    width: 100
                }],
                store : 'teststore'
            }]
        });
    });
    
    
    
    
    describe("defect with saving a store and selecting the same row again", function() {        
        it("test", function() {
               var store = Ext.StoreMgr.get('teststore');        var grid = Ext.getCmp('testgrid');
            
            grid.getSelectionModel().select(0);
            
            var record = grid.getSelectionModel().getSelection()[0];
            record.set('field1',14);
            record.set('field2','testdata2');
            store.reload();
            var record = grid.getSelectionModel().getSelection()[0];
            expect(record.store).toBeNull();//the store is now missing from the record
            record.set('field1',15);
            record.set('field2','testdata3');
            expect(record.modified.field1).not.toBeNull();
            expect(record.modified.field2).not.toBeNull();
            
    //at this point the store record is not modifed even though I set two different fields on it.
            expect(store.getAt(0).modified.field1).toBeUndefined();
            expect(store.getAt(0).modified.field2).toBeUndefined();
        });
    });


    HELPFUL INFORMATION


    Debugging already done:
    • none
    Possible fix:
    • not provided
    Additional JS used:
    • using mock.js to mock xhr requests.
    • using jasmine.js to run unit tests
    Operating System:
    • Windows 7
    *EDIT BY SLEMMON
    Issue observed in 4.2.1, 4.2, 4.1.3, 4.1.1.
    Inline test case (modified the Writer example) with test sequence highlighted and bolded
    Code:
    Ext.define('Writer.Form', {
        extend: 'Ext.form.Panel',
        alias: 'widget.writerform',
    
    
        requires: ['Ext.form.field.Text'],
    
    
        initComponent: function(){
            this.addEvents('create');
            Ext.apply(this, {
                activeRecord: null,
                iconCls: 'icon-user',
                frame: true,
                title: 'User -- All fields are required',
                defaultType: 'textfield',
                bodyPadding: 5,
                fieldDefaults: {
                    anchor: '100%',
                    labelAlign: 'right'
                },
                items: [{
                    fieldLabel: 'Email',
                    name: 'email',
                    allowBlank: false,
                    vtype: 'email'
                }, {
                    fieldLabel: 'First',
                    name: 'first',
                    allowBlank: false
                }, {
                    fieldLabel: 'Last',
                    name: 'last',
                    allowBlank: false
                }],
                dockedItems: [{
                    xtype: 'toolbar',
                    dock: 'bottom',
                    ui: 'footer',
                    items: ['->', {
                        iconCls: 'icon-save',
                        itemId: 'save',
                        text: 'Save',
                        disabled: true,
                        scope: this,
                        handler: this.onSave
                    }, {
                        iconCls: 'icon-user-add',
                        text: 'Create',
                        scope: this,
                        handler: this.onCreate
                    }, {
                        iconCls: 'icon-reset',
                        text: 'Reset',
                        scope: this,
                        handler: this.onReset
                    }]
                }]
            });
            this.callParent();
        },
    
    
        setActiveRecord: function(record){
            this.activeRecord = record;
            if (record) {
                this.down('#save').enable();
                this.getForm().loadRecord(record);
            } else {
                this.down('#save').disable();
                this.getForm().reset();
            }
        },
    
    
        onSave: function(){
            var active = this.activeRecord,
                form = this.getForm();
    
    
            if (!active) {
                return;
            }
            if (form.isValid()) {
                form.updateRecord(active);
                this.onReset();
            }
        },
    
    
        onCreate: function(){
            var form = this.getForm();
    
    
            if (form.isValid()) {
                this.fireEvent('create', this, form.getValues());
                form.reset();
            }
    
    
        },
    
    
        onReset: function(){
            this.setActiveRecord(null);
            this.getForm().reset();
        }
    });
    
    
    Ext.define('Writer.Grid', {
        extend: 'Ext.grid.Panel',
        alias: 'widget.writergrid',
    
    
        requires: [
            'Ext.grid.plugin.CellEditing',
            'Ext.form.field.Text',
            'Ext.toolbar.TextItem'
        ],
    
    
        initComponent: function(){
    
    
            this.editing = Ext.create('Ext.grid.plugin.CellEditing');
    
    
            Ext.apply(this, {
                iconCls: 'icon-grid',
                frame: true,
                plugins: [this.editing],
                dockedItems: [{
                    xtype: 'toolbar',
                    items: [{
                        iconCls: 'icon-add',
                        text: 'Add',
                        scope: this,
                        handler: this.onAddClick
                    }, {
                        iconCls: 'icon-delete',
                        text: 'Delete',
                        disabled: true,
                        itemId: 'delete',
                        scope: this,
                        handler: this.onDeleteClick
                    }]
                }, {
                    weight: 2,
                    xtype: 'toolbar',
                    dock: 'bottom',
                    items: [{
                        xtype: 'tbtext',
                        text: '<b>@cfg</b>'
                    }, '|', {
                        text: 'autoSync',
                        enableToggle: true,
                        pressed: true,
                        tooltip: 'When enabled, Store will execute Ajax requests as soon as a Record becomes dirty.',
                        scope: this,
                        toggleHandler: function(btn, pressed){
                            this.store.autoSync = pressed;
                        }
                    }, {
                        text: 'batch',
                        enableToggle: true,
                        pressed: true,
                        tooltip: 'When enabled, Store will batch all records for each type of CRUD verb into a single Ajax request.',
                        scope: this,
                        toggleHandler: function(btn, pressed){
                            this.store.getProxy().batchActions = pressed;
                        }
                    }, {
                        text: 'writeAllFields',
                        enableToggle: true,
                        pressed: false,
                        tooltip: 'When enabled, Writer will write *all* fields to the server -- not just those that changed.',
                        scope: this,
                        toggleHandler: function(btn, pressed){
                            this.store.getProxy().getWriter().writeAllFields = pressed;
                        }
                    }]
                }, {
                    weight: 1,
                    xtype: 'toolbar',
                    dock: 'bottom',
                    ui: 'footer',
                    items: ['->', {
                        iconCls: 'icon-save',
                        text: 'Sync',
                        scope: this,
                        handler: this.onSync
                    }]
                }],
                columns: [{
                    text: 'ID',
                    width: 40,
                    sortable: true,
                    resizable: false,
                    draggable: false,
                    hideable: false,
                    menuDisabled: true,
                    dataIndex: 'id'
                }, {
                    header: 'Email',
                    flex: 1,
                    sortable: true,
                    dataIndex: 'email',
                    field: {
                        type: 'textfield'
                    }
                }, {
                    header: 'First',
                    width: 100,
                    sortable: true,
                    dataIndex: 'first',
                    field: {
                        type: 'textfield'
                    }
                }, {
                    header: 'Last',
                    width: 100,
                    sortable: true,
                    dataIndex: 'last',
                    field: {
                        type: 'textfield'
                    }
                }]
            });
            this.callParent();
            this.getSelectionModel().on('selectionchange', this.onSelectChange, this);
        },
        
        onSelectChange: function(selModel, selections){
            this.down('#delete').setDisabled(selections.length === 0);
        },
    
    
        onSync: function(){
            this.store.sync();
        },
    
    
        onDeleteClick: function(){
            var selection = this.getView().getSelectionModel().getSelection()[0];
            if (selection) {
                this.store.remove(selection);
            }
        },
    
    
        onAddClick: function(){
            var rec = new Writer.Person({
                first: '',
                last: '',
                email: ''
            }), edit = this.editing;
    
    
            edit.cancelEdit();
            this.store.insert(0, rec);
            edit.startEditByPosition({
                row: 0,
                column: 1
            });
        }
    });
    
    
    Ext.define('Writer.Person', {
        extend: 'Ext.data.Model',
        fields: [{
            name: 'id',
            type: 'int',
            useNull: true
        }, 'email', 'first', 'last'],
        validations: [{
            type: 'length',
            field: 'email',
            min: 1
        }, {
            type: 'length',
            field: 'first',
            min: 1
        }, {
            type: 'length',
            field: 'last',
            min: 1
        }]
    });
    
    
    Ext.require([
        'Ext.data.*',
        'Ext.tip.QuickTipManager',
        'Ext.window.MessageBox'
    ]);
    
    
    Ext.onReady(function(){
        Ext.tip.QuickTipManager.init();
        
        Ext.create('Ext.button.Button', {
            margin: '0 0 20 20',
            text: 'Reset sample database back to initial state',
            renderTo: document.body,
            tooltip: 'The sample database is stored in the session, including any changes you make. Click this button to reset the sample database to the initial state',
            handler: function(){
                Ext.getBody().mask('Resetting...');
                Ext.Ajax.request({
                    url: 'app.php/example/reset',
                    callback: function(options, success, response) {
                        Ext.getBody().unmask();
                        
                        var didReset = true,
                            o;
                        
                        if (success) {
                            try {
                                o = Ext.decode(response.responseText);
                                didReset = o.success === true;
                            } catch (e) {
                                didReset = false;
                            }
                        } else {
                            didReset = false;
                        }
                        
                        if (didReset) {
                            store.load();
                            main.down('#form').setActiveRecord(null);
                            Ext.example.msg('Reset', 'Reset successful');
                        } else {
                            Ext.MessageBox.alert('Error', 'Unable to reset example database');
                        }
                        
                    }
                });
            }
        })
        
        var store = Ext.create('Ext.data.Store', {
            model: 'Writer.Person',
            autoLoad: true,
            autoSync: true,
            proxy: {
                type: 'ajax',
                api: {
                    read: 'app.php/users/view',
                    create: 'app.php/users/create',
                    update: 'app.php/users/update',
                    destroy: 'app.php/users/destroy'
                },
                reader: {
                    type: 'json',
                    successProperty: 'success',
                    root: 'data',
                    messageProperty: 'message'
                },
                writer: {
                    type: 'json',
                    writeAllFields: false,
                    root: 'data'
                },
                listeners: {
                    exception: function(proxy, response, operation){
                        Ext.MessageBox.show({
                            title: 'REMOTE EXCEPTION',
                            msg: operation.getError(),
                            icon: Ext.MessageBox.ERROR,
                            buttons: Ext.Msg.OK
                        });
                    }
                }
            },
            listeners: {
                write: function(proxy, operation){
                    if (operation.action == 'destroy') {
                        main.child('#form').setActiveRecord(null);
                    }
                    Ext.example.msg(operation.action, operation.resultSet.message);
                }
            }
        });
    
    
        var main = Ext.create('Ext.container.Container', {
            padding: '0 0 0 20',
            width: 500,
            height: Ext.themeName === 'neptune' ? 500 : 450,
            renderTo: document.body,
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            items: [{
                itemId: 'form',
                xtype: 'writerform',
                manageHeight: false,
                margins: '0 0 10 0',
                listeners: {
                    create: function(form, data){
                        store.insert(0, data);
                    }
                }
            }, {
                itemId: 'grid',
                xtype: 'writergrid',
                title: 'User List',
                flex: 1,
                store: store,
                listeners: {
                    selectionchange: function(selModel, selected) {
                        main.child('#form').setActiveRecord(selected[0] || null);
                    },
                    viewready: {
                        fn: function (view) {
                            var grid = main.down('writergrid');
                            var selModel = grid.getSelectionModel();
    
    
                            selModel.select(1);
    
    
                            var record = selModel.getSelection()[0];
                            record.set('first',14);
                            record.set('last','testdata2');
                            grid.getStore().reload({
                                callback: function () {
                                    // deselect > select to remedy
                                    // selModel.deselectAll();
                                    // selModel.select(1);
                                    var record = grid.getSelectionModel().getSelection()[0];
                                    record.set('first',15);
                                    record.set('last','testdata3');
                                }
                            });
                        },
                        delay: 1000
                    }
                }
            }]
        });
    });
    Last edited by slemmon; 13 Jun 2013 at 12:16 PM. Reason: additional test notes

  2. #2
    Sencha - Support Team slemmon's Avatar
    Join Date
    Mar 2009
    Location
    Boise, ID
    Posts
    6,138
    Vote Rating
    240
    slemmon has much to be proud of slemmon has much to be proud of slemmon has much to be proud of slemmon has much to be proud of slemmon has much to be proud of slemmon has much to be proud of slemmon has much to be proud of slemmon has much to be proud of slemmon has much to be proud of

      0  

    Default

    Thanks for the report! I have opened a bug in our bug tracker.

  3. #3
    Sencha User
    Join Date
    Jun 2014
    Location
    Sweden
    Posts
    9
    Vote Rating
    0
    ulrika.sandberg is on a distinguished road

      0  

    Default

    What is the status of this? I'm having this problem also with Ext JS 5.1.