Как правильно сделать вызов rest API для positionStack в Angular с вводом адреса ng-autocomplete и отменить его

Я впервые пытаюсь реализовать форму автозаполнения адреса в Angular 9. Я использую positionstack rest api для получения адресов и angular-ng-autocomplete для отображения результатов в ввод текста. Мне удается вызвать API и получить результаты, даже отобразить их, но я не думаю, что делаю это правильно, потому что у меня появляется несколько предупреждений и ошибок.

И, наконец, я хотел бы отказаться от остальных вызовов API. Пока он выполняется при каждом нажатии клавиши, но мне не удается заставить его работать.

Вот объект ответа, который я получаю от API:

"data": [
        {
            "latitude": 45.136054,
            "longitude": 5.711749,
            "type": "street",
            "name": "Avenue Général de Gaulle",
            "number": null,
            "postal_code": null,
            "street": "Avenue Général de Gaulle",
            "confidence": 0.8,
            "region": "Isère",
            "region_code": "IS",
            "county": null,
            "locality": null,
            "administrative_area": "Le Pont-De-Claix",
            "neighbourhood": null,
            "country": "France",
            "country_code": "FRA",
            "continent": "Europe",
            "label": "Avenue Général de Gaulle, Le Pont-De-Claix, France"
        },...
]

Вот мой html-шаблон

  <ng-autocomplete
    [data]="addresses"
    [searchKeyword]="keyword"
    [itemTemplate]="itemTemplate"
    [notFoundTemplate]="notFoundTemplate"
    (inputChanged)="onChange($event)"
    placeHolder="Recherchez votre adresse">
  </ng-autocomplete>

  <ng-template #itemTemplate let-item>
    <a [innerHTML]="item?.label"></a>
  </ng-template>

  <ng-template #notFoundTemplate let-notFound>
    <div [innerHTML]="notFound"></div>
  </ng-template>

и мои классы машинописи

export class AutocompleteAddressComponent {

  keyword: string;
  public addresses;

  constructor(private mapService: MapService) { }

  onChange(event: any) {
    this.addresses = [];
    if (event.length > 3 ) {
      this.mapService.getCoordinates(event).subscribe(
        response => {
          this.addresses = response.data; <- WARNING: Property 'data' does not exist on type 'Object
        }
      );
    }
  }
}

Метод картографического сервиса представляет собой простое получение с параметрами запроса.

  public getCoordinates(query: string) {
    console.log('Sending request');
    query = query.trim();
    const options = query ?
      {
        params: new HttpParams()
          .set('access_key', environment.positionstack_apikey)
          .set('query', query)
          .set('limit', '10')
          .set('output', 'json')
      } : {};

    return this.httpClient.get(
      this.baseUrl + '/forward',
      options
    );
  }

У меня есть предупреждение в IntelliJ, когда я пытаюсь повлиять на результат вызова объекта моего адреса (который я позже преобразую в модель). и я получаю сообщение об ошибке в консоли веб-браузера:

core.js:6185 ERROR TypeError: Cannot read property 'replace' of undefined
at HighlightPipe.transform (angular-ng-autocomplete.js:1529)
    at pureFunction3Internal (core.js:36519)
    at Module.ɵɵpipeBind3 (core.js:36698)
    at AutocompleteComponent_li_10_div_2_Template (angular-ng-autocomplete.js:92)

идентификатор списка результатов отображается, но он не очень стабилен, поэтому я, вероятно, делаю что-то не так.

Наконец, я попытался добавить метод устранения отказов к оставшемуся вызову, используя тему и наблюдаемое, но он больше не вызывает вызов.

Мой модифицированный класс:

export class AutocompleteAddressComponent implements OnInit{
  result$: Observable<any>;
  subject = new Subject<string>();

  public addresses;
  keyword: string;

  constructor(private mapService: MapService) { }

  ngOnInit(): void {
    this.result$ = this.subject.pipe(
      debounceTime(500),
      map(searchText => this.mapService.getCoordinates(searchText).subscribe(
        result => console.log(result)
      ))
    );
  }

  onChange(keyword: any) {
    this.addresses = [];
    if (keyword.length > 3 ) {
      this.subject.next(keyword);
    }
  }
}

Если вопрос недостаточно ясен, я сделаю stackblitz! Спасибо за помощь !


person Alain Duguine    schedule 15.06.2020    source источник


Ответы (1)


Хорошо, когда я копал больше, я нашел гораздо более простой способ делать все, как я хочу, благодаря это руководство.

На самом деле все правильно обрабатывается ng-autocomplete из коробки. Моя основная проблема заключалась в том, что я не оценил переменную «ключевое слово» с именем поля, в котором я хочу искать.

Итак, вот полное решение с остальным API-вызовом картографической службы, временем устранения отказов и минимальной длиной запроса, которые также обрабатываются ng-autocomplete, и при выборе создания объекта модели с нужными мне свойствами.

HTML-шаблон:

<div class="ng-autocomplete">
  <ng-autocomplete 
    [data]="addresses"
    [searchKeyword]="keyword"
    (selected)='selectEvent($event)'
    (inputChanged)='getServerResponse($event)'
    (inputCleared)="searchCleared()"
    [debounceTime]="300"
    [isLoading]="isLoadingResult"
    [minQueryLength]="3"
    [itemTemplate]="itemTemplate"
    [notFoundTemplate]="notFoundTemplate"
    placeHolder="Rechercher une adresse...">
  </ng-autocomplete>

  <ng-template #itemTemplate let-item>
    <a [innerHTML]="item.label"></a>
  </ng-template>

  <ng-template #notFoundTemplate let-notFound>
    Aucun résultat !
  </ng-template>
</div>

файл component.ts:

import {Component} from '@angular/core';
import {MapService} from '../service/map.service';
import {AddressModel} from '../model/AddressModel';


@Component({
  selector: 'app-autocomplete',
  templateUrl: './autocomplete.component.html',
  styleUrls: ['./autocomplete.component.css']
})
export class AutocompleteAddressComponent {

  address: AddressModel;
  addresses: any;
  keyword = 'label';
  isLoadingResult: boolean;

  constructor(private mapService: MapService) {
  }

  getServerResponse(event) {
    this.isLoadingResult = true;
    this.mapService.getCoordinates(event).subscribe(
      (result: any) => {
        this.addresses = result.data;
        this.isLoadingResult = false;
      }
    );
  }

  selectEvent(event: any) {
    this.address = new AddressModel(
      event.latitude,
      event.longitude,
      event.label
    );
  }

  searchCleared() {
    console.log('searchCleared');
    this.addresses = [];
  }
}

картографический сервис:

import { Injectable } from '@angular/core';
import {HttpClient, HttpParams} from '@angular/common/http';
import {environment} from '../../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class MapService {

  private baseUrl: string = environment.map_url;

  constructor(private httpClient: HttpClient) { }

  public getCoordinates(query: string) {
    query = query.trim();
    const options = query ?
      {
        params: new HttpParams()
          .set('access_key', environment.positionstack_apikey)
          .set('query', query)
          .set('limit', '10')
          .set('output', 'json')
      } : {};

    return this.httpClient.get(
      this.baseUrl + '/forward',
      options
    );
  }
}
person Alain Duguine    schedule 16.06.2020