I'm trying to accomplish something like this
I have a list of plans to subscribe
{ id: 1, data: '1GB', minsms: '5.000', value: 7.5, simCards: 1 },
{ id: 2, data: '3GB', minsms: '5.000', value: 10, simCards: 3 },
{ id: 3, data: '10GB', minsms: '5.000', value: 15, simCards: 2 },
{ id: 4, data: '20GB', minsms: '5.000', value: 20, simCards: 1 },
The client can choose how many sim cards per plan and I want to put a validation (the client needs to select even if is zero cards)
I tried to use FormArray but I'm stuck. Can someone help me?
The code (I wrote a new one just for the question) https://stackblitz.com/edit/angular-ivy-mwvat5
Thanks
CodePudding user response:
you has an array of plans, and you want to manage an array of "simCards". As you only need one input, you need an FormArray of FormControls.
See that you only need an array (**), so you can simple define
simCards:FormArray
You can subscribe(*) in .ts to create the FormArray
plans:any[]
ngOnInit()
{
this._plansService.getPlans().subscribe(res=>{
this.plans=res;
this.simCards=new FormArray(res.map(_=>new FormControl(0)))
})
}
And create a function to get the control of the formArray
getControl(index)
{
return this.simCards.at(index) as FormControl
}
And you use
<!--see that iterate over simCards.controls-->
<mat-form-field *ngFor="let control of simCards.controls;let i=index">
<mat-label
>Num of SIM cards for {{ plans[i].data }} / {{ plans[i].minsms }} at
{{ plans[i].value | currency: 'EUR' }}</mat-label
>
<input matInput type="text" [formControl]="getControl(i)" />
</mat-form-field>
Be carefull, the value of the simCars is only an array of "numbers" (really string), so, in submit you need make some like
submit()
{
const values=this.plans.map((x:any,index:number)=>{
id:x.id,
simsCards:this.simCards.value[index]
})
this._plansService.save(values)
}
(*)don't forget unsubscribe!
You can also choose create a formArray of FormGroups with two properties: id and simsCards. In this case you can use
ngOnInit()
{
this._plansService.getPlans().subscribe(res=>{
this.plans=res;
this.simCards=new FormArray(res.map((x:any)=>new FormGroup({
id:new FormControl(x.id),
simCards:new FormControl(0)
})
))
})
}
You create a function getGroup
getGroup(index)
{
return this.simCards.at(index) as FormGroup
}
And use
<mat-form-field *ngFor="let control of simCards.controls;let i=index" [formGroup]="getGroup(i)">
<mat-label
>Num of SIM cards for {{ plans[i].data }} / {{ plans[i].minsms }} at
{{ plans[i].value | currency: 'EUR' }}</mat-label
>
<input matInput type="text" formControlName="simCards" />
</mat-form-field>
See that, in this case this.simCards.value give us an array of object with id and simCards
Finally, If we want to use pipe async, instead of subscribe, we can use the "tap" to create the formArray
ngOnInit(){
this.plans$ = this._plansService.getPlans().pipe(
tap((plan: Plan[]) => {
this.simCards = new FormArray(
plan.map(
(x: any) =>
new FormGroup({
id: new FormControl(x.id),
simCards: new FormControl(0),
})
)
);
})
);
}
The .html is very similar to the above
<mat-form-field *ngFor="let plan of plans$ | async;let i=index" [formGroup]="getGroup(i)">
<mat-label
>Num of SIM cards for {{ plan.data }} / {{ plan.minsms }} at
{{ plan.value | currency: 'EUR' }}</mat-label
>
<input matInput type="text" formControlName="simCards" />
</mat-form-field>
NOTE: you can also define a FormGroup with a formArray inside in the way
form=new FormGroup({
simCards:new FormArray([])
})
And, as always we use a FormArray use a getter
get simCards()
{
return this.form.get('simCards') as FormArray
}
This allow us avoid use the function getGroup() and getControl() enclosed the mat-forms in a form
In case a FormArray of FormControls
<form [formGroup]="form">
<div formArrayName="simCards">
<mat-form-field
*ngFor="let plan of plans; let i = index"
>
<mat-label>...</mat-label
>
<input matInput type="text" [formControlName]="i" />
</mat-form-field>
</div>
</form>
ngOnInit()
{
this._plansService.getPlans().subscribe(res=>{
this.plans=res;
this.form=new FormGroup({
simCards:new FormArray(res.map(_=>new FormControl(0)))
})
})
}
In case a FormArray of FormGroups
<form [formGroup]="form">
<div formArrayName="simCards">
<mat-form-field *ngFor="let control of simCards.controls;let i=index"
[formGroupName]="i" >
<label>...</label>
<input matInput formControlName="simCards" type="text" />
</mat-form-field>
</div>
</form>
ngOnInit()
{
this._plansService.getPlans().subscribe(res=>{
this.plans=res;
this.form=new FormGroup({
simCards:new FormArray(res.map((x:any)=>new FormGroup({
id:new FormControl(x.id),
simCards:new FormControl(0)
})
))
})
})
}
In case use pipe async we need enclosed the above form in a *ngIf in the way
<div *ngIf="{ plan: plans$ | async } as data">
<form [formGroup]="form">
<div formArrayName="simCards">
<mat-form-field
*ngFor="let control of simCards.controls; let i = index"
[formGroupName]="i"
>
<mat-label
>Num of SIM cards for {{ data.plan[i].data }} /
{{ data.plan[i].minsms }} at
{{ data.plan[i].value | currency: 'EUR' }}</mat-label
>
<input matInput type="text" formControlName="simCards" />
</mat-form-field>
</div>
</form>
</div>
this.plans$ = this._plansService.getPlans().pipe(
tap((plan: Plan[]) => {
this.form=new FormGroup({
simCards: new FormArray(
plan.map(
(x: any) =>
new FormGroup({
id: new FormControl(x.id),
simCards: new FormControl(0),
})
)
)
})
})
);