6

I recently updated version of EXT JS to 5 and and override of doSort function no longer works. Someone an idea how to do ?

Exampple of override :

{
    text: 'Custom',
    sortable : true,
    dataIndex: 'customsort',
    doSort: function(state) {
        var ds = this.up('grid').getStore();
        var field = this.getSortParam();
        ds.sort({
            property: field,
            direction: state,
            sorterFn: function(v1, v2){
                v1 = v1.get(field);
                v2 = v2.get(field);

                return v1.length > v2.length ? 1 : (v1.length < v2.length ? -1 : 0);
            }
        });
    }
}

Edit 1 : I just try the solution of @tomgranerod but the me.sortState is always 'undefined'. So I do this to update my variable :

    sort: function () {
        var me = this,
            grid = me.up('tablepanel'),
            store = grid.store;

        me.sortState = me.sortState === 'ASC' ? 'DESC' : 'ASC';

        Ext.suspendLayouts();
        me.sorting = true;
        store.sort({
            property: me.getSortParam(),
            direction: me.sortState,
            sortFn: function (v1, v2) {
                v1 = v1.get(field);
                v2 = v2.get(field);

                return v1.length > v2.length ? 1 : (v1.length < v2.length ? -1 : 0);
            }
        });
        delete me.sorting;
        Ext.resumeLayouts(true);
    }

But the sortFn funcion is never called. I don't know why. ===> !!!! it works with EXT JS 5.0.1 but the sortFin function is always never called. !!!!

Edit 2 : This is what i attempt to have :

ASC :

if (v1 and v2 are numbers) return v1 > v2;
else if (v1 is a number and v2 a string) return false;
else if (v1 is a string and v2 a number) return true;
else if (v1 and v2 are strings) return v1 > v2;

DESC :

if (v1 and v2 are numbers) return v1 < v2;
else if (v1 is a number and v2 a string) return true;
else if (v1 is a string and v2 a number) return false;
else if (v1 and v2 are strings) return v1 < v2;
Mickaël P
  • 365
  • 1
  • 4
  • 13
  • You should not override `doSort`, it's private. You should expect it to break when you upgrade. Why don't you just provide a sorter to the grid? See http://docs-origin.sencha.com/extjs/4.2.2/#!/api/Ext.data.Field-cfg-sortType or http://docs-origin.sencha.com/extjs/4.2.2/#!/example/grid/multiple-sorting.html – Ruan Mendes Aug 08 '14 at 12:40
  • If you post a running (broken) example at https://fiddle.sencha.com/#home, I can take a look – Ruan Mendes Aug 08 '14 at 12:45
  • Thanks for your help Juan. I try to set an example to sencha fiddle. – Mickaël P Aug 08 '14 at 12:56
  • This is the fiddle : https://fiddle.sencha.com/#fiddle/8kk . I try to sort reference to have this order => ASC : 1, 12, 103, T01, T02 and DESC : T02, T01, 103, 12, 1. But if you test the sort doesn't do that. So I have to override the sort. – Mickaël P Aug 08 '14 at 13:10
  • The correct way is to provide a sorter for your `Ext.data.Field` I will take a look as soon as I can, but may not be for a few hours. By the way, very nice simplification of the problem on the fiddle, makes it really easy to look at – Ruan Mendes Aug 08 '14 at 13:25
  • Is the pattern for sorting the length of the string? – Ruan Mendes Aug 08 '14 at 13:44

4 Answers4

6

You were overriding a private method. So it's almost expected that it would break after a major release. If you look at http://docs.sencha.com/extjs/5.0.0/apidocs/source/Column2.html#Ext-grid-column-Column You'll see that there's no doSort function anymore.

Ext's suggested way is by to use sortType config can take a function which converts your value into something that sorts naturally, usually the easiest thing is to convert it into a number. So if you want something slightly different, you can modify the code I've posted to do what you want without overriding private methods.

Running Example: https://fiddle.sencha.com/#fiddle/8km

var store = Ext.create('Ext.data.Store', {
    fields: [{
        name: 'ref',
        sortType: function(str)  {
            // Ext-JS requires that you return a naturally sortable value
            // not your typical comparator function.
            // The following code puts all valid integers in the range 
            // Number.MIN_SAFE_INTEGER and 0
            // And assumes the other values start with T and sorts 
            // them as positive integers               
            var parsed = parseInt(str, 10); 
            if ( isNaN( parsed ) ){
                return parseInt(str.substring(1), 10);
            } else {
                return Number.MIN_SAFE_INTEGER + parsed;
            }          
        }
    }],
    data: {
        'items': [
            {'ref': '1'},
            {'ref': '12'},
            {'ref': 'T0134'},
            {'ref': '121878'},
            {'ref': 'T0134343'},
            {'ref': 'T01POPI'},
            {'ref': '103'},
            {'ref': 'T01'}            
        ]
    },
    proxy: {
        type: 'memory',
        reader: {
            type: 'json',
            rootProperty: 'items'
        }
    }
});

