EDIT
The first part of the problem was solved thanks to the answer of Chellappan above, but i am encountering other problems, that made me do a stackblitz that can be found here : https://stackblitz.com/edit/angular-ivy-bzh3dh
The problems are that i can't retrieve correctly the list of products selected from the form cause they are all outputed :
"courses": [
{
"courseName": "Dessert",
"products": [
{
"product": true
},
{
"product": false
},
{
"product": null
}
]
I don't know how to be able to retrieve something like:
"courses": [
{
"courseName": "Dessert",
"products": [
{
"Crumble aux pommes": true
},
{
"Mousse au chocolat maison": false
},
{
"Riz au lait": false
}
]
And the second problem is that the labels of my checkboxes are not independent. If i add a new 'course' and that i select some products, the labels of all checkboxes in other courses are changed too.
I hope that my post is not too messy, i don't edit messages so often on Stack Overflow
END OF EDIT
I have a reactive form with nested form Array to describe a menu that contains several courses and each course contans several products, but i can't figure out how to make it works. My actual problem is that i don't manage to retrieve my second level of controls.
Here is the json structure of the menu:
{
"name": "",
"description": "",
"price": "",
"courses": [
{
"courseName": "",
"products": [
{
"product": ""
}
]
}
]
}
I followed this project as an example :
Here is my simplified form for readability purpose :
<form [formGroup]="menuForm" (ngSubmit)="onSubmit(menuForm)">
<div class="form-group">
<input
class="form-control"
formControlName="name"
type="text">
</div>
<div class="form-group">
<textarea
class="form-control"
formControlName="description"></textarea>
</div>
<div class="col-md-3 form-group">
<input
class="form-control"
formControlName="price"
type="number">
</div>
</div>
<div class="row col-md-12">
<button class="btn btn-outline-dark" type="button" (click)="onAddCourse()">
Add course
</button>
</div>
<div class="row" formArrayName="courses">
<div *ngFor="let course of getCourses(menuForm); let i = index"
[formGroupName]="i">
<div class="...">
<select
class="form-control"
formControlName="courseName">
<option *ngFor="let course of courses">{{course.name}}</option>
</select>
<button type="button" class="mt-2 btn btn-outline-dark" (click)="onLoadProducts(i)">
Load Products // <- will load products that have course Name selected form the input 'courseName'above
</button>
<div class="row" formArrayName="products">
<div class="col-md-12"
*ngFor="let product of getProducts(menuForm); let j = index"
[formGroupName]="j">
<input class="form-check-input" type="checkbox" id="product{{i}}-{{j}}"
formControlName="product"/>
<label class="form-check-label" for="product{{i}}-{{j}}">product</label>
</div>
</div>
</div>
</div>
</div>
<br>
<div class="row col-md-12">
<button class="btn btn-success mr-1" type="submit" [disabled]="!menuForm.valid">{{buttonSubmitLabel}}</button>
</div>
</form>
And here is my form initialisation in TS :
export class MenuEditComponent implements OnInit {
menuForm: FormGroup;
menu: MenuModel;
categories: CategoryModel[];
...
constructor(private router: Router,
private route: ActivatedRoute,
private productService: ProductService,
private menuService: MenuService) { }
ngOnInit(): void {
this.productService.getCategories().subscribe(
result => {
this.categories = result;
},
error => {
console.log(error);
this.categories = [];
});
this.initForm();
}
initForm() {
this.menuForm = new FormGroup({
name: new FormControl('', [Validators.required, Validators.maxLength(100)]),
description: new FormControl('', Validators.maxLength(255)),
price: new FormControl('', [Validators.required, Validators.min(0)]),
courses: new FormArray([
this.initCourse(),
])
});
}
initCourse() {
return new FormGroup({
courseName: new FormControl(''),
products: new FormArray([
this.initProduct()
])
});
}
initProduct() {
return new FormGroup({
product: new FormControl('')
});
}
getCourses(form) {
return form.controls.courses.controls;
}
getProducts(form) {
return form.controls.products.controls;
}
onAddCourse() {
const control = this.menuForm.get('courses') as FormArray;
control.push(this.initCourse());
}
onLoadProducts(i: number) {
console.log(this.menuForm.controls.courses);
const courseControl = this.menuForm.get('courses') as FormArray;
const course = courseControl.at(i).get('courseName').value;
console.log(course);
const products: ProductModel[] = this.mapProducts.get(course);
console.log(products);
const productControl = this.menuForm.get('courses').get[i].get('products') as FormArray;
onSubmit(menuForm: FormGroup) {
...
}
...
}
If i leave the backend like that, i have an error :
Cannot read property 'controls' of undefined at MenuEditComponent.getProducts
So my 'products' FormArray is not initialized. If i do :
getProducts(form) {
return form.controls.products?.controls;
}
i don't have the error anymore, but i can't access the products formControls. If i try :
onLoadProducts(i: number) {
const courseControl = this.menuForm.get('courses') as FormArray;
const course = courseControl.at(i).get('courseName').value;
const products: ProductModel[] = this.mapProducts.get(course);
const productControl = this.menuForm.get('courses').get[i].get('products') as FormArray;
//On the last line i get a 'Cannot read property 'get' of undefined'
...
}
What am i doing wrong ? Why my 'products' FormArray is not initialized ?
Thanks a lot for your help !
