Angular 8 Autocomplete | Remote call after input event


Topic: Angular 8 Autocomplete | Remote call after input event

JeroenVunderink pro premium asked 6 months ago

Expected behavior See the result of my remote call in the autocompleter list

Actual behavior Empty list, no errors

Resources (screenshots, code snippets etc.) Are you absolutely sure that this example code works?

enter image description here enter image description here I'm using Angular 8 and mdbootstrap 10. I can't det the code to provide me with the desired result. I'm 100% sure my call http call delivers proper JSON results. The list is unfortunately not shown in the list and with a subject and observable it's not the easiest thing to debug. Can for instance find the results that should be there after the http call. But the example is so simple that I don't understand why it doesn't work.

Thanks for the help in advance.


Arkadiusz Idzikowski staff commented 6 months ago

@JeroenVunderink Did you test that on the code from our documentation or did you add any modifications? I checked the examples in our documentation and it turned out that the link to the API was out of date. It should be fixed now.

If you use modified code, please update your post and provide an example so we can reproduce the problem on our end.


JeroenVunderink pro premium commented 6 months ago

I used the standard code and of course modified it for my website. The code is use is the following:

HTML:

<!-- State -->
                <div class="md-form" *ngIf="country.valid">
                    <input type="text" class="completer-input form-control mdb-autocomplete mb-0"
                        [ngModel]="stateSearch | async" (ngModelChange)="stateSearch.next($event)"
                        [mdbAutoCompleter]="auto" placeholder="Select a state *" formControlName="state"
                        id="state" />
                    <mdb-auto-completer #auto="mdbAutoCompleter" textNoResults="Start typing..." [clearButton]="true" [displayValue]="displayStateValue">
                        <mdb-option *ngFor="let option of stateResults | async" [value]="option">
                            <div class="d-flex flex-column">
                                <strong>{{ option.Code }} - {{ option.Name }}</strong>
                            </div>
                        </mdb-option>
                    </mdb-auto-completer>
                </div>

TS:

import { Component, OnInit } from "@angular/core";
import { FormGroup, FormControl, Validators } from "@angular/forms";
import { Router } from "@angular/router";
import { Observable, Subject } from "rxjs";
import { debounceTime, map, startWith, switchMap } from "rxjs/operators";
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { environment } from "src/environments/environment";
import { ICountry, IState, ICity } from "src/app/models/country";

@Component({
  selector: "app-subscribe",
  templateUrl: "./subscribe.component.html",
  styleUrls: ["./subscribe.component.scss"],
})
export class SubscribeSubForm implements OnInit {

 // State lookup
  stateURL = environment.apiUrl + "states";
  stateSearch = new Subject();
  stateResults: Observable<any>;

  constructor(
    private router: Router,
    private http: HttpClient
  ) {}

  ngOnInit(): void {

    // States //
    this.stateResults = this.stateSearch.pipe(
      startWith(this.stateSearch),
      switchMap((stateSearch: string) => this.getStateForCountry(stateSearch))
    );

  /*
   * States lookup
   */
  getStateForCountry(searchText: string) {
      var url = this.stateURL + "/";
      if (this.country.value.Id) {
        url += this.country.value.Id + (searchText.length > 0 ? "/" + searchText : "");
      } else {
        url += "-1"
      }
      return this.http.get(url).pipe(
        debounceTime(250),
        map((items: any) => items.results)
      );
  }
  displayStateValue(selectedValue: IState) {
    return selectedValue ? selectedValue.Name : "";
  }
}

Thanks in advance for your help!


JeroenVunderink pro premium commented 6 months ago

I hope you can find the find to provide me feddback today. I have submiited my code in the post.


Arkadiusz Idzikowski staff commented 6 months ago

@JeroenVunderink We could not find any problems in the implementation. We would need more information about the data returned from your API (items.results) in order to help you resolve this problem. Currently, we can't reproduce the problem on our end because we don't have access to all variables used in your example.

Please provide more information about the data returned from API or modify the example HTML/TS code so that it uses a public API to get the data (like in our documentation).


JeroenVunderink pro premium commented 6 months ago

@Arkadiusz Idzikowski

Hi Arkadiusz, Thanks for your reply and updated the code to a public API as you asked. Still the same result in the lookup. The dropdown is empty. Hope this helps to provide me an answer on what's wrong.

Attached the code with a public API.

The API used:

https://api.genderize.io/?name=

The parameter used (Capital L):

https://api.genderize.io/?name=L

The output of the public API:

{"name":"L","gender":"male","probability":0.6,"count":8939}

TS code:

import { Component, OnInit } from "@angular/core";
import { FormGroup, FormControl, Validators } from "@angular/forms";
import { Router } from "@angular/router";
import { Observable, Subject } from "rxjs";
import { debounceTime, map, startWith, switchMap } from "rxjs/operators";
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { environment } from "src/environments/environment";
import { ICountry, IState, ICity } from "src/app/models/country";

@Component({
  selector: "app-subscribe",
  templateUrl: "./subscribe.component.html",
  styleUrls: ["./subscribe.component.scss"],
})
export class SubscribeSubForm implements OnInit {