Ext.create('Ext.grid.Panel', {
    title: 'Grid custom',
    store: store,
    columns: [{
        text: 'Reference',
        dataIndex: 'ref',
    }],
    height: 300,
    width: 400,
    renderTo: Ext.getBody()
});

If you're going to be reusing this functionality, take a look at http://spin.atomicobject.com/2012/07/20/simple-natural-sorting-in-extjs/

/** Sort on string length  */
Ext.apply(Ext.data.SortTypes, {
    myCrazySorter: function (str) {
        // Same as above
    }
});

// And use it like
var store = Ext.create('Ext.data.Store', {
    fields: [{
        name: 'ref',
        sortType: 'myCrazySorter'
    }],
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • It works but is not my pattern. I edit my post to write my pattern. In my first post, it is an example. sorry for the ambiguity. – Mickaël P Aug 08 '14 at 14:08
  • 2
    @MickaëlP I showed you the correct way to implement custom sorting with an example, and I implemented it as your code showed. Did you try implementing the algorithm you've described? That seems to be outside of the scope of the question – Ruan Mendes Aug 08 '14 at 14:41
  • @JuandMendes I see your example but it is not really what I attempt to do. I describe my pattern in the second edit of my post. I don't know how to implement my sort with your way ? – Mickaël P Aug 08 '14 at 14:46
  • @MickaëlP I've fixed it to work with the set you gave me. Ext doesn't give you a comparator function. You have to map your values to something naturally comparable. This should be plenty for you to figure out your problem even if this is not exactly what you're looking for. – Ruan Mendes Aug 08 '14 at 17:11
  • I tried this solution and it works as long as my references begin with a letter or are numbers. But if I want to implement a comparator it's not possible. I don't know why the ExtJS frameworks prevents the developper to do. – Mickaël P Aug 11 '14 at 07:59
  • 1
    @MickaëlP I've filed a complaint with them once, it doesn't seem like they're going to change this. You can use a comparator on the store if you'd like. http://docs.sencha.com/extjs/5.0.0/apidocs/#!/api/Ext.data.AbstractStore-cfg-sorters It's not that simple to use if you expect it to work for multilevel sorting and with multiple columns since you can only give it comparator for the whole store, not one that is specific to a column – Ruan Mendes Aug 11 '14 at 12:49
1

The equivalent function to doSort in ExtJS 5, seem to be 'sort', after a quick look at the source code of Ext.grid.column.Column. The sortState parameter that I've used in this example seem to have been introduced in ExtJS 5.0.1.

sort: function () {
            var me = this,
                grid = me.up('tablepanel'),
                direction,
                store = grid.store;

            direction = me.sortState === 'ASC' ? 'DESC' : 'ASC';

            Ext.suspendLayouts();
            me.sorting = true;
            store.sort({
                sorterFn: function (v1, v2) {
                    v1 = v1.get(me.getSortParam());
                    v2 = v2.get(me.getSortParam());

                    return v1.length > v2.length ? 1 : (v1.length < v2.length ? -1 : 0);
                },
                direction: direction
            });
            delete me.sorting;
            Ext.resumeLayouts(true);
        }

However the solution Juan Mendes describe is a much safer and viable solution than overriding the internal sort function.

tomgranerod
  • 106
  • 4
  • @MickaëlP Please don't add a comment as an answer, either comment on the answer, or update your question – Ruan Mendes Aug 08 '14 at 12:40
  • 1
    Yes but i try to delete it but i did not succeed :( sorry. – Mickaël P Aug 08 '14 at 12:49
  • Hmm, this works fine for me. Although I tested this on v5.0.1 (just released yesterday), so there might be some differences there. – tomgranerod Aug 08 '14 at 13:08
  • I use the 5.0.0 version. Is it possible that the 5.0.1 works better for my problem ? – Mickaël P Aug 08 '14 at 13:16
  • @tomgranerod you can see the problem here : https://fiddle.sencha.com/#fiddle/8kk – Mickaël P Aug 08 '14 at 13:28
  • @MickaëlP Changing the framework to 5.0.1.1255 in that Fiddle, seem to solve the issue, no? – tomgranerod Aug 08 '14 at 13:39
  • @tomgranerod If it does, the OP should still not override a private function – Ruan Mendes Aug 08 '14 at 13:42
  • @tomgranerod Also, it's preferrable to explain what you did, instead of just posting code. Makes it a lot easier for others to learn – Ruan Mendes Aug 08 '14 at 13:55
  • @JuanMendes I've updated my answer - I'm kind of new to answering questions here and will try to take my time to explain in the future :-) – tomgranerod Aug 08 '14 at 14:09
  • @tomgranerod yes the direction is updated in 5.0.1. But it still not pass in the doSort function. I have updated my fiddle with my pattern. – Mickaël P Aug 08 '14 at 14:42
  • I prefered this method due to the complexity of my Object. I chose to update sort state right before resuming Layouts. I'm new to SO and im not sure if i should edit the post or what to do ? me.sortState = direction – Akin Okegbile Jan 21 '16 at 21:51
0

I finally find a way to compare two records during the sort :

1) Define a custom Column :

Ext.define('Eloi.grid.column.ReferenceColumn', {
    extend : 'Ext.grid.column.Column',

    alias  : 'widget.referencecolumn',

    config : {
        ascSorter  : null,
        descSorter : null
    },

    destroy : function() {
        delete this.ascSorter;
        delete this.descSorter;

        this.callParent();
    },

    /**
     * @param {String} state The direction of the sort, `ASC` or `DESC`
     */
    sort : function(state) {
        var me         = this,
            tablePanel = me.up('tablepanel'),
            store      = tablePanel.store,
            sorter     = this[state === 'ASC' ? 'getAscSorter' : 'getDescSorter']();

        Ext.suspendLayouts();
        this.sorting = true;

        store.sort(sorter, state, 'replace');

        delete this.sorting;
        Ext.resumeLayouts(true);
    },

    getAscSorter : function() {
        var sorter = this.ascSorter;

        if (!sorter) {
            sorter = new Ext.util.Sorter({
                sorterFn  : this.createSorter('ASC'),
                direction : 'ASC'
            });

            this.setAscSorter(sorter);
        }

        return sorter;
    },

    getDescSorter : function() {
        var sorter = this.ascSorter;

        if (!sorter) {
            sorter = new Ext.util.Sorter({
                sorterFn  : this.createSorter('DESC'),
                direction : 'DESC'
            });

            this.setAscSorter(sorter);
        }

        return sorter;
    },

    createSorter : function(state) {
        var dataIndex = this.dataIndex;

        return function(rec1, rec2) {
            var v1   = rec1.get(dataIndex),
                v2   = rec2.get(dataIndex),
                num1 = parseInt(v1),
                num2 = parseInt(v2),
                ret;

            if (num1 && num2) {
                ret = num1 > num2 ? 1 : (num1 < num2 ? -1 : 0);
            } else {
                if (!num1 && !num2) {
                    num1 = parseInt(v1.substr(1));
                    num2 = parseInt(v2.substr(1));

                    ret = num1 > num2 ? 1 : (num1 < num2 ? -1 : 0);
                }
                if (!num1) {
                    ret = 1;
                }
                if (!num2) {
                    ret = -1;
                }
            }

            if (state === 'DESC') {
                ret = ret * -1;
            }

            return ret;
        };
    }
});

2) set the new type to the column in your grid (with alias) and don't forget to set properly the requires config :

