Home > Software design >  Angular async autocomplete filtering without http request?
Angular async autocomplete filtering without http request?

Time:12-14

I'm trying to create a users async autocomplete as following:

this.users = this.usersService.getUsers()
<input type="text" matInput formControlName="user" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
    <mat-option *ngFor="let user of users | async" [value]="user.id">
        {{ user.name }}
    </mat-option>
</mat-autocomplete>

I'm trying to filter the autocomplete options on typing, but I can't find how.

this.user.valueChanges.subscribe((x) => {
  // Filter users
});

Is it possible to filter users without making another http request?

CodePudding user response:

Yeah you can use rxjs filter for that. I would create a separate state variable that filters the users list.

this.filtered$ = this.user.valueChanges.pipe(
  filter(input => !!input),
  map(input => input.toLowerCase()),
  switchMap(input => this.users.pipe(
    map(users => users.filter(user => user.name.toLowerCase().includes(input)))
  ))
)

And then use the filtered$ in the template as options:

<mat-option *ngFor="let user of filtered$ | async" [value]="user.id">
       {{ user.name }}
</mat-option>

CodePudding user response:

You can just store the API call once, and then apply the filter whenever the search happens like so. The subject is used to trigger the filtering through the form value!

import { Component } from '@angular/core';
import { Observable, of, Subject, switchMap } from 'rxjs';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  subject = new Subject<void>();
  name = 'Angular';
  users: Observable<any>;
  usersUnfiltered: Array<any>;
  user;

  constructor(private usersService: UsersService) {}

  ngOnInit() {
    this.user.valueChanges.subscribe((x) => {
      // Filter users
      this.subject.next();
    });
    this.reloadUsers();
  }

  reloadUsers() {
    this.users = this.subject.asObservable().pipe(
      switchMap(() => {
        if (this.usersUnfiltered && this.usersUnfiltered.length) {
          return this.usersService.getUsers().pipe(
            map((res: any) => {
              this.usersUnfiltered = res;
            })
          );
        } else {
          const filterKey = this.user.value;
          return of(this.usersUnfiltered.filter((x) => x.includes(filterKey)));
        }
      })
    );
  }
}
  • Related