Reputation: 935
I've a huge list of counties
and want to implement an auto complete feature.
I'm using the same form to add
a new customer or update
an existing customer.
Now, the problem is that for adding a new customer, my program works perfectly, but when I updates it, in county dropdown list it show the countyId
instead of name
.
How do achieve that ?
see the images in order
county schema is as
{
countyId: number;
name:string
}
I'm trying with the following code in html
<mat-form-field appearance="outline" fxFlex="50" class="pr-4">
<mat-label>County</mat-label>
<input type="text" placeholder="County" name="" matInput formControlName="countyId" [matAutocomplete]="auto">
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
<mat-option *ngFor="let county of filteredCounties | async" [value]="county.name">
{{county.name}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
and in my component file
import { Component, OnInit, Inject, ViewChild, AfterViewInit, ElementRef, ComponentRef } from '@angular/core';
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { CustomerService } from '../../../../services/customer/customer.service';
import { CountyService } from '../../../../services/county/county.service';
import { SubCountyService } from '../../../../services/subCounty/sub-county.service';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import PNotify from 'pnotify/dist/es/PNotify';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
@Component({
selector: 'app-new-customer',
templateUrl: './new-customer.component.html',
styleUrls: ['./new-customer.component.scss']
})
export class NewCustomerComponent implements OnInit {
dataSending = false;
customerId;
updatePerson;
updatedCustomer;
ready = false;
dialogView;
ref: ComponentRef<any>;
newCustomerForm: FormGroup;
counties; // list of counties
subCounties;
filteredCounties: Observable<string[]>;
constructor(
private _formBuilder: FormBuilder,
private router: Router,
private route: ActivatedRoute,
private customerService: CustomerService,
private countyService: CountyService,
private subCountyService: SubCountyService,
public dialogRef: MatDialogRef<NewCustomerComponent>,
@Inject(MAT_DIALOG_DATA) public display: any
) {
this.dialogView = display.view;
this.route.paramMap.subscribe((params) => {
this.customerId = params.get('customerId');
if (this.customerId) {
this.customerService.getCustomer(this.customerId).subscribe((response) => {
this.updatePerson = response;
this.newCustomerForm = this._formBuilder.group({
name: [this.updatePerson.name, Validators.required],
nationalId: [this.updatePerson.nationalId, Validators.required],
gender: [this.updatePerson.gender, Validators.required],
phone1: [this.updatePerson.phone1],
phone2: [this.updatePerson.phone2],
bishopName: [this.updatePerson.bishopName, Validators.required],
bishopPhone: [this.updatePerson.bishopPhone, Validators.required],
countyId: [this.updatePerson.countyId, Validators.required],
county: [this.updatePerson.county, Validators.required],
subCountyId: [this.updatePerson.subCountyId, Validators.required],
address: [this.updatePerson.address, Validators.required],
additionalInfo: [this.updatePerson.additionalInfo],
input1: [this.updatePerson.input1],
input2: [this.updatePerson.input2],
defaultingRecord: [''],
});
this.ready = true;
});
}
else {
this.ready = true;
this.newCustomerForm = this._formBuilder.group({
nationalId: ['', Validators.required],
name: ['', Validators.required],
gender: ['', Validators.required],
phone1: [''],
phone2: [''],
county: [''],
countyId: ['', Validators.required],
subCountyId: ['', Validators.required],
bishopName: ['', Validators.required],
bishopPhone: ['', Validators.required],
address: ['', Validators.required],
additionalInfo: [''],
input1: [''],
input2: [''],
defaultingRecord: [''],
});
}
});
}
// tslint:disable:typedef
ngOnInit() {
// Getting the list of counties
this.countyService.getCounties().subscribe((response) => {
this.counties = response;
this.newCustomerForm.patchValue({
county: response
});
if (!this.customerId) {
this.filteredCounties = this.newCustomerForm.get('county').valueChanges.pipe(
startWith(''),
map(value => typeof value === 'string' ? value : value.name),
map(value => this._filter(value))
);
}
}, error => {
this.counties = [
{
"countyId": 63,
"name": "Mombasa"
},
{
"countyId": 64,
"name": "Isiolo"
},
{
"countyId": 65,
"name": "Murang'a"
},
{
"countyId": 66,
"name": "Laikipia"
},
{
"countyId": 67,
"name": "Siaya"
},
{
"countyId": 68,
"name": "Kwale"
},
{
"countyId": 69,
"name": "Meru"
},
{
"countyId": 70,
"name": "Kiambu"
},
{
"countyId": 71,
"name": "Nakuru"
},
{
"countyId": 72,
"name": "Kisumu"
},
{
"countyId": 73,
"name": "Kilifi"
},
{
"countyId": 74,
"name": "Tharaka-Nithi"
},
{
"countyId": 75,
"name": "Turkana"
},
{
"countyId": 76,
"name": "Narok"
},
{
"countyId": 77,
"name": "Homa Bay"
},
{
"countyId": 78,
"name": "Tana River"
},
{
"countyId": 79,
"name": "Embu"
},
{
"countyId": 80,
"name": "West Pokot"
},
{
"countyId": 81,
"name": "Kajiado"
},
{
"countyId": 82,
"name": "Migori"
},
{
"countyId": 83,
"name": "Lamu"
},
{
"countyId": 84,
"name": "Kitui"
},
{
"countyId": 85,
"name": "Samburu"
},
{
"countyId": 86,
"name": "Kericho"
},
{
"countyId": 87,
"name": "Kisii"
},
{
"countyId": 88,
"name": "Taita-Taveta"
},
{
"countyId": 89,
"name": "Machakos"
},
{
"countyId": 90,
"name": "Trans Nzoia"
},
{
"countyId": 91,
"name": "Bomet"
},
{
"countyId": 92,
"name": "Nyamira"
},
{
"countyId": 93,
"name": "Garissa"
},
{
"countyId": 94,
"name": "Makueni"
},
{
"countyId": 95,
"name": "Uasin Gishu"
},
{
"countyId": 96,
"name": "Kakamega"
},
{
"countyId": 97,
"name": "Nairobi"
},
{
"countyId": 98,
"name": "Wajir"
},
{
"countyId": 99,
"name": "Nyandarua"
},
{
"countyId": 100,
"name": "Elgeyo-Marakwet"
},
{
"countyId": 101,
"name": "Vihiga"
},
{
"countyId": 102,
"name": "Mandera"
},
{
"countyId": 103,
"name": "Nyeri"
},
{
"countyId": 104,
"name": "Nandi"
},
{
"countyId": 105,
"name": "Bungoma"
},
{
"countyId": 106,
"name": "Marsabit"
},
{
"countyId": 107,
"name": "Kirinyaga"
},
{
"countyId": 108,
"name": "Baringo"
},
{
"countyId": 109,
"name": "Busia"
}
];
});
// Getting a list of sub-counties
this.subCountyService.getSubCounties().subscribe((response) => {
this.subCounties = response;
});
}
displayFn(county?: any): string | undefined {
return county ? county.name : undefined
}
private _filter(value) {
console.log('value: ', value);
const filterValue = value.toLowerCase();
return this.counties.filter(county => county.name.toLowerCase().indexOf(filterValue) === 0);
}
addCustomer(customer) {
console.log('Value of customer form before setting is: ', this.newCustomerForm.value);
const selectedCountyId = this.counties.filter(county => county.name === this.newCustomerForm.value.countyId)[0];
console.log('CountyId: ', selectedCountyId);
if (!selectedCountyId) {
PNotify.error({
title: 'Please selecte a valid county',
minHeight: '75px'
});
return;
}
this.newCustomerForm.patchValue({
countyId: selectedCountyId.countyId
});
customer.countyId = selectedCountyId.countyId;
console.log('Value of customer form after setting is: ', this.newCustomerForm.value);
this.dataSending = true;
this.customerService.addCustomer(customer).subscribe((response) => {
if (this.dialogView) {
PNotify.success({
title: 'Customer added Successfully',
minHeight: '75px'
});
this.dialogRef.close();
}
else {
PNotify.success({
title: 'Customer added Successfully',
text: 'Redirecting to list page',
minHeight: '75px'
});
this.dataSending = false;
document.getElementById('submitButton').style.display = 'initial';
this.router.navigate(['searchcustomer']);
}
}, (error) => {
console.log('Following error occured: ', error);
PNotify.error({
title: 'Error occured while adding customer',
text: 'Failed to add new customer',
minHeight: '75px'
});
this.dataSending = false;
});
}
updateCustomer(customer) {
this.dataSending = true;
this.updatedCustomer = this._formBuilder.group({
customerId: [this.customerId],
nationalId: [customer.nationalId],
name: [customer.name],
gender: [customer.gender],
phone1: [customer.phone1],
phone2: [customer.phone2],
countyId: [customer.countyId],
subCountyId: [customer.subCountyId],
address: [customer.address],
additionalInfo: [customer.additionalInfo],
bishopName: [customer.bishopName],
bishopPhone: [customer.bishopPhone],
input1: [customer.input1],
input2: [customer.input2],
});
this.customerService.updateCustomer(this.updatedCustomer.value, this.customerId).subscribe((response) => {
PNotify.success({
title: 'Customer updated Successfully',
text: 'Redirecting to list page',
minHeight: '75px'
});
this.dataSending = false;
this.router.navigate(['searchcustomer']);
}, (error) => {
console.log('An error occured while updating customer: ', error);
PNotify.error({
title: 'Error occured while updating customer',
text: 'Failed to update customer',
minHeight: '75px'
});
this.dataSending = false;
});
}
}
Pictures for better understanding
Upvotes: 0
Views: 1853
Reputation: 73377
If you feed the autocomplete an id, that is what it will show. In your first image where you say it works, it looks like it works, but in fact it is not storing the countyId
when a choice is made, but the name
property which you have set as value
. I would assume that it is actually the id you want stored there too.
The cleanest solution in my opinion, is to actually store the whole county object. So I would suggest the following:
<input type="text" matInput formControlName="county" [matAutocomplete]="auto">
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let county of filteredCounties | async" [value]="county">
{{county.name}}
</mat-option>
</mat-autocomplete>
with that I removed the countyId
formcontrol and instead added county
, which stores the whole object and added displayWith
to show the name
property of the county.
Then modify the valueChanges
to check if user has typed or it has been fed an object:
// add the correct name of your form
this.filteredCounties = this.myForm
.get("county")
.valueChanges.pipe(
startWith(""),
map(value => typeof value === 'string' ? value : value.name),
map(value => this._filter(value))
);
and the displayFn
which is shown in template:
// don't use 'any' !!
displayFn(county?: any): string | undefined {
return county ? county.name : undefined
}
So now is only left the option where you want to create the edit form. If you only have the countyId
property, you can use find
to add the correct object. Here in this sample I set it on creation, but you will maybe use setValue()
, but the idea is the same!
// replace 2 with the variable/prop you have the desired id
county: [this.counties.find(x => x.countyId === 2)],
So now you have the whole object. When submitting form and if you only want the countyId
from the form control, you can access it by: this.myForm.get('county').value.countyId
.
STACKBLITZ with the above code.
Upvotes: 1