Search code examples
angularpipeangular-pipe

Angular4 - sorting data with pipe


I have to order a list with Angular4x, and I can't find an easy example to understand how it works. It seems that with AngularJs it was even more easier, I would like to do something like that.

ng-repeat="x in customers | orderBy : 'city'"

By the way, if you can help me I would really appreciate.

Thanks Andrea


Solution

  • I happen to have one lying around. But please be careful with this, as Ali Sajid also pointed out, these pipes are not part of the default Angular implementation for good reasons. So know that you shouldn't use this pipe for huge lists/tables or if you want to aggressively minify your code.

    That having been said, here's the pipe that I use:

    import { Pipe, PipeTransform } from '@angular/core';
    
    @Pipe({ name: 'orderBy' })
    export class OrderByPipe implements PipeTransform {
        static compare(reverse: boolean, a: any, b: any): number {
            if (a < b && reverse === false) {
                return -1;
            }
            if (a > b && reverse === false) {
                return 1;
            }
            if (a < b && reverse === true) {
                return 1;
            }
            if (a > b && reverse === true) {
                return -1;
            }
            return 0;
        }
    
        transform(input: any[], config?: string | string[]): any {
            if(!input) {
                return input;
            }
    
            if (config === '+' || config === '-') {
                return config === '+' ? input.sort() : input.sort().reverse();
            }
    
            if (Array.isArray(config) === false) {
                config = <string[]>[config];
            }
    
            // As soon as a or b is smaller/greater than the other, we can immediately return
            return input.sort((a: any, b: any): number => {
                for (let fullProp of config) {
                    let reverse = fullProp[0] === '-';
                    let prop = fullProp.substr(1);
    
                    // Is it a subobject?
                    if (prop.indexOf('.') > 0) {
                        let first = prop.split('.')[0];
                        let last = prop.split('.')[1];
    
                        let result = OrderByPipe.compare(reverse, a[first][last], b[first][last]);
                        if (result !== 0) {
                            return result;
                        }
    
                        continue;
                    }
    
                    let result = OrderByPipe.compare(reverse, a[prop], b[prop]);
                    if (result !== 0) {
                        return result;
                    }
                };
    
                return 0;
            });
        }
    }
    

    The tests for this pipe:

    import { OrderByPipe } from './order-by.pipe';
    
    describe('Pipe: OrderBy', () => {
        let orderBy: OrderByPipe;
    
        beforeEach(() => {
            orderBy = new OrderByPipe();
        });
    
        it('should sort an array in ascending order', () => {
            let data = [5, 3, 1, 2, 4];
            let result = [1, 2, 3, 4, 5];
            expect(orderBy.transform(data, '+')).toEqual(result);
        });
    
        it('should sort an array in descending order', () => {
            let data = [5, 3, 1, 2, 4];
            let result = [5, 4, 3, 2, 1];
            expect(orderBy.transform(data, '-')).toEqual(result);
        });
    
        it('should sort an array in ascending order based on a property', () => {
            let data = [{ q: 1 }, { q: 8 }, { q: 5 }];
            let result = [{ q: 1 }, { q: 5 }, { q: 8 }];
            expect(orderBy.transform(data, '+q')).toEqual(result);
        });
    
        it('should sort an array in descending order based on a property', () => {
            let data = [{ q: 1 }, { q: 8 }, { q: 5 }];
            let result = [{ q: 8 }, { q: 5 }, { q: 1 }];
            expect(orderBy.transform(data, '-q')).toEqual(result);
        });
    
        it('should sort an array based on multiple properties', () => {
            let data = [{ d: 'yada' }, { d: 'something', n: 8 }, { d: 'something', n: 4 }];
            let result = [{ d: 'something', n: 4 }, { d: 'something', n: 8 }, { d: 'yada' }];
            expect(orderBy.transform(data, ['+d', '+n'])).toEqual(result);
        });
    
        it('should sort an array based on a nested object', () => {
            let data = [
                { d: 'something', q: { n: 8 } },
                { d: 'yada', q: { n: 3 } },
                { d: 'something', q: { n: 4 } }
            ];
            let result = [
                { d: 'yada', q: { n: 3 } },
                { d: 'something', q: { n: 4 } },
                { d: 'something', q: { n: 8 } }
            ];
            expect(orderBy.transform(data, '+q.n')).toEqual(result);
        });
    
        it('should handle empty values gracefully', () => {
            expect(orderBy.transform(undefined)).toBe(undefined);
        });
    });
    

    And the usage:

    <li *ngFor="let item of data | orderBy: ['-date']">
    

    A + or - in the front of the property name indicates ascending/descending.