I'm trying to create a form that contains a list of select controls (just for the sake of simplicity).
I'm not going to add new selects to the form, I just want to keep track of the selected option in each of the select controls. This is the json I use to generate the select controls:
sports = [
{
sportId: '1',
desc: 'Football',
categories: [
{
categId: '1',
desc: 'U-18'
},
{
categId: '1',
desc: 'U-20'
},
{
categId: '2',
desc: 'U-23'
}
]
},
{
sportId: '2',
desc: 'Volleyball',
categories: [
{
categId: '3',
desc: 'Junior'
},
{
categId: '4',
desc: 'Youth'
}
]
},
{
sportId: '3',
desc: 'Tennis',
categories: [
{
categId: '5',
desc: 'Singles'
},
{
categId: '6',
desc: 'Doubles'
}
]
}
];
And this is the html of my form
<form [formGroup]="registrationForm"(submit)="save()">
<div class="form-group">
<label for="firstname">First Name</label>
<input
type="text"
class="form-control"
id="firstname"
formControlName="firstName"
placeholder="First Name"
/>
</div>
<div class="form-group">
<label for="lastname">Last Name</label>
<input
type="text"
class="form-control"
formControlName="lastName"
id="lastname"
placeholder="Last Name"
/>
</div>
<div class="form-group" *ngFor="let sport of sports; let i = index">
<label attr.for="sport_{{i}}">{{sport.desc}}</label>
<select class="form-control" id="sport_{{i}}">
<option value="0">Select one</option>
<option *ngFor="let cat of sport.categories" [value]="cat.categId">{{cat.desc}}</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Building the form for the firstName and lastName is straightforward:
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
this.registrationForm = this.fb.group({
firstName: '',
lastName: ''
});
}
But I'm not sure about how to keep track of the selected options on each of the different select controls.
I'd really appreciate it if you could help me here.
Here's a demo
Each control that you want to keep track of could have an entry in the Form Group:
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
this.registrationForm = this.fb.group({
firstName: '',
lastName: '',
// <- Each additional control should be listed here
football: '',
volleyball: '',
tennis: ''
});
}
Think of the Form Group as the set of data you want to keep track of. So if you said you were not interested in dynamically adding sports, just keeping track of their values, then each of their values becomes an entry in the Form Group.
You can find a "brute force" style solution here: https://stackblitz.com/edit/angular-i7ntj3
If you want to dynamically add the controls (even though you don't plan to add more), you can use a FormArray as shown in your example. It looks like you were primarily missing the [formControlName]
property and the code populating the FormArray.
Component
registrationForm: FormGroup;
get categoryOptions(): FormArray {
return <FormArray>this.registrationForm.get('options');
}
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.registrationForm = this.fb.group({
firstName: '',
lastName: '',
options: this.fb.array([])
});
// Add the sports to the FormArray
this.sports.forEach(s =>
this.categoryOptions.push(new FormControl()));
}
save() {
console.log(JSON.stringify(this.registrationForm.value));
}
NOTE: The FormGroup needs to contain all of the form controls you want to track. In the above example, we track two text fields and a FormArray. A FormArray contains either a set of Form Controls or a set of Form Groups. And since those Form Array elements are not being added dynamically, we need to create them manually. That's the purpose of the forEach
in the code above.
Template
<div formArrayName="options">
<div *ngFor="let sport of sports; let i=index">
<label attr.for="i">{{sport.desc}}</label>
<select class="form-control" id="i" [formControlName]="i">
<option value="0">Select one</option>
<option *ngFor="let cat of sport.categories" [value]="cat.categId">{{cat.desc}}
</option>
</select>
</div>
</div>
NOTE: The formArrayName must be set to the name of the FormArray as defined in the FormGroup. The ngFor loops through each sport and sets up an index that will map to each element of the array. The formControlName
for each select element is then set to the array index.
You can find this FormArray solution here: https://stackblitz.com/edit/angular-reactive-select-deborahk
Hope this helps.