How to filter items inside “ngFor” loop, based on object property string

39,749

Here is a sample pipe:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'matchesCategory'
})
export class MathcesCategoryPipe implements PipeTransform {
    transform(items: Array<any>, category: string): Array<any> {
        return items.filter(item => item.category === category);
    }
}

To use it:

<li *ngFor="let model; of models | matchesCategory:model.category" (click)="gotoDetail(model)">

===== for the plunkr example ====

You need your select changes to reflect in some variable

First define in your class a member:

selectedCategory: string;

then update your template:

<select (change)="selectedCategory = $event.target.value">
   <option *ngFor="let model of models ">{{model.category}}</option>
</select>

last, use the pipe:

  <li *ngFor="let model; of models | matchesCategory:selectedCategory" (click)="gotoDetail(model)">

==== comments after seeing the plunker ====

I noticed you used promise. Angular2 is more rxjs oriented. So the first thing I'd change is in your service, replace:

getModels(): Promise<Model[]> {
  return Promise.resolve(MODELS);
}

to:

getModels(): Observable<Array<Model>> {
  return Promise.resolve(MODELS);
}

and

getModels(id: number): Observable<Model> {
  return getModels().map(models => models.find(model.id === id);
}

then in your ModelsComponent

models$: Observable<Array<Model>> = svc.getModels();
uniqueCategories$: Observable<Array<Model>> = this.models$
  .map(models => models.map(model => model.category)
  .map(categories => Array.from(new Set(categories)));

Your options will become:

     <option *ngFor="let category; of uniqueCategories$ | async">{{model.category}}</option>

and your list:

      <li *ngFor="let model; of models$ | async | matchesCategory:selectedCategory" (click)="gotoDetail(model)">

This is a very drafty solution since you have many duplicates and you keep querying the service. Take this as a starting point and query the service only once, then derive specific values from the result you got.

If you'd like to keep you code, just implement a UniqueValuesPipe, its transform will get a single parameter and filter it to return unique categories using the Array.from(new Set(...)). You will need though to map it to strings (categories) first.

Share:
39,749
gundra
Author by

gundra

Updated on July 09, 2022

Comments

  • gundra
    gundra almost 2 years

    I need to filter items inside an ngFor loop, by changing the category in a drop-down list. Therefore, when a particular category is selected from the list, it should only list the items containing that same category.

    HTML Template:

    <select>
      <option *ngFor="let model of models">{{model.category}}</option>
    </select>
    
    <ul class="models">
      <li *ngFor="let model of models" (click)="gotoDetail(model)">
      <img [src]="model.image"/>
      {{model.name}},{{model.category}}
      </li>
    </ul>
    

    Items Array:

    export var MODELS: Model[] = [
    { id: 1, 
      name: 'Model 1', 
      image: 'img1', 
      category: 'Cat1', 
    },
    
    { id: 2, 
      name: 'Model 2', 
      image: 'img2', 
      category: 'Cat3',
    },
    
    { id: 3, 
      name: 'Model 3', 
      image: 'img3', 
      category: 'Cat1',
    },
    { id: 4, 
      name: 'Model 4', 
      image: 'img4', 
      category: 'Cat4',
    },
    
    ...
    ];
    

    Also, the drop-down list contains repeated category names. It is necessary for it to list only unique categories (strings).

    I know that creating a custom pipe would be the right way to do this, but I don't know how to write one.

    Plunker: http://plnkr.co/edit/tpl:2GZg5pLaPWKrsD2JRted?p=preview

  • gundra
    gundra over 7 years
    Thanks Meir, i'll try to implement this code into my project, to see if it gives result. I will get back to you... thx
  • gundra
    gundra over 7 years
    Meir, when i tried to filter items by using this code, console throws me an error "Cannot read property 'category' of undefined"
  • Meir
    Meir over 7 years
    add a break point (or console.log(items, category) at the top of the function and see it gets the values right
  • Meir
    Meir over 7 years
    and if this doesn't work, put your full implementation on plnkr so I can have a look
  • Meir
    Meir over 7 years
    Found the problem, your select is not bound, i.e. changes do not update any value. Will edit the answer
  • gundra
    gundra over 7 years
    Great job Meir, it works! I marked answer as correct, still i need one last thing. Is it possible to have only unique categories in dropdown list, not to get same names repeated?
  • gundra
    gundra over 7 years
    Thanks Meir, i will try to do with your code. Regards
  • Meir
    Meir about 7 years
    thx. I don't think I posted a plunker. I commented on the original one. Can you please refer me to the link?
  • Maxim Mazurok
    Maxim Mazurok over 6 years
    you've got a typo in MathcesCategoryPipe (Mathces)