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

3
  • 1
    Sad news for you: angular.io/guide/pipes#no-filter-pipe Commented Apr 4, 2018 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. Commented Apr 4, 2018 at 21:50
  • Possible duplicate of Angular 2 OrderBy Pipe Commented Nov 17, 2018 at 16:56

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks a lot, do you mean that it wuold be better if the server orders data instead the client?
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.
Thanks a a lot J.P. The last question do you know if is possible to run this pipe sorting using the code bihind?
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: angular.io/guide/dependency-injection-in-action

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.