Reputation: 105
I have an Angular 7.2.4 application with Bootstrap 4.2.1. and I would like to call a backend service to populate an autocomplete input box but I'm having troubles even though I have already a simple bootstrap typeahead.
The html page:
<div class="form-group" [class.has-danger]="searchFailed">
<label class="form-control-label" jhiTranslate="gatewayApp.legalAddress.fullAddress" for="field_fullAddress">Full Address</label>
<ng-template #rt let-r="result" let-t="term">
<ngb-highlight [result]="r.fullAddress" [term]="t"></ngb-highlight>
</ng-template>
<input type="text" class="form-control" name="fullAddress" id="field_fullAddress"
[(ngModel)]="legalAddress.fullAddress" [ngbTypeahead]="searchAddress" placeholder="n°, nome via, città"
[resultTemplate]="rt"
[resultFormatter]="formatter"
[inputFormatter]="formatter" />
<span *ngIf="searching">searching...</span>
<div class="form-control-feedback" *ngIf="searchFailed">Sorry, suggestions could not be loaded.</div>
</div>
The service:
type EntityArrayResponseType = HttpResponse<IAddress[]>;
query(req?: any): Observable<EntityArrayResponseType> {
console.log('[address.service.ts] query');
const param = new HttpParams().set('searchCriteria', ADDRESS_OTHER_TYPE_CODE);
return this.http.get<IAddress[]>(`${this.resourceUrl}/findbycriteria`, { params: param, observe: 'response' });
}
The model:
export interface IAddress {
id?: number;
addressType?: string;
...
fullAddress?: string;
}
export class Address implements IAddress {
constructor(
public id?: number,
public addressType?: string,
...
public fullAddress?: string
) {}
}
The controller:
searchAddress = (text$: Observable < string >) =>
text$.pipe(
debounceTime(300),
distinctUntilChanged(),
tap(() => (this.searching = true)),
switchMap(term =>
this.geoLocalizationService.query(term).pipe(
tap(() => (this.searchFailed = false)),
catchError(() => {
this.searchFailed = true;
return of([]);
})
)
),
tap(() => (this.searching = false))
)
formatter = (x: {fullAddress: string}) => x.fullAddress;
Two problems:
1) Filling the fullAddress
input box does trigger the searchAddress
but geoLocalizationService.query
always receive an empty term
and send null to the underlying backend service
2) Forcing the backend service to give back however a result as a test, does make no data displayed into the input box. Getting this:
[geo-localization.service.ts] query geo-localization.service.ts:18:8
ERROR Error: "Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays."
Angular 6
View_NgbTypeaheadWindow_0 NgbTypeaheadWindow.html:5
Angular 8
node_modules ng-bootstrap.js:11024
RxJS 41
Angular 8
NgbTypeaheadWindow.html:5:4
View_NgbTypeaheadWindow_0 NgbTypeaheadWindow.html:5
Angular 5
RxJS 5
Angular 11
ERROR CONTEXT
Object { view: {…}, nodeIndex: 4, nodeDef: {…}, elDef: {…}, elView: {…} }
NgbTypeaheadWindow.html:5:4
View_NgbTypeaheadWindow_0 NgbTypeaheadWindow.html:5
Angular 5
RxJS 5
Angular 11
Thanks in advance!
==========
Solved point 1 due to a silly parameter. Now I'm able to hit properly the backend service as I receive:
[[{"place_id":48841472,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright","osm_type":"node","osm_id":3749006665,"boundingbox":["46...","46...","13...","13...."],"lat":"46....","lon":"13....","display_name":"---","class":"place","type":"house","importance":0.411,"address":{"house_number":"5","road":"---a","neighbourhood":"---","suburb":"---","city":"---","county":"---","state":"---","postcode":"---","country":"---","country_code":"---"}}]]
Now still having point 2 error, I think because there is something wrong with this service:
@Injectable({ providedIn: 'root' })
export class GeoLocalizationService {
public resourceUrl = SERVER_API_URL + 'api/geolocalization/addresses';
constructor(protected http: HttpClient) {}
query(req?: any): Observable<EntityArrayResponseType> {
console.log('[geo-localization.service.ts] query' + req);
const param = new HttpParams().set('searchString', req);
return this.http.get<IAddress[]>(this.resourceUrl, {
params: param, observe: 'response' });
}
}
May be that this is not the right way to return an Observable. Should I return it using a subscribe instead? In an other class this is working:
provinces: IDomainBean [];
ngOnInit() {
this.legalPersonService.findAllProvinces().subscribe(
(res: HttpResponse<IDomainBean[]>) => {
this.provinces = res.body;
},
(res: HttpErrorResponse) => this.onLegalFormTypeError(res.message)
);
}
searchProvince = (text$: Observable< string >) =>
text$.pipe(
debounceTime(200),
distinctUntilChanged(),
map(term => term === '' ? []
: this.provinces.filter(v => v.description.toLowerCase().indexOf(term.toLowerCase()) > -1).slice(0, 10))
)
formatter = (x: {description: string}) => x.description;
Upvotes: 2
Views: 578
Reputation: 105
Almost resolved...now I'm able to query the service with these changes:
1) "address" instead of "address.fullAddress" in ngModel:
<input type="text" class="form-control" name="fullAddress" id="field_fullAddress"
[(ngModel)]="address" [ngbTypeahead]="searchAddress" placeholder="n°, nome via, città"
[resultTemplate]="rt"
[resultFormatter]="formatter"
[inputFormatter]="formatter" />
2) Changed the service:
searchAddress = (text$: Observable <string>) =>
text$.pipe(
debounceTime(300),
distinctUntilChanged(),
tap(() => (this.searching = true)),
switchMap(term =>
this.geoService(term).pipe(
tap(() => (this.searchFailed = false)),
catchError(() => {
this.searchFailed = true;
return of([]);
})
)
),
tap(() => (this.searching = false))
)
formatter = (x: {fullAddress: string}) => x.fullAddress;
geoService(term: String): Observable <IGeoAddress []> {
if (term === '') {
return of([]);
}
return this.geoLocalizationService.queryGeoAddress(term).pipe(
map(res => {
return res.body.map(add => {
const addnew = new Address(
// setting initial address values
this.address.idSubject,
this.address.idContact,
add.latitude,
add.longitude,
...
);
return addnew;
});
})
);
}
If it could help someone else or it could be improved.
Upvotes: 2