Search code examples
htmltypescriptfilterangular6mat-autocomplete

Auto complete filter function only works after type something


The drop down value should be appear when the user touches the input field, but my drop down only appears after I type something in the input.

This is my HTML code:

<mat-form-field class="example-chip-list" style="width:100%">
    <input placeholder="Vacancy" formControlName="job" [matAutocomplete]="auto" matInput>
    <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
      <mat-option *ngFor="let job of filteredJobs | async" [value]="job">
        {{job?.refId}} - {{job?.title}}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>

And here are my type script functions:

ngOnInit() {
   this.getAllVacancyDetails();

    this.filteredJobs = this.vacancyForm.controls['job'].valueChanges.pipe(
      startWith(null),
      map((possition: string | null) => possition ? this._filterJobs(possition) : this.jobs)
    );
  }


public getAllVacancyDetails() {
    this.vacancyService.getAllvacancies().subscribe(
      res => {
        if (res.status == 200) {
          this.jobs = res.body;
        }
      },
      err => {
        this.openSnackbar("An Error Occured while Loading Dropdown Data");
      }
    );
  }

     private _filterJobs(value: any): Job[] {
        let jobsList: Job[] = new Array();
        if (!(value instanceof Object)) {

          const filterValue = value.toLowerCase();
          this.jobs.forEach(job => {
            if (job.title.toLowerCase().indexOf(filterValue) === 0) {
              jobsList.push(job);
            }
          });
          if(jobsList.length == 0){
            this.vacancyForm.controls['job'].setErrors({'incorrect': true});
          }
        }


    return jobsList;
  }

Solution

  • It happens because getAllVacancyDetails() is async and when you emit null with startWith(null) - this.jobs hasn't received job list from Backend yet. So you need to notify this.filteredJobs stream once jobs was loaded. You could fix it somehow like this:

    1.In typescript file add a new property:

    private _loadedJobs$ = new Subject()
    
    1. In getAllVacancyDetails() method (just after this.jobs = res.body;) add a string this._loadedJobs$.next('');
    2. Modify your this.filteredJobs stream like this:

      this.filteredJobs = merge(this.vacancyForm.controls['job'].valueChanges, this._loadedJobs$.next('')).pipe( ...same what do you have now )

    I am quite sure that there are more elegant way to fix or rework it but I just wanted to give you some hint :) Hope it will help. Also there is example:

    https://stackblitz.com/edit/angular6-material-autocomplete-qrlhaf?file=src/app/app.component.ts