 // State lookup
  stateURL = environment.apiUrl + "states";
  stateSearch = new Subject();
  stateResults: Observable<any>;

  constructor(
    private router: Router,
    private http: HttpClient
  ) {}

  ngOnInit(): void {

    // States //
    this.stateResults = this.stateSearch.pipe(
      startWith(this.stateSearch),
      switchMap((stateSearch: string) => this.getStateForCountry(stateSearch))
    );

  /*
   * States lookup
   */
getStateForCountry(searchText: string) {
    /*
      var url = this.stateURL + "/";
      if (this.country.value.Id) {
        url += this.country.value.Id + (searchText.length > 0 ? "/" + encodeURIComponent(searchText) : "");
      } else {
        url += "-1"
      }
      */
      var url = "https://api.genderize.io/?name=" + encodeURIComponent(searchText);
      console.log(url);
      return this.http.get(url).pipe(
        debounceTime(250),
        map((items: any) => items.results)
      );
  }
  displayStateValue(selectedValue: any) {
    //return selectedValue ? selectedValue.Name : "";
    return selectedValue ? selectedValue.name : "";
  }

HTML:

            <!-- State -->
            <div class="md-form" *ngIf="country.valid || true">
                <input type="text" class="completer-input form-control mdb-autocomplete mb-0"
                    [ngModel]="stateSearch | async" (ngModelChange)="stateSearch.next($event)"
                    [mdbAutoCompleter]="auto" placeholder="Select a state *" formControlName="state"
                    id="state" />
                <mdb-auto-completer #auto="mdbAutoCompleter" textNoResults="Start typing..." [clearButton]="true" [displayValue]="displayStateValue">
                    <mdb-option *ngFor="let option of stateResults | async" [value]="option">
                        <div class="d-flex flex-column">
                     <!--   <strong>{{ option.Code }} - {{ option.Name }}</strong> -->
                            <strong>{{ option.name }} - {{ option.gender }}</strong>
                        </div>
                    </mdb-option>
                </mdb-auto-completer>
            </div>

Arkadiusz Idzikowski staff answered 6 months ago

Are there any errors in the browser console? You should find some information about the problems when using this syntax. I found some problems in the code.

  1. You should not add formControlName when you use ngModel.
  2. In map inside http.get you return items.results but this is not correct syntax in this case, because your API returns a single object without nested parameters (you can see that when you console.log(items)). We use items.results in our examples because it is the correct syntax for the data returned by the API, in your case data structure may be different.
  3. In the example with public API, the API will return a single object but the ngFor method used in mdb-option expects iterable (array of objects). You would need to transform the data you get from the HTTP method to an array of objects in order to display it in the component (maybe there is a similar problem in your original example.

Example:

   map((items: any) => {
     const arr = [] as any[];
     arr.push(items);
     return arr;
   })

JeroenVunderink pro premium commented 6 months ago

Thanks for the feedback. Got everything work now after some changes. Wish you a happy new year!


Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Resolved

Specification of the issue
  • User: Pro
  • Premium support: Yes
  • Technology: MDB Angular
  • MDB Version: MDB4 10.1.1
  • Device: Mac
  • Browser: Chrome
  • OS: OSX
  • Provided sample code: No
  • Provided link: No