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.