In my Angular app I have this simple stream for a search bar.
.ts
user$: Observable<User> = this.authService.user$;
ally$: Observable<UserAndMatch> = this.user$.pipe(
switchMap((user: User) =>
this.userService.getFollowing(user)
shareReplay(1),
);
filteredAlly$: Observable<UserAndMatch> = new Observable<UserAndMatch>();
@ViewChild('search') search: ElementRef;
ngAfterViewInit() {
const query$ = fromEvent(this.search.nativeElement, 'input').pipe(
debounceTime(200),
map(() => this.search.nativeElement.value),
startWith(''),
map((query: string) => query.trim().toLowerCase()),
distinctUntilChanged(),
);
this.filteredAlly$ = combineLatest([query$, this.ally$]).pipe(
map(([query, ally]: [string, UserAndMatch]) => {
return !query ? ally : this.allyFilter(ally, query)
}),
);
}
allyFilter(ally, query) {
ally.users = ally.users.filter(user => user.name.includes(query));
return ally;
}
.html
<form autocomplete="off">
<input
#search
/>
</form>
<ng-container *ngrxLet="filteredAlly$ as ally"></ng-container
The stream works as expected when typing a word, but when the user deletes some letters the array from filteredAlly$
doesn't repopulate and, logging the values in the ally$
pipeline, I found out that ally$
values are being filtered too, while I wanted them to be a "backup" to apply some filter function to which should return filteredAlly$
CodePudding user response:
The probable culprit is the first line of allyFilter
:
ally.users = ally.users.filter(user => user.name.includes(query));
You're modifying ally.users
in place, instead of creating a copy. This means that you're shortening the list of allies when the user is typing, and you're not getting a "fresh" copy of allies later, when the user deletes the characters.
It will probably suffice to shallow-copy ally object in allyFilter, like so:
allyFilter(ally, query) {
return {
...ally,
users: ally.users.filter(user => user.name.includes(query));
};
}