columns  : [
        {
            xtype     : 'referencecolumn',
            text      : 'Reference',
            dataIndex : 'ref',
            flex      : 1
        }
    ]
Mickaël P
  • 365
  • 1
  • 4
  • 13
  • 1
    Will this still work if you use multi-column sorting? Also, you should use `isNaN` on the result of `parseInt` since just a boolean check will fail for `parseInt("0")`. Lastly, it is recommended that you specify the second parameter, radix. The documentation says **Always specify this parameter** to eliminate reader confusion and to guarantee predictable behavior. Different implementations produce different results when a radix is not specified See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt – Ruan Mendes Aug 13 '14 at 15:05
  • 1
    One case of confusing behavior/different results is `parseInt("077")` Some browsers (IE) will treat it as an octal because you're starting it with a `0` and return 63. Some browsers (Chrome) will assume a radix of 10 and return `77` – Ruan Mendes Aug 13 '14 at 15:11
  • 1
    Also, keep in mind that you're overriding a private method, so you're almost asking for your implementation to break in future versions :) My suggestion of applying a custom sorter to the store is a supported API. Try that before touching Ext's privates :p – Ruan Mendes Aug 13 '14 at 15:14
0

You need to use

sorterFn not sortFn

Grey2k
  • 464
  • 1
  • 7
  • 12