Search code examples
angulartypescriptangular-ng-if

Filter data before displaying in the component in angular 5


I have a list of football data coming from an api.

enter image description here

All the available list are shown when I request this api endpoint. I am trying to separate the list based on the status of the game. For eg. Finished, Postponed, Timed.

I tried something like this

<div *ngFor="let fixture of fixtures">
    <div *ngIf="fixture.status !== 'FINISHED'">
        <p>{{fixture.date}}</p>
    </div>
</div>

It gets filtered and doesn't show on the view but it isn't filtered completely. Now the problem is that, since the list is too long, I want to show only first 20 items and I am doing as below:

<div *ngFor="let fixture of fixtures | slice:0:20">
    <div *ngIf="fixture.status !== 'FINISHED'">
        <p>{{fixture.date}}</p>
    </div>
</div>

As it should show up only the first 20 items from the list of filtered items but it didn't. It doesn't show any list because the list is still there that isn't filtered.

Now I think the way I am filtering at the moment isn't the right way. Since it gets filtered only in the view but not in list coming from the api, when I try to slice the list, it doesn't work like this.

Please let me know if you people can help me achieve this. Thanks in advance.


Solution

  • Do not use the slice pipe like that, it won't work well in conjunction with your *ngIf; it knows nothing about the filtered result set, only the original list.

    Before I give the solution, take a look at the source code of the slice pipe to see how it works internally on your list:

    @Pipe({name: 'slice', pure: false})
    export class SlicePipe implements PipeTransform {
      transform(value: any, start: number, end?: number): any {
        if (value == null) return value;
    
        if (!this.supports(value)) {
          throw invalidPipeArgumentError(SlicePipe, value);
        }
    
        return value.slice(start, end);
      }
    
      private supports(obj: any): boolean { return typeof obj === 'string' || Array.isArray(obj); }
    }
    

    As you see, it takes in an original list (which would be the filtered result set in this case), and THEN it slices it.

    Source: Angular.io


    To solve your problem, filter in your component, and loop through the filtered results in your template. Then and only then should you apply the slice pipe.

    This is more efficient (because you aren't looping needlessly in the component), and allows you to run more operations on the filtered lists since you have them cached.

    Component:

    @Component({...})
    export class MyComponent {
        list: any[] = [...];
        fixtures: any[] = this.list.filter(item => item.status !== 'FINISHED');
    }
    

    Template:

    <div *ngFor="let fixture of fixtures | slice:0:20">
        <p>{{fixture.date}}</p>
    </div>
    

    Update:

    To fix your problem with searching, search against the original list, not the filtered list.