I am using Angular12, and here i am using autocomplete in angular material, here the issue is that i am able to get result based on api hit, but in html, i always get latest-1 response shown.
Ts:
private getAutoPopulateData(serchVal: any): Observable<string[]> {
let data: any = [];
let results: any = [];
if (serchVal && serchVal.length > 0) {
this.searchService.searchOpenSearchData(serchVal).subscribe((searchData: AutoPopulate) => {
results = searchData.hits;
if (results.hits.length > 0) {
results.hits.forEach(h=>{
let item=h._source;
let addressProps=Object.keys(item).filter(p=> p.startsWith('AddressLine') && item[p].length >0);
addressProps.forEach(ap=>{
let addresses=item[ap].map((ai,idx)=>{
let address= `${ai} ${item.City[idx]} ${item.State} ${item.Zip[idx]}`;
return {name:item.ProfileName,address}
});
data= data.concat(addresses);
this.seeResultsData = data.length;
})
});
}
this.resultData = of(data);
this.isLoading = false;
});
}
else {
this.isLoading = false
}
if(this.resultData) {
return this.resultData;
} else
return null
}
private autoCompleteFormInit() {
this.stateCtrl.valueChanges.pipe(
debounceTime(500),
tap((() => this.isLoading = true)),
map(value => (this.getAutoPopulateData(value))
.pipe(finalize(() => this.isLoading = false)))
).subscribe(value => {
this.filteredOptions = value
});
}
public resultData: Observable<any> = of([]);
filteredOptions: Observable<any> = of([]);
searchOpenSearchData(searchValue:any): Observable<AutoPopulate> {
let url = URLConstants.openSearchUrl + searchValue;
return this.http.get<AutoPopulate>(url);
}
HTML:
<input matInput aria-label="State" placeholder="Enter a Name" [matAutocomplete]="auto" class="paddingleft-5 text-medium" (ngModelChange)="updatedVal($event)" type="text" [formControl]="stateCtrl" *ngIf="form.value.tradingPartnerValue && dropdownSelection == 'tradingPartnerName'"/>
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="onOptionSelected($event.option)">
<div *ngIf="showAutocomplete" class="{{seeResultsData >3 ? 'set-height' : ''}}">
<mat-option *ngIf="isLoading" class="is-loading content-center">
<mat-spinner diameter="50" class="show-center"></mat-spinner>
</mat-option>
<ng-container *ngIf="!isLoading">
<mat-option class="option-custom text-medium" *ngFor="let state of filteredOptions | async"
[value]="state" [ngClass]="{'hide-autocomplete': !showAutocomplete}">
<span [innerHTML]="state.name | highlight : stateCtrl.value"></span>
<span [innerHTML]="state.address | highlight : stateCtrl.value"></span>
</mat-option>
</ng-container>
</div>
</mat-autocomplete>
Json response:
{
"took": 178,
"timed_out": false,
"_shards": {
"total": 0,
"successful": 0,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 10000,
"relation": "gte"
},
"max_score": 12.9226,
"hits": [
{
"_index": "pulse",
"_id": "17320",
"_score": 12.9226,
"_source": {
"ProfileName": "Pharmarx Pharmaceutical Inc",
"State": "CA",
"BusinessModel": "Dispenser",
"LicenseInfo": [
{
"LicenseType": "Resident Licensed Sterile Compounding",
"LicenseNumber": "99658",
"AddressLine1": "21441 Osborne St Ste C & D",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "91304",
"City": "Canoga Park"
},
{
"LicenseType": "Retail Pharmacy",
"LicenseNumber": "50511",
"AddressLine1": "21441 Osborne St Ste C & D",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "91304",
"City": "Canoga Park"
}
]
}
},
{
"_index": "pulse",
"_id": "7723",
"_score": 10.891928,
"_source": {
"ProfileName": "ENCINO PHARMACY_AKA MDR PHARMACEUTICAL CARE",
"State": "MI",
"BusinessModel": "Dispenser",
"LicenseInfo": [
{
"LicenseType": "PHARMACY",
"LicenseNumber": "5301010993",
"AddressLine1": "17071 VENTURA BLVD STE 100",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "91316",
"City": "ENCINO"
}
]
}
},
{
"_index": "pulse",
"_id": "7724",
"_score": 9.796367,
"_source": {
"ProfileName": "Encino Pharmacy_aka Mdr Pharmaceutical Care",
"State": "CA",
"BusinessModel": "Dispenser",
"LicenseInfo": [
{
"LicenseType": "Retail Pharmacy",
"LicenseNumber": "34818",
"AddressLine1": "17059-17071 VENTURA BLVD.",
"AddressLine2": "STE. 100",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "91316",
"City": "ENCINO"
}
]
}
},
{
"_index": "pulse",
"_id": "192",
"_score": 9.656487,
"_source": {
"ProfileName": "Abdin Pharmacies Pharma LLC",
"State": "FL",
"BusinessModel": "Dispenser",
"LicenseInfo": [
{
"LicenseType": "Pharmacy",
"LicenseNumber": "036936",
"AddressLine1": "1873 SECOND AVE. NEW",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "10029",
"City": "YORK"
},
{
"LicenseType": "Pharmacy",
"LicenseNumber": "033404",
"AddressLine1": "1401 BRONX RIVER AVE.",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "10472",
"City": "BRONX"
},
{
"LicenseType": "Pharmacy",
"LicenseNumber": "PH33043",
"AddressLine1": "32866 US Hwy 19N",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "34684",
"City": "PALM HARBOR"
}
]
}
},
{
"_index": "pulse",
"_id": "17304",
"_score": 9.186218,
"_source": {
"ProfileName": "PHARMALABS, LLC PharmaLabs",
"State": "FL",
"BusinessModel": "Dispenser",
"LicenseInfo": [
{
"LicenseType": "Pharmacy",
"LicenseNumber": "PH26495",
"AddressLine1": "10901 ROOSEVELT BOULEVARD NORTH SUITE 1200",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "33716",
"City": "Saint Petersburg"
}
]
}
}
]
}
}
in UI, i need to display, Profile Name as name in UI, along with multiple address present under same ProfileName, for example one sample i have taken from json, so from this array of object must contain,
[{name:'Pharmarx Pharmaceutical Inc', address:'21441 Osborne St Ste C & D,Canoga Park CA 9104'},
{name:'Pharmarx Pharmaceutical Inc', address:'21442 Osborne St Ste C & D(Address2/Address3/Address4)what ever is present must show, Canoga Park CA 9104'}]
so it is like with one Profile Name, we have multiple location address
"_source": {
"ProfileName": "Pharmarx Pharmaceutical Inc",
"State": "CA",
"BusinessModel": "Dispenser",
"LicenseInfo": [
{
"LicenseType": "Resident Licensed Sterile Compounding",
"LicenseNumber": "99658",
"AddressLine1": "21441 Osborne St Ste C & D",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "91304",
"City": "Canoga Park"
},
{
"LicenseType": "Retail Pharmacy",
"LicenseNumber": "50511",
"AddressLine1": "21441 Osborne St Ste C & D",
"AddressLine2": "",
"AddressLine3": "",
"AddressLine4": "",
"Zip": "91304",
"City": "Canoga Park"
}
]
}
You have some issues in your code, but the main reason that you get the previous result is that you do:
if (this.resultData) {
return this.resultData;
}
If this.resultData
is populated with the data from a previous request it will return immediately before the observable (this.searchService.searchOpenSearchData(serchVal)
) emits and sets a new value for this.resultData
.
You should set this.resultData
to the result of the observable from the api request and await the result rather then returning it like you did in your code.
Something like:
private getAutoPopulateData(serchVal: any): Observable<string[]> {
let data: any = [];
let results: any = [];
if (serchVal && serchVal.length > 0) {
this.isLoading = true;
this.resultData = this.searchService.searchOpenSearchData(serchVal).pipe(
map((searchData: AutoPopulate) => {
results = searchData.hits;
if (results.hits.length > 0) {
results.hits.forEach(h => {
let item=h._source;
let addressProps=Object.keys(item).filter(
p => p.startsWith('AddressLine') && item[p].length > 0
);
addressProps.forEach(ap => {
let addresses=item[ap].map((ai,idx)=>{
let address= `${ai} ${item.City[idx]} ${item.State} ${item.Zip[idx]}`;
return {name:item.ProfileName, address}
});
data = data.concat(addresses);
this.seeResultsData = data.length;
});
return data;
}
return null;
}),
finalize(() => this.isLoading = false);
);
} else {
this.resultData = of(null);
}
return this.resultData;
}
I tried to return null
where needed, but check if those places are according to your need+s (maybe you want to return an empty array instead). I used pipe
, map
and finalize
from rxjs which need to be imported. I moved your isLoading = false
into finalize so it also "stops loading" on error responses.
If further questions, leave a comment.
After you added additional code to your question in an "edit" I have to update my answer:
With the changes I mention above, your autoCompleteFormInit
method should change as follows:
private this.subscription: Subscription;
private autoCompleteFormInit() {
this.subscription = this.stateCtrl.valueChanges.pipe(
debounceTime(500),
switchMap(value => this.getAutoPopulateData(value))
).subscribe(value => this.filteredOptions = value);
}
// To prevent memory leaks, store subscription and unsubscribe on destroy
public ngOnDestroy(): void {
if (this.subscription) this.subscription.unsubscribe();
}
I also changed the logic regarding isLoading
, it is better if it is only called in the method that actually sends the request.
There is still a lot more that can be improved, but this should work with minimal code changes in the original posted code example.