Im working with angular 5 and angular material2, im trying to filter a list of element in the *ngFor
like you can see here:
<div class="book" *ngFor="let book of documents |
docCategory: fitleredCategories | sortBy: sortvalue : asc">
now the second filter is working but first one is not.
pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'docCategory'
})
export class DocCategoryPipe implements PipeTransform {
transform(value: any, args?: any): any {
let filtered = [];
if (!value) {
return;
}
filtered = value.filter((doc) => args.includes(doc.categories[0]));
if (args.length === 0) {
return value;
} else {
return filtered;
}
}
}
Here is a group of checkbox that on click push the value into fitleredCategories
array:
library.component.html
<li class="category" *ngFor="let category of categories">
<mat-checkbox value="{{category.name}}" (click)="toggleCatInArray(category.name)">{{category.name}}</mat-checkbox>
</li>
the toggleCatInArray
only check if the value exist or not on the fitleredCategories to push it or remove it.
but for some reason the pipe is not working.
library.component.ts
public fitleredCategories: any = [];
public toggleCatInArray(category): void {
this.toggleInArray(category, this.fitleredCategories);
}
i don´t receive any error on the console or something, it just doesn´t filter the *ngFor
The problem is caused by 2 reasons:
Bad implementation of the DocCategoryPipe:
@Pipe({
name: 'docCategory'
})
export class DocCategoryPipe implements PipeTransform {
transform(value: any[], categories: string[]): any[] {
if (!value || !categories) {
return [];
}
return value.filter((doc) => args.includes(doc.categories[0]));
}
}
Mutation of the filteredCategories array:
In order to re run the pipe in the template, new values/object references need to be passed as arguments. Angular wont re evaluate a pure pipe when you mutate one of its arguments.
To solve this, refactor your code as follows:
<li class="category" *ngFor="let category of categories">
<mat-checkbox [value]="category.name" (change)="toggleCategory($event)">{{category.name}}</mat-checkbox>
</li>
import {MatCheckboxChange} from '@angular/material';
filteredCategories: string[] = [];
toggleCategory(event: MatCheckboxChange){
const category = event.source.value;
if(event.checked){
this.filteredCategories= [...this.filteredCategories, category];
}else{
const matchIndex = this.filteredCategories.indexOf(category);
this.filteredCategories= this.fitleredCategories.splice(matchIndex,1).
}
}
As you are now setting filteredCategories
with a new object reference every time that you add/remove categories, angular will re evaluate the pipeline in your template.