Reputation: 655
I try to make a dynamic form (so you can limitless add items to a list), but somehow the content of my list is not getting send because it can't find the control with path:
Cannot find control with path: 'list_items -> list_item'
My component:
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css']
})
export class ListComponent implements OnInit {
listForm: FormGroup;
constructor(private nodeService: NodeService, private messageService: MessageService, private fb: FormBuilder,
private listService: ListService) {
this.listForm = this.fb.group({
title: ['', [Validators.required, Validators.minLength(5)]],
list_items: this.fb.array([
this.initListItem(),
])
});
}
initListItem() {
return this.fb.group({
list_item: ['']
});
}
initListItemType() {
return this.fb.group({
list_item_type: ['']
});
}
addListItem() {
const control = <FormArray>this.listForm.controls['list_items'];
control.push(this.initListItem());
}
The Template (list.component.html):
<h2>Add List </h2>
<form [formGroup]="listForm" novalidate (ngSubmit)="addList(listForm)">
<div class="form-group">
<input type="text" class="form-control" formControlName="title" placeholder="Title">
</div>
<div formArrayName="list_items">
<div *ngFor="let list_item of listForm.controls.list_items.controls; let i=index" class="panel panel-default">
{{i + 1}}.) <input type="text" formControlName="list_item" placeholder="List Item" class="form-control">
</div>
<a (click)="addListItem()">Add List Item +</a>
</div>
<button type="submit">Submit</button>
</form>
The title works just fine, but I can't find the error I have with the "formControlName", which is causing the error.
Update with what I changed list.component.html
<h2>Add List </h2>
<form [formGroup]="listForm" novalidate (ngSubmit)="addList(listForm)">
<div class="form-group">
<input type="text" class="form-control" formControlName="title" placeholder="Title">
</div>
<div formArrayName="list_items">
<div *ngFor="let list_item of listForm.controls.list_items.controls; let i=index" class="panel panel-default">
{{i + 1}}.) <input type="text" formControlName="{{i}}" placeholder="List Item" class="form-control">
</div>
<a (click)="addListItem()">Add List Item +</a>
</div>
<button type="submit">Submit</button>
</form>
And in my component I changed the constructor and my addListItem method:
listForm: FormGroup;
constructor(private nodeService: NodeService, private messageService: MessageService, private fb: FormBuilder,
private listService: ListService) {
this.listForm = this.fb.group({
title: ['', [Validators.required, Validators.minLength(5)]],
list_items: this.fb.array([
[''],
])
});
}
addListItem() {
const control = <FormArray>this.listForm.controls['list_items'];
control.push(this.fb.control(['']));
console.log(control)
}
Upvotes: 62
Views: 160739
Reputation: 81
This is working in angular 14
[formGroupName]="i"
is MANDATORY using nested arrays.
getControls() {
return (<FormArray>this.signupForm.get('yourCustomInputsName')).controls;
}
you can use any name for controlname
<div
formArrayName="yourCustomInputsName"
*ngFor="let item of getControls(); let i = index">
<div [formGroupName]="i">
<input type="text" formControlName="nameOfyourControl">
</div>
</div>
Upvotes: 2
Reputation: 2661
-> Adding FormArray in component file
//TO USE FormArray (set form1:any;)
form1:any;
hobbies: new FormArray([
new FormControl()
])
-> To Add/Delete controls to/from hobbies
addHobbies(){
this.form1.get('hobbies').push(new FormControl());
}
removeHobbies(index:number){
this.form1.get('hobbies').removeAt(index);
}
-> Template File <!------- [FormArray] ADD HOBBIES------->
<div formArrayName="hobbies">
<label for="hobbies">Hobbies</label>
<div *ngFor="let hobby of form1.get('hobbies').controls;index as i">
<input type='text' id="hobbies" class="form-control" placeholder="hobbies" [formControlName]="i">
<button type="button" (click)="removeHobbies(i)">remove</button>
</div>
<button type="button (click)="addHobbies()">Add</button>
</div>
Upvotes: 2
Reputation: 2190
There should be a formControlName
in your HTML form mapped to your component file.
<div *ngFor="let list_item of [0,1,2]; let i=index" class="panel panel-default">
{{i + 1}}.) <input type="text" formControlName="{{i}}" placeholder="List Item" class="form-control">
</div>
list_items: this.fb.array([
[''], //0 points to this
[''], //1 points to this
[''] //2 points to this
])
Upvotes: 50
Reputation: 211
You must use the interpolation.
Instead of:
<div
class="form-group"
*ngFor="let hobbyControl of getControls(); let i = index">
<input type="text" class="form-control" formControlName="i">
</div>
You must use the interpolation with "formControlName":
<div
class="form-group"
*ngFor="let hobbyControl of getControls(); let i = index">
<input type="text" class="form-control" [formControlName]="i">
</div>
If you're a using a recent version of Angular, make sure to do the operations in the TypeScript file, instead of in the Web page, as shown below in the file "app.component.ts":
getControls() {
return (<FormArray>this.signupForm.get('hobbies')).controls;
}
Upvotes: 6
Reputation: 6668
A simple example with FormArray @ stackblitz (gist'd below)
app.component.ts
import { Component,OnInit } from '@angular/core';
import { FormGroup, FormControl, FormArray, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
fg: FormGroup;
constructor(private fb: FormBuilder){}
ngOnInit() {
this.fg = this.fb.group({
address: this.fb.group({
street: ['', Validators.required],
}),
aliases: this.fb.array([])
});
const fa = (this.fg.get('aliases')as FormArray);
this.addNewAlias();
}
addNewAlias(){
const fa = (this.fg.get('aliases')as FormArray);
fa.push(this.fb.group({
name: ['', Validators.required]
}));
}
deleteAlias(i:number){
const fa = (this.fg.get('aliases')as FormArray);
fa.removeAt(i);
if(fa.length===0) this.addNewAlias();
}
}
app.component.html
<form [formGroup]="fg" class="spaced">
<h3>Nested in Group:</h3>
<div formGroupName="address" class="spaced">
<label>
<input type="text" formControlName="street">
valid: {{fg.get('address').get('street')?.valid}}
</label>
</div>
<h3>Nested in Array:</h3>
<div formArrayName="aliases" *ngFor="let alias of fg.get('aliases').controls; let i = index;" class="border">
<div [formGroupName]="i">
<label>
Alias {{i+1}}:
<input formControlName="name" placeholder="Item name">valid: {{alias.get('name')?.valid}}
</label>
<button type="button" (click)="deleteAlias(i)">X</button>
</div>
</div>
<button type="button" (click)="addNewAlias()">add</button>
</form>
<div>form valid: {{fg?.valid}}</div>
Upvotes: 13
Reputation: 32010
Note that if your FormArray
contains other FormGroup
controls (which contain other instances of FormControl
), you'll need to do this to access the controls inside each FormGroup
:
<div *ngFor="let item of myFormArray.controls; let i=index">
<div formGroupName="{{i}}">
<input formControlName="myFormGroupSubControl1" />
<input formControlName="myFormGroupSubControl2" />
Upvotes: 43
Reputation: 3041
I also encountered this error, and I solved this problem by initiating the variables of class(elements in this.fb.array([])
).
Code Snippet
mobileNumbers: this.fb.array([this.fb.group(new MobileNumber('IN'))]),
Where class MobileNumber is used.
export class MobileNumber{
public country_code: string;
public mobile_number: string;
constructor($cc){
this.country_code = COUNTRY_CODES[$cc];
}
}
To
export class MobileNumber{
public country_code = '';
public mobile_number = '';
constructor($cc){
this.country_code = COUNTRY_CODES[$cc];
}
}
Upvotes: 2