0

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

R. Richards
  • 24,603
  • 10
  • 64
  • 64
Gelso77
  • 1,763
  • 6
  • 30
  • 47
  • 1
    Sad news for you: https://angular.io/guide/pipes#no-filter-pipe – Ali Sajid Apr 04 '18 at 21:48
  • Create your own pipe to filter, or create a directive to filter. They should take in a reference list (the original list), and either modify it or give a result of a new list with the filtered result. A function definition can be something like `filter(originalList: any[], options: any): any[];`. Try it out and come back if you have any problems. – Lansana Camara Apr 04 '18 at 21:50
  • Possible duplicate of [Angular 2 OrderBy Pipe](https://stackoverflow.com/questions/35158817/angular-2-orderby-pipe) – Random Nov 17 '18 at 16:56

1 Answers1

2

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.

J.P.
  • 5,567
  • 3
  • 25
  • 39
  • Thanks a lot, do you mean that it wuold be better if the server orders data instead the client? – Gelso77 Apr 04 '18 at 22:11
  • Depends on the amount of data that needs to be sorted. A server-side database (SQL Server, MySQL, ...) might indeed be the more approriate solution if your dataset contains millions of rows. If it's within thousands, most clients these days are able to process that, specially if you place that logic inside your component so that it doesn't get executed as often as a pipe does. It's a judgement call. – J.P. Apr 04 '18 at 22:14
  • Thanks a a lot J.P. The last question do you know if is possible to run this pipe sorting using the code bihind? – Gelso77 Apr 04 '18 at 22:36
  • Code-behind as in server-side code? No. This code lives in the browser and can't easily and directly be called from the server. If you mean code-behind as in from an Angular component, then sort of. Angular has a built-in dependency injection mechanism. Change this pipe into an injectable service that you can inject and use inside your components. See also: https://angular.io/guide/dependency-injection-in-action – J.P. Apr 04 '18 at 22:57