Home > Back-end >  Autocomplete from object instead of array
Autocomplete from object instead of array

Time:07-24

I am trying to use Autocomplete from Angular Material.

Their data is options: string[] = ['One', 'Two', 'Three'];, but instead of that I have an object.

This is how I get the data from the object, which is in currencies.json:

list(): Observable<Currencies> {
  return this._http.get('./assets/currencies.json') as unknown as Observable<Currencies>;
}
public currencies$ = this.getAllCurrencies();

getAllCurrencies(): Observable<Currencies> {
  return this._currencyService.list();
}

Now this is how I tried to bind the autocomplete:

<ng-container *ngIf="currencies$ | async">
  <mat-form-field appearance="fill">
    <mat-label>Currency</mat-label>
      <input id="currency" type="text" matInput placeholder="Select a currency. E.g. USD" [matAutocomplete]="auto" formControlName="currency">
      <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
        <mat-option *ngFor="let currency of currencies$ | keyvalue" [value]="currency.key">
          {{currency.key}}
        </mat-option>
      </mat-autocomplete>
  </mat-form-field>
</ng-container>
this.currencies$ = this.formMain.valueChanges.pipe(
  startWith(''),
  map(value => this.currencies$.subscribe(data => data.currency.includes((value.toString().toUpperCase() || '')))),
);

But then I get this error:

TS2322: Type 'Observable' is not assignable to type 'Observable'. Property 'currency' is missing in type 'Subscription' but required in type 'Currencies'.

How can I fix this?


I have also tried it this way:

currencies?: Observable<string[]>;

this.currencies = this.formMain.valueChanges.pipe(
  startWith(''),
  map(value => this.currencies$.subscribe(data => data.currency.includes((value.toString().toUpperCase() || '')))),
);

but then I get this error:

TS2322: Type 'Observable' is not assignable to type 'Observable<string[]>'. Type 'Subscription' is missing the following properties from type 'string[]': length, pop, push, concat, and 28 more.

Also tried it without subscribe():

this.formMain.valueChanges.pipe(
  startWith(''),
  map(value => [this.currencies$].filter(currency => currency.includes(value)))
);

But then I get the error:

TS2339: Property 'includes' does not exist on type 'Observable'.

This is my code on StackBlitz and this is the official code of Angular Material on StackBlitz. I want the result to be exactly the same as the official code but with my own data.

CodePudding user response:

First of all, you use wrong interface for your object.

The code

export interface Currencies {
  currency: string;
}

means that your data should be smth like:

{
  currency: 'some string'
}

In case of your object you can use:

export type Currencies = Record<string, string>;

Secondly, you don't have to cast your http call:

this._http.get('./assets/currencies.json') as unknown as Observable<Currencies>;

just use generic version of http.get<T> method:

this._http.get<Currencies>('./assets/currencies.json')
               ^^^^^^^^^^

Third, you should be using [formControl] binding in your stackblitz because you don't have a wrapper with formGroup:

formControlName="currency" => [formControl]="myControl"

Finally, while constructing currencies$ observable you need to continue work with Observable by using switchMap instead of map rxjs operator:

currencies$ = this._currencyService.list().pipe(
  switchMap((currencies) => {
    return this.myControl.valueChanges.pipe(
      startWith(''),
      map((query) =>
        Object.fromEntries(
          Object.entries(currencies).filter(([key]) =>
            key.includes(query.toUpperCase())
          )
        )
      )
    );
  })
);

Forked Stackblitz

  • Related