如何使用 ng-autocomplete 地址输入正确地对 Angular 中的 positionStack 进行 rest api 调用,并对其进行去抖动
How to properly make a rest api call to positionStack in Angular with an ng-autocomplete address input, and debounce it
我第一次尝试在 Angular 9 中实现自动完成地址表单。
我使用 positionstack rest api to retrieve addresses, and 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>
和我的打字稿 classes
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
}
);
}
}
}
地图服务方法是一个带有查询参数的简单 get。
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 中收到警告。我在 Web 浏览器控制台中收到错误消息:
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)
显示结果列表id,但不是很稳定,所以我可能做错了。
最后,我尝试使用 subject 和 observable 向其余调用添加去抖动方法,但它不再触发调用。
我修改的class:
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 !
感谢您的帮助!
好吧,多亏了 this tutorial。
事实上,一切都由开箱即用的 ng-autocomplete 正确处理。
我的主要问题是我没有使用我要搜索的字段名称来评估 'keyword' 变量。
所以这里是完整的解决方案,剩下的 api 调用地图服务,去抖动时间和最小查询长度也由 ng-autocomplete 处理,在 select使用我需要的属性创建模型对象。
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
);
}
}
我第一次尝试在 Angular 9 中实现自动完成地址表单。 我使用 positionstack rest api to retrieve addresses, and 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>
和我的打字稿 classes
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
}
);
}
}
}
地图服务方法是一个带有查询参数的简单 get。
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 中收到警告。我在 Web 浏览器控制台中收到错误消息:
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)
显示结果列表id,但不是很稳定,所以我可能做错了。
最后,我尝试使用 subject 和 observable 向其余调用添加去抖动方法,但它不再触发调用。
我修改的class:
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 ! 感谢您的帮助!
好吧,多亏了 this tutorial。
事实上,一切都由开箱即用的 ng-autocomplete 正确处理。 我的主要问题是我没有使用我要搜索的字段名称来评估 'keyword' 变量。
所以这里是完整的解决方案,剩下的 api 调用地图服务,去抖动时间和最小查询长度也由 ng-autocomplete 处理,在 select使用我需要的属性创建模型对象。
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
);
}
}