angularangular8angular-material-8

Implement JSON data from API into mat-autocomplete input


I am trying to use Angular Material Component "Highlight the first autocomplete option" onto an input which uses JSON data from an API server (which already works by fetching all the JSON data). Don't know where I am going wrong with this.

I have tried using the Angular Material example but that uses the hardcoding method where I need my data from an API

service.ts

import { Appl } from '../intdets';
export class IntdetsService {
  private url = "/assets/IntDet.json";
  constructor(private http: HttpClient) { }

  getName(): Observable<Appl[]> {
    return this.http.get<Appl[]>(this.url);
  }
}


intdets.ts

export class Appl {
    _NAME: string;
}

JSON

{
  "_APPLS": [
    {
     "_NAME": "some string",
    },...]}

component.ts

export class IntdetsComponent implements OnInit {
  public apps = [];
  testCtrl: FormControl;
  filteredTest: Observable<Appl[]>;

  constructor(private _intdetsService: IntdetsService) { 
this.testCtrl = new FormControl();
    this.filteredTest = this.testCtrl.valueChanges
      .pipe(
            startWith(null),
            switchMap(val => { return this.filterTest(val || '' )}));
  }
  filterTest(val: string) {
    return this._intdetsService.getName().pipe(
      map(res => res.filter(s => {
        return s._NAME.toLowerCase().indexOf(val.toLowerCase()) === 0
      }))
    ) 
  }
  ngOnInit() {
    this._intdetsService.getName()
        .subscribe(data => {
          this.apps = data;
          console.log(data);
         });
  }
}

HTML
         <mat-form-field >
            <mat-label>Test Name</mat-label>
            <input type="text" matInput [formControl]="testCtrl" [matAutocomplete]="autoTest">
            <mat-autocomplete autoActiveFirstOption #autoTest="matAutocomplete">
               <mat-option *ngFor="let test of filteredTest | async" [value]="test._APPLS">
               {{test._NAME}}
               </mat-option>
            </mat-autocomplete>
         </mat-form-field>
      </p>

Solution

  • The array you want, is inside _APPLS, so you need to extract that:

    getName(): Observable<Appl[]> {
      return this.http.get<any>(this.url).pipe(
        map(data => data._APPL as Appl[])
      )
    }
    

    I would also suggest, that in the service you would store the response in a variable after first fetch. We wouldn't want to overload an api by fetching data on each keystroke. You could do like:

    data = <Appl[]>[];
    
    getName(): Observable<Appl[]> {
     return this.data.length 
       ? of(this.data) 
       : this.http.get<any>(this.url).pipe(
           map(data => { 
             this.data = data._APPL as Appl[]
             return this.data;
           })
         )
    }
    

    You could also consider adding a debounceTime on your form control change.