1

I have a problem filling a Users autocomplete on first load.

This is my UsersService:

public users: User[] = [];

constructor(private http: HttpClient) {
    this.http.get<User[]>(environment.apiUrl + URLS.USERS).subscribe({
      next: (res) => {
        this.Users = res;
      },
      error: (err) => {
        console.log(err);
      },
    });
}

filterUsers(val: string) {
  return this.users.filter(
    (user) =>
      user.name.toLowerCase().includes(val.toLowerCase())
  );
}

I get Users[] on the service constructor because this.users is going to be used by multiple components.

On the other hand I have a component as following:

constructor(
  private fb: FormBuilder,
  public userService: UsersService) {

  this.form = this.fb.group({
    user: this.user = this.fb.control('')
  });

  this.filteredUsers = this.user.valueChanges.pipe(
      startWith(''),
      map((value: string) => this.userService.filterUsers(value))
  );
}

And the view:

<mat-form-field class="example-full-width" appearance="outline">
    <mat-label>User</mat-label>
    <input type="text" matInput formControlName="user" [matAutocomplete]="auto">
    <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete" [panelWidth]="'auto'">
        <mat-option *ngFor="let sp of filteredUsers | async" [value]="sp.name">
            {{sp.name}}
        </mat-option>
    </mat-autocomplete>
</mat-form-field>

The problem: on first load,filterUsers is executed before the http GET request is completed, so this.users is null and nothing is returned. The autocomplete is empty when focusing.

If I type anything then this.users already has value and the list is correctly displayed.

How can I set values on my autocomplete on first load?

2 Answers 2

0

Finally solved with observables:

Service:

public users: User[] = [];

private subj = new BehaviorSubject('');
userObs = this.subj.asObservable();

constructor(private http: HttpClient) {
    this.http.get<User[]>(environment.apiUrl + URLS.USERS).subscribe({
      next: (res) => {
        this.Users = res;
        this.subj.next('ok');
      },
      error: (err) => {
        console.log(err);
      },
    });
}

Component:

ngOnInit(): void {
  this.userService.userObs.subscribe(x => { if (x == "ok") this.user.updateValueAndValidity(); });
}
Sign up to request clarification or add additional context in comments.

1 Comment

This solution will scale very poorly if you're going to have a lot of users in your application. It would be better to change your API to do the filtering on the server. Then you wouldn't even need a service method: filteredUsers$ = this.user.valueChanges.pipe(debounceTime(100), switchMap(str => this.http.get<User[]>(url +'filter='+ str)))
-1

Instead of putting filterUsers in constructor put it into ngOnInit. ngOnInit is called after the constructor.

2 Comments

No sense, ngOnInit also executed before an http response.
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.