Reputation: 20415
I've got an Angular 6, Reactive Forms project that connects to an ASP.NET Web Api 2 / MS SQL Server 2017 back end. I am in the middle of wiring up the front end and have come across a situation I have not yet encountered and can't find examples of how to accomplish:
Nested FormGroups whose object is represented by a select
element. What I have so far does not error and it seemingly selects the correct item in the drop down upon load. However, upon selection, it only updates one field and not the whole object.
I think I need to use formGroupName="company"
to tell registerOnChange()
what control to look for and then some form of formControlName="..."
, but I don't understand how do so with the select
or option
elements to achieve what I'm looking for. I need a selection to update the entire object, both the id and name properties.
Click here to view the StackBlitz editor/demo
...
<form class="form-horizontal" novalidate (ngSubmit)="save()" [formGroup]="siteForm">
<fieldset>
...
<div class="form-group" formGroupName="company">
<label class="col-md-2 control-label" for="companyId">Company</label>
<div class="col-md-8">
<select id="companyId" class="form-control" formControlName="company">
<option *ngFor="let company of companies" [ngValue]="company.name">
{{ company.name }}
</option>
</select>
</div>
</div>
...
</fieldset>
</form>
siteForm: FormGroup;
// TODO: refactor to use http Company Service instead
companies: Company[] = [
{ id: 1, name: 'Company 1'},
{ id: 2, name: 'Company 2'},
{ id: 3, name: 'Company 3'}
];
site: Site;
constructor(private fb: FormBuilder,
private siteService: SiteService) { }
ngOnInit(): void {
this.siteForm = this.fb.group({
...
company: this.fb.group({
id: null,
name: ''
}),
...
});
}
getSite(id: number): void {
this.siteService.getSite(id)
.subscribe(site => this.onSiteRetrieved(site));
}
save(): void {
if (this.siteForm.dirty && this.siteForm.valid) {
// copy form values over the site object values
const s = Object.assign({}, this.site, this.siteForm.value);
// simulate call to save service
console.log('Saved: ' + JSON.stringify(s));
}
}
onSiteRetrieved(site: Site): void {
// clear all the state flags (dirty, touched, etc.)
if (this.siteForm) {
this.siteForm.reset();
}
this.site = site;
// update the data on the form
this.siteForm.setValue({
...
company: {
id: this.site.company.id,
name: this.site.company.name
},
...
});
}
... more
Upvotes: 1
Views: 1574
Reputation: 20415
I've solved it by following Angular's SetControlValueAccessor Model-Driven Example where it uses object identity to select the option.
Click here to see StackBlitz demo
I removed formGroupName="company"
off the wrapper div, used formControlName="company"
on the select
which is the entire object, and then used [ngValue]="company"
on the option
which is also the entire object.
See below :
<div class="form-group">
<label class="col-xs-2 control-label" for="companyId">Company</label>
<div class="col-xs-8">
<select id="companyId"
class="form-control"
formControlName="company"
[compareWith]="compareCompanies">
<option *ngFor="let company of companies" [ngValue]="company">
{{ company.name }}
</option>
</select>
</div>
</div>
The only changes I needed to make in the class were :
company: this.fb.group({ id:
null, name: ''})
and replacing it with company: null
. compareWith
function. Needed because the default comparison uses object identity and different objects have different identities. You can override this method with your own implementation to determine uniqueness.See below :
ngOnInit(): void {
this.siteForm = this.fb.group({
name: ['', Validators.required],
company: null
});
...
}
compareCompanies(c1: Company, c2: Company): boolean {
return c1 && c2 ? c1.id === c2.id : c1 === c2;
}
Upvotes: 1
Reputation: 19913
since there is a lot of code to wire, I believe this is what you need,
<select formControlName="id">
<option *ngFor="let company of companies" [ngValue]="company.id">{{country.name}}</option>
</select>
The above solution is correct, but you need to do some changes in your stackblitz, I fixed it for you please take a look this
this.siteForm.patchValue
instead of setValue
please pay attention company name wont be updated, if you need to change company name as well you have to subscribe to change event and change either your form or change site
object and patch form again
Upvotes: 1