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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With