I am using *ngFor
to display a collection, I also have one input
which will be used as a filter criteria (this input
is a formControl
). Now I have added a PipeTransform
to filter my collection based on that input
.
Everything works as expected but I can't figure out how to debounce. I want my filterPipe
to be called 0.5s after last keystroke inside input
but at the moment it is called instantly after any change inside my input
.
HTML:
<div class="test-ctr">
<div class="form-ctr">
<form class="example-form" [formGroup]="filterForm">>
<input formControlName="nick" placeholder="ex. hdw">
</form>
</div>
<div class="app-cards-ctr">
<div class="app-card-ctr" *ngFor="let nick of nicks | filterPipe: filterForm.get('nick')?.value">
<app-card [nick]="nick"></app-card>
</div>
</div>
</div>
Component.ts:
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
})
export class TestComponent implements OnInit {
nicks: string[] = ['Siema', 'Elo', 'Tu', 'Hdw3DCtV', 'and', 'Gównow', 'Zdzisiu11', 'Zdzisiu1','Zdzisiu2','Zdzisiu3','Zdzisiu4','Zdzisiu5'];
constructor(private formBuilder: FormBuilder) {}
filterForm = this.formBuilder.group({
nick: ['']
})
ngOnInit(): void {}
}
Pipe.ts:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'filterPipe', pure: true })
export class FilterPipe implements PipeTransform {
transform(value: string[], arg: string): string[] {
console.log(arg);
return value.filter(
(e) => e.toLowerCase().indexOf(arg.toLowerCase()) !== -1
);
}
}
CodePudding user response:
HTML:
<h3>Filter:</h3>
<form [formGroup]="filterForm">
<input type="text" formControlName="nick" />
</form>
<h3>Nicks:</h3>
<div *ngFor="let nick of nicks | filterPipe: (debouncedControl$ | async)">
{{ nick }}
</div>
Component:
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
nicks: string[] = [
'Siema',
'Elo',
'Tu',
'Hdw3DCtV',
'and',
'Gównow',
'Zdzisiu11',
'Zdzisiu1',
'Zdzisiu2',
'Zdzisiu3',
'Zdzisiu4',
'Zdzisiu5',
];
constructor(private formBuilder: FormBuilder) {}
filterForm = this.formBuilder.group({
nick: [''],
});
debouncedControl$ = this.filterForm.controls.nick.valueChanges.pipe(
startWith(''),
debounceTime(500)
);
}
Pipe:
@Pipe({
name: 'filterPipe',
})
export class FilterPipe implements PipeTransform {
transform(values: string[], inputValue: string): string[] {
return values.filter((v) => {
return (
v.toLowerCase().indexOf((inputValue || '').toLocaleLowerCase()) > -1
);
});
}
}
All that said, this is probably not a scenario where I would reach for an angular pipe.
Alternate component solution:
@Component({
selector: 'alternate',
template: `
<form [formGroup]="filterForm">
<input type="text" formControlName="nick" />
</form>
<ul>
<li *ngFor="let nick of filteredNicks$ | async"> {{nick}} </li>
</ul>
`,
})
export class AlternateSolutionComponent {
constructor(private formBuilder: FormBuilder) {}
nicks = ['Siema', 'Elo', 'Tu'];
lowercaseIncludes(a: string, b: string): boolean {
return a.toLocaleLowerCase().indexOf(b.toLocaleLowerCase()) > -1;
}
filterForm = this.formBuilder.group({ nick: [''] });
filteredNicks$ = this.filterForm.controls.nick.valueChanges.pipe(
startWith(''),
debounceTime(500),
map((inputValue) => {
return this.nicks.filter((nick) =>
this.lowercaseIncludes(nick, inputValue)
);
})
);
}
CodePudding user response:
For me, best approach is to use the pipe inside the component and not in the template.
constructor(
private filterPipe: FilterPipe
) {}
ngOnInit() {
this.filterForm.controls.nick.valueChanges.pipe(
startWith(''),
debounceTime(500),
map(value => {
this.nicks.forEach(nick => {
nick = this.filterPipe.transform(value, nick); // or what you want to do
});
})
);
}