You found a bug! We've classified it as EXTJS-10816 . We encourage you to continue the discussion and to find an acceptable workaround while we work on a permanent fix.
  1. #1
    Sencha User
    Join Date
    Dec 2011
    Posts
    242
    Vote Rating
    7
    ypandey will become famous soon enough

      0  

    Exclamation Unable to use focusRow for Buffered grid.

    For Buffered Grid, this method is not working for records which has not been rendered yet.
    Code:
    grid.getView().focusRow(rowIndex);
    Is there any other way to do the same? Since buffered grid renders records while scrolling.
    I won't mind, if i could animate scroll to the required position(rowIndex).
    Please help me with this! Thanks.

    *EDIT BY SLEMMON
    Issue observed in 4.2.0 and 4.2.1

    Steps to reproduce:
    Using the below test case add a new record at 1998 > add a new record at 2.

    Issue observed:
    The record is added, but scrollTo fails with error: Uncaught TypeError: Cannot call method 'insertSibling' of null

    Inline test case:
    Code:
    var generateData = function (count) {
        var totalRecords = [], i;
        for (i=0; i < count; i++) {
            totalRecords[i] = {
                name: 'name',
                description: 'description'
            };
        };
    
    
    
    
    
    
    
    
        return { data: totalRecords };
    };
    
    
    
    
    Ext.define('MyApp.model.SomeModel', {
        extend: 'Ext.data.Model',
        fields: [{
            name: 'name',
            type: 'string'
        }, {
            name: 'description',
            type: 'string'
        }, 'id']
    });
    
    
    
    
    Ext.define("MyApp.store.SomeStore", {
        extend: 'Ext.data.Store',
        model: 'MyApp.model.SomeModel',
        autoDestroy: true,
        pageSize: 75,
        autoLoad: false,
        sorters: [{
            property: 'name',
            direction: 'ASC'
        }],
        data: generateData(2000),
        proxy: {
            type: 'memory',
            //url: 'data.json',// I use struts action to get 2000 records
            //data: generateData(2000),
            reader: {
                type: 'json',
                root: 'data'
            }
        }
    });
    
    
    
    
    
    
    var someStore = Ext.create('MyApp.store.SomeStore', {
        storeId: 'SomeStore'
    });
    
    
    
    
    Ext.widget('viewport', {
        layout: 'fit',
        items: [{
            xtype: 'grid',
            itemId: 'maingrid',
            store: 'SomeStore',
            loadMask: true,
            tbar: [{
                text: 'Insert at ',
                handler: function (btn) {
                    var grid = btn.up('gridpanel'),
                        store = grid.getStore(),
                        inserted;
    
    
    
    
    
    
    
    
                    inserted = store.insert( btn.up('gridpanel').down('#Position').getValue(), {
                        name: 'New Name',
                        description: 'New Description'
                    })[0];
    
    
    
    
    
    
    
    
                    grid.findPlugin('bufferedrenderer').scrollTo(store.indexOf(inserted), true);
                }
            },{
                xtype: 'numberfield',
                itemId: 'Position',
                minValue: 1,
                value: 1
            }],
            plugins: {
                ptype: 'bufferedrenderer',
                trailingBufferZone: 40, // Keep 40 rows rendered in the table behind scroll
                leadingBufferZone: 80 // Keep 80 rows rendered in the table ahead of scroll
            },
            viewConfig: {
                emptyText: 'No Records',
                deferEmptyText: true,
                trackOver: false
            },
            autoScroll: true,
            border: 0,
            selModel: {
                selType: 'rowmodel',
                pruneRemoved: false,
                mode: 'MULTI'
            },
            columnLines: true,
            columns: [{
                text: 'Name',
                flex: 2,
                minWidth: 120,
                sortable: true,
                dataIndex: 'name'
            }, {
                text: 'Description',
                flex: 3,
                minWidth: 120,
                sortable: true,
                dataIndex: 'description',
                renderer: function (value, metaData, record,
                    rowIdx, colIdx, store) {
                    metaData.tdAttr = 'data-qtip="' + value + '"';
                    return value;
                }
            }]
        }]
    });
    Last edited by ypandey; 1 Aug 2013 at 2:38 AM. Reason: adding generateData method

  2. #2
    Sencha - Support Team slemmon's Avatar
    Join Date
    Mar 2009
    Location
    Boise, ID
    Posts
    6,129
    Vote Rating
    228
    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

    I think you'll need to use the bufferedrenderer's scrollTo method in this case:
    http://docs.sencha.com/extjs/4.2.1/#...ethod-scrollTo
    Are you a Sencha products veteran who has wondered what it might be like to work at Sencha? If so, please reach out to our human resources manager: fabienne.bell@sencha.com

  3. #3
    Sencha User
    Join Date
    Dec 2011
    Posts
    242
    Vote Rating
    7
    ypandey will become famous soon enough

      0  

    Thumbs up

    oh Great! this method looks perfect for me. Could you show how to call grid plugin methods?

  4. #4
    Sencha User
    Join Date
    Dec 2011
    Posts
    242
    Vote Rating
    7
    ypandey will become famous soon enough

      0  

    Exclamation

    This is how I am using buffered grid plugin's scrollTo method:
    Code:
    grid.findPlugin('bufferedrenderer').scrollTo(...);
    Is this the right way? and I am having problems if I give my own Bufferzone

    Code:
    plugins:{
    ptype:'bufferedrenderer',        
    trailingBufferZone: 40,// Keep 40 rows rendered in the table behind scroll        
    leadingBufferZone: 80// Keep 80 rows rendered in the table ahead of scroll
    }

  5. #5
    Sencha - Support Team slemmon's Avatar
    Join Date
    Mar 2009
    Location
    Boise, ID
    Posts
    6,129
    Vote Rating
    228
    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

    The findPlugin or getPlugin methods should both work ok.

    I tried to reproduce an issue using the buffered scrolling example from the Sencha.com ExtJS 4.2.1 examples page and I included your leading and trailing bufferZone sizes and I wasn't able to see any issues. Do you see any difference between the test case and your code that jumps out at you?

    Code:
    Ext.define('Employee', {
        extend: 'Ext.data.Model',
        fields: [{
            name: 'employeeNo'
        }, {
            name: 'rating',
            type: 'int'
        }, {
            name: 'salary',
            type: 'float'
        }, {
            name: 'forename'
        }, {
            name: 'surname'
        }, {
            name: 'email'
        }, {
            name: 'department'
        }, {
            name: 'dob',
            type: 'date',
            dateFormat: 'Ymd'
        }, {
            name: 'joinDate',
            type: 'date',
            dateFormat: 'Ymd'
        }, {
            name: 'noticePeriod'
        }, {
            name: 'sickDays',
            type: 'int'
        }, {
            name: 'holidayDays',
            type: 'int'
        }, {
            name: 'holidayAllowance',
            type: 'int'
        }],
        idField: 'employeeNo'
    });
    
    
    
    
    
    
    function random(from, to) {
        return Math.floor(Math.random() * (to - from + 1) + from);
    }
    
    
    function getEmployeeNo() {
        var out = '',
            i = 0;
        for (; i < 6; ++i) {
            out += random(0, 7);
        }
        return out;
    }
    
    
    /**
     * Returns an array of fake data
     * @param {Number} count The number of fake rows to create data for
     * @return {Array} The fake record data, suitable for usage with an ArrayReader
     */
    
    
    function createFakeData(count, data) {
        var firstNames = ['Ed', 'Tommy', 'Aaron', 'Abe', 'Jamie', 'Adam', 'Dave', 'David', 'Jay', 'Nicolas', 'Nige'],
            lastNames = ['Spencer', 'Maintz', 'Conran', 'Elias', 'Avins', 'Mishcon', 'Kaneda', 'Davis', 'Robinson', 'Ferrero', 'White'],
            departments = ['Engineering', 'Sales', 'Marketing', 'Managment', 'Support', 'Administration'],
            ratings = [1, 2, 3, 4, 5],
            salaries = [100, 400, 900, 1500, 1000000],
            noticePeriods = ['2 weeks', '1 month', '3 months'],
            i;
    
    
        for (i = 0; i < (count || 25); i++) {
            var firstName = firstNames[random(0, firstNames.length - 1)],
                lastName = lastNames[random(0, lastNames.length - 1)],
                name = Ext.String.format("{0} {1}", firstName, lastName),
                email = firstName.toLowerCase() + '.' + lastName.toLowerCase() + '@sentcha.com',
                rating = ratings[(name === 'Nige White') ? 0 : random(0, ratings.length - 1)],
                salary = salaries[(name === 'Nige White') ? 4 : random(0, salaries.length - 1)],
                department = departments[random(0, departments.length - 1)],
                ageInYears = random(23, 55),
                dob = new Date(new Date().getFullYear() - ageInYears, random(0, 11), random(0, 31)),
                joinDate = new Date(new Date() - random(60, 2000) * 1000 * 60 * 60 * 24),
                sickDays = random(0, 10),
                holidayDays = random(0, 10),
                holidayAllowance = random(20, 40);
    
    
            data.push({
                employeeNo: getEmployeeNo(),
                rating: rating,
                salary: salary,
                forename: firstName,
                surname: lastName,
                email: email,
                department: department,
                dob: dob,
                joinDate: joinDate,
                sickDays: sickDays,
                holidayDays: holidayDays,
                holidayAllowance: holidayAllowance,
                noticePeriod: noticePeriods[random(0, noticePeriods.length - 1)]
            });
        }
    }
    
    
    // Create the Data Store.
    // Because it is buffered, the automatic load will be directed
    // through the prefetch mechanism, and be read through the page cache
    var store = Ext.create('Ext.data.Store', {
        groupField: 'department',
        model: 'Employee'
    });
    
    
    var jumpToRow = function () {
        var fld = grid.down('#gotoLine');
        if (fld.isValid()) {
            grid.view.bufferedRenderer.scrollTo(fld.getValue() - 1, true);
        }
    };
    
    
    var data = [],
        perBatch = 1000,
        max = 5000;
    
    
    var grid = Ext.create('Ext.grid.Panel', {
        width: 700,
        height: 500,
        title: 'Buffered Grid of 5,000 random records',
        store: store,
        loadMask: true,
        plugins: [{
            ptype: 'bufferedrenderer',
            trailingBufferZone: 40,// Keep 40 rows rendered in the table behind scroll        
            leadingBufferZone: 80// Keep 80 rows rendered in the table ahead of scroll
        }],
        selModel: {
            pruneRemoved: false
        },
        viewConfig: {
            trackOver: false
        },
        features: [{
            ftype: 'groupingsummary',
            groupHeaderTpl: 'Department: {name}',
            showSummaryRow: false
        }],
        // grid columns
        columns: [{
            xtype: 'rownumberer',
            width: 40,
            sortable: false
        }, {
            text: 'Id',
            sortable: true,
            dataIndex: 'employeeNo',
            groupable: false,
            width: 70
        }, {
            text: 'Name',
            sortable: true,
            dataIndex: 'name',
            groupable: false,
            renderer: function (v, cellValues, rec) {
                return rec.get('forename') + ' ' + rec.get('surname');
            },
            width: 120
        }, {
            text: 'Date of birth',
            dataIndex: 'dob',
            xtype: 'datecolumn',
            groupable: false
        }, {
            text: 'Join date',
            dataIndex: 'joinDate',
            xtype: 'datecolumn',
            groupable: false
        }, {
            text: 'Notice period',
            dataIndex: 'noticePeriod',
            groupable: false
        }, {
            text: 'Email address',
            dataIndex: 'email',
            width: 200,
            groupable: false,
            renderer: function (v) {
                return '<a href="mailto:' + v + '">' + v + '</a>';
            }
        }, {
            text: 'Department',
            dataIndex: 'department',
            hidden: true,
            hideable: false,
            groupable: false
        }, {
            text: 'Absences',
            columns: [{
                text: 'Illness',
                dataIndex: 'sickDays',
                width: 60,
                groupable: false
            }, {
                text: 'Holidays',
                dataIndex: 'holidayDays',
                width: 70,
                groupable: false
            }, {
                text: 'Holday Allowance',
                dataIndex: 'holidayAllowance',
                width: 125,
                groupable: false
            }]
        }, {
            text: 'Rating',
            width: 70,
            sortable: true,
            dataIndex: 'rating',
            groupable: false
        }, {
            text: 'Salary',
            width: 110,
            sortable: true,
            dataIndex: 'salary',
            align: 'right',
            renderer: Ext.util.Format.usMoney,
            groupable: false
        }],
        bbar: [{
            labelWidth: 80,
            fieldLabel: 'Jump to row',
            xtype: 'numberfield',
            minValue: 1,
            maxValue: max,
            allowDecimals: false,
            itemId: 'gotoLine',
            enableKeyEvents: true,
            listeners: {
                specialkey: function (field, e) {
                    if (e.getKey() === e.ENTER) {
                        jumpToRow();
                    }
                }
            }
        }, {
            text: 'Go',
            handler: jumpToRow
        }],
        renderTo: Ext.getBody()
    });
    
    
    function makeData() {
        createFakeData(perBatch, data);
        if (data.length < max) {
            setTimeout(makeData, 10);
        } else {
            Ext.getBody().unmask();
            store.loadData(data);
        }
    }
    
    
    Ext.getBody().mask('Generating fake data...');
    
    
    // In old IE, the fake data loop can cause a slow script warning,
    // so kick this off in the "background" to load the data in chunks.
    makeData();
    Are you a Sencha products veteran who has wondered what it might be like to work at Sencha? If so, please reach out to our human resources manager: fabienne.bell@sencha.com

  6. #6
    Sencha User
    Join Date
    Dec 2011
    Posts
    242
    Vote Rating
    7
    ypandey will become famous soon enough

      0  

    Exclamation

    Thanks alot slemmon..
    I compared mine code with your given example and found these two things were missing in my grid config.
    Code:
    selModel: {        
    pruneRemoved: false
        },
        viewConfig: {
            trackOver: false // What is this config for?
        }
    So I added these configs.
    but still, sometimes it throws error on adding record to the store.
    otherwise scrollTo method works perfectly.
    Couldn't reproduce this issue if I remove my custom bufferzones.

    Error Stack Trace on Chrome
    • Uncaught TypeError: Cannot call method 'insertSibling' of null ext-all.js:18
    • Ext.cmd.derive.doAdd ext-all.js:18
    • Ext.cmd.derive.onAdd ext-all.js:18
    • b.implement.callParent ext-all.js:18
    • Ext.cmd.derive.onAdd ext-all.js:18
    • b.implement.callParent ext-all.js:18
    • Ext.define.onAdd ext-all.js:18
    • fire ext-all.js:18
    • continueFireEvent ext-all.js:18
    • fireEventArgs ext-all.js:18
    • a.fireEventArgs ext-all.js:18
    • fireEvent ext-all.js:18
    • Ext.cmd.derive.insert ext-all.js:18
    • Ext.cmd.derive.addSorted ext-all.js:18
    • Ext.cmd.derive.add

  7. #7
    Sencha - Support Team slemmon's Avatar
    Join Date
    Mar 2009
    Location
    Boise, ID
    Posts
    6,129
    Vote Rating
    228
    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

    I haven't been able to reproduce the issue myself. Do you have a test case that you could share that seems to reproduce the issue you're still seeing at times?
    Are you a Sencha products veteran who has wondered what it might be like to work at Sencha? If so, please reach out to our human resources manager: fabienne.bell@sencha.com

  8. #8
    Sencha User
    Join Date
    Dec 2011
    Posts
    242
    Vote Rating
    7
    ypandey will become famous soon enough

      0  

    Post

    I really appreciate your time on this issue.
    I am sharing some part of the code, hopefully Its would be
    enough to reproduce the problem.
    Code:
    Ext.define('MyApp.model.SomeModel', {
        extend: 'Ext.data.Model',
        fields: [{
            name: 'name',
            type: 'string'
        }, {
            name: 'description',
            type: 'string'
        }]
    });
    Code:
    Ext.define("MyApp.store.SomeStore", {
        extend: 'Ext.data.Store',
        model: 'MyApp.model.SomeModel',
        autoDestroy: true,
        pageSize: 75,
        autoLoad: false,
        sorters: [{
            property: 'name',
            direction: 'ASC'
        }],
       proxy: {
            type: 'ajax',
            url: 'data.json',// I use struts action to get 2000 records
            reader: {
                type: 'json',
                root: 'data'
            }
        }
    });
    Code:
    {
                        xtype: 'grid',
                        itemId: 'maingrid',
                        store: 'SomeStore',
                        loadMask: true,
                        plugins: {
                            ptype: 'bufferedrenderer',
                            trailingBufferZone: 40, // Keep 40 rows rendered in the table behind scroll
                            leadingBufferZone: 80 // Keep 80 rows rendered in the table ahead of scroll
                        },
                        viewConfig: {
                            emptyText: 'No Records',
                            deferEmptyText: true,
                            trackOver: false
                        },
                        autoScroll: true,
                        border: 0,
                        selModel: {
                            selType: 'rowmodel',
                            pruneRemoved: false,
                            mode: 'MULTI'
                        },
                        columnLines: true,
                        columns: [{
                            text: 'Name',
                            flex: 2,
                            minWidth: 120,
                            sortable: true,
                            dataIndex: 'name'
                        }, {
                            text: 'Description',
                            flex: 3,
                            minWidth: 120,
                            sortable: true,
                            dataIndex: 'description',
                            renderer: function(value, metaData, record,
                            rowIdx, colIdx, store) {
                                metaData.tdAttr = 'data-qtip="' + value + '"';
                                return value;
                            }
                        }]
    }
    Code:
    //Inside Controller,for Form Save Action It will add or update record in Grid
    addOrUpdateRecord: function(formPanel) {
        var name = formPanel.down('#ruleObjectName').getValue();
        var desc = formPanel.down('#description').getValue();
            var newRec = Ext.create(MyApp.model.SomeModel, {
                name: name,
                description: desc
            });
            mainStore = this.getMaingrid().getStore();//maingrid is present as ref
            oldRec = mainStore.findRecord('name', name);
            if (oldRec) {
                mainStore.remove(oldRec);
            }
            mainStore.add(newRec); //Sometimes throws Error: Cannot call method 'insertSibling' of null 
            //mainStore .loadData([newRec],true); // Always works fine
            var rowIndex = mainStore.indexOf(newRec);
            this.getMaingrid().view.bufferedRenderer.scrollTo(rowIndex, true);
        }

  9. #9
    Sencha - Support Team slemmon's Avatar
    Join Date
    Mar 2009
    Location
    Boise, ID
    Posts
    6,129
    Vote Rating
    228
    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

    I abstracted your code into an inline example with a few superficial edits and it seemed to test OK for me in 4.2.1 in Chrome using the inline test case below:

    Code:
    var generateData = function (count) {
        var totalRecords = [], i;
    
    
        for (i=0; i < count; i++) {
            totalRecords[i] = {
                name: 'name',
                description: 'description'
            };
        };
    
    
        return { data: totalRecords };
    };
    
    
    
    
    Ext.define('MyApp.model.SomeModel', {
        extend: 'Ext.data.Model',
        fields: [{
            name: 'name',
            type: 'string'
        }, {
            name: 'description',
            type: 'string'
        }, 'id']
    });
    
    
    Ext.define("MyApp.store.SomeStore", {
        extend: 'Ext.data.Store',
        model: 'MyApp.model.SomeModel',
        autoDestroy: true,
        pageSize: 75,
        autoLoad: false,
        sorters: [{
            property: 'name',
            direction: 'ASC'
        }],
        data: generateData(2000),
        proxy: {
            type: 'memory',
            //url: 'data.json',// I use struts action to get 2000 records
            //data: generateData(2000),
            reader: {
                type: 'json',
                root: 'data'
            }
        }
    });
    
    
    var someStore = Ext.create('MyApp.store.SomeStore', {
        storeId: 'SomeStore'
    });
    
    
    
    
    Ext.widget('viewport', {
        layout: 'fit',
        items: [{
            xtype: 'grid',
            itemId: 'maingrid',
            store: 'SomeStore',
            loadMask: true,
            tbar: [{
                text: 'Insert at 1000',
                handler: function (btn) {
                    var grid = btn.up('gridpanel'),
                        store = grid.getStore(),
                        inserted;
    
    
                    inserted = store.insert(1000, {
                        name: 'New Name',
                        description: 'New Description'
                    })[0];
    
    
                    grid.findPlugin('bufferedrenderer').scrollTo(store.indexOf(inserted), true);
                }
            }],
            plugins: {
                ptype: 'bufferedrenderer',
                trailingBufferZone: 40, // Keep 40 rows rendered in the table behind scroll
                leadingBufferZone: 80 // Keep 80 rows rendered in the table ahead of scroll
            },
            viewConfig: {
                emptyText: 'No Records',
                deferEmptyText: true,
                trackOver: false
            },
            autoScroll: true,
            border: 0,
            selModel: {
                selType: 'rowmodel',
                pruneRemoved: false,
                mode: 'MULTI'
            },
            columnLines: true,
            columns: [{
                text: 'Name',
                flex: 2,
                minWidth: 120,
                sortable: true,
                dataIndex: 'name'
            }, {
                text: 'Description',
                flex: 3,
                minWidth: 120,
                sortable: true,
                dataIndex: 'description',
                renderer: function (value, metaData, record,
                    rowIdx, colIdx, store) {
                    metaData.tdAttr = 'data-qtip="' + value + '"';
                    return value;
                }
            }]
        }]
    });
    Are you a Sencha products veteran who has wondered what it might be like to work at Sencha? If so, please reach out to our human resources manager: fabienne.bell@sencha.com

  10. #10
    Sencha User
    Join Date
    Dec 2011
    Posts
    242
    Vote Rating
    7
    ypandey will become famous soon enough

      0  

    Arrow

    I was able to reproduce this issue with minor change in your code.
    Instead of adding record at fixed position 1000, taking input from user.
    Steps To Reproduce:
    1. First Add entry at 1998 position
    2. Then Try to add at 2 position
    You will get.
    Uncaught TypeError: Cannot call method 'insertSibling' of null

    Code:
    var generateData = function (count) {
        var totalRecords = [], i;
        for (i=0; i < count; i++) {
            totalRecords[i] = {
                name: 'name',
                description: 'description'
            };
        };
    
    
    
    
        return { data: totalRecords };
    };
    
    
    
    
    
    
    
    
    Ext.define('MyApp.model.SomeModel', {
        extend: 'Ext.data.Model',
        fields: [{
            name: 'name',
            type: 'string'
        }, {
            name: 'description',
            type: 'string'
        }, 'id']
    });
    
    
    
    
    Ext.define("MyApp.store.SomeStore", {
        extend: 'Ext.data.Store',
        model: 'MyApp.model.SomeModel',
        autoDestroy: true,
        pageSize: 75,
        autoLoad: false,
        sorters: [{
            property: 'name',
            direction: 'ASC'
        }],
        data: generateData(2000),
        proxy: {
            type: 'memory',
            //url: 'data.json',// I use struts action to get 2000 records
            //data: generateData(2000),
            reader: {
                type: 'json',
                root: 'data'
            }
        }
    });
    
    
    
    
    var someStore = Ext.create('MyApp.store.SomeStore', {
        storeId: 'SomeStore'
    });
    
    
    
    
    
    
    
    
    Ext.widget('viewport', {
        layout: 'fit',
        items: [{
            xtype: 'grid',
            itemId: 'maingrid',
            store: 'SomeStore',
            loadMask: true,
            tbar: [{
                text: 'Insert at ',
                handler: function (btn) {
                    var grid = btn.up('gridpanel'),
                        store = grid.getStore(),
                        inserted;
    
    
    
    
                    inserted = store.insert( btn.up('gridpanel').down('#Position').getValue(), {
                        name: 'New Name',
                        description: 'New Description'
                    })[0];
    
    
    
    
                    grid.findPlugin('bufferedrenderer').scrollTo(store.indexOf(inserted), true);
                }
            },{
                xtype: 'numberfield',
                itemId: 'Position',
                minValue: 1,
                value: 1
            }],
            plugins: {
                ptype: 'bufferedrenderer',
                trailingBufferZone: 40, // Keep 40 rows rendered in the table behind scroll
                leadingBufferZone: 80 // Keep 80 rows rendered in the table ahead of scroll
            },
            viewConfig: {
                emptyText: 'No Records',
                deferEmptyText: true,
                trackOver: false
            },
            autoScroll: true,
            border: 0,
            selModel: {
                selType: 'rowmodel',
                pruneRemoved: false,
                mode: 'MULTI'
            },
            columnLines: true,
            columns: [{
                text: 'Name',
                flex: 2,
                minWidth: 120,
                sortable: true,
                dataIndex: 'name'
            }, {
                text: 'Description',
                flex: 3,
                minWidth: 120,
                sortable: true,
                dataIndex: 'description',
                renderer: function (value, metaData, record,
                    rowIdx, colIdx, store) {
                    metaData.tdAttr = 'data-qtip="' + value + '"';
                    return value;
                }
            }]
        }]
    });