I have a function that is implemented and works properly I want to make it rxjs with filter in rxjs directory use both pipe and includes
And update with subscribe
posts: Post[] = [];
validPosts: Post[] = [];
authorName: string = "";
this is my function:
SearchPostsByName($event: Event) {
const searchKey = ($event.target as HTMLInputElement).value;
if (searchKey === '') {
this.validPosts = this.posts;
return;
}
this.validPosts = this.posts.filter(
value => value.authorName.includes(this.searchName)
);
}
CodePudding user response:
To build a search using an input field, you bind the input element to a @ViewChild
.
<label>Search Posts </label>
<div>
<input #search type="text" [value]="filterInput" />
</div>
export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('search', { static: true }) search: ElementRef;
private subscription: Subscription;
public posts$: Observable<Post[]>;
public filterInput: string = '';
public errorMessage: string = '';
constructor(
private router: Router,
private route: ActivatedRoute,
private appService: AppService
) {}
...
}
In the AfterViewInit
, you add an observable to the keyup
event of the element. Through a pipe
, you map
the event to its value. You add a debounceTime
so the observable doesn't emit too often, and you only listen to changes trough distinctUntilChanged
.
Within the subscription itself, you check the input value and change the url queryParams
based upon the input.
ngAfterViewInit() {
this.subscription = fromEvent(this.search.nativeElement, 'keyup')
.pipe(
map((event: KeyboardEvent) => (event.target as HTMLInputElement).value),
debounceTime(500),
distinctUntilChanged()
)
.subscribe((filter) => {
const queryParams = filter ? { queryParams: { filter } } : undefined;
this.router.navigate(['.'], {
relativeTo: this.route,
...queryParams,
});
});
}
During OnInit
, you switchMap
the observable of the route's queryParams
to a new observable which will fetch the filtered posts from your database through the service. Upon a window refresh the filter query from the url will also be set as the input value.
private fetchPosts() {
this.posts$ = this.route.queryParams
.pipe(
switchMap((params) => {
if (params.filter) this.filterInput = params.filter;
return this.appService.fetchPosts(params.filter);
})
)
.pipe(
catchError((err) => {
this.errorMessage = err;
return EMPTY;
})
);
}
ngOnInit() {
this.fetchPosts();
}
In order to show the content, you subscribe to the observable through an async
pipe within the html.
<div *ngIf="posts$ | async as posts; else loadingOrError">
<div *ngIf="!posts.length">
<h3>No posts found</h3>
</div>
<div *ngFor="let post of posts">
<b>{{ post?.content }}</b>
<br/>
<i>
by <b>{{ post?.authorName }}</b> at
<b>{{ post?.date | date: 'dd MMMM YYYY' }}</b>
</i>
<hr />
</div>
</div>
<ng-template #loadingOrError>
<span *ngIf="errorMessage; else loading">
An error occured while loading the posts: {{ errorMessage }}
</span>
<ng-template #loading> ...loading... </ng-template>
</ng-template>