Reputation: 1527
Unable to patch values to FormArray resultList
.
Anybody can please explain me, what i'm missing?
TS File:
import { Component, OnInit } from '@angular/core';
import { Student } from '../student';
import { FormGroup, FormControl, Validators, FormArray } from '@angular/forms';
@Component({
selector: 'app-container',
templateUrl: './container.component.html',
styleUrls: ['./container.component.css']
})
export class ContainerComponent implements OnInit {
studList: Student[] = [];
myform: FormGroup = new FormGroup({
firstName: new FormControl('', [Validators.required, Validators.minLength(4)]),
lastName: new FormControl(),
gender: new FormControl('male'),
dob: new FormControl(),
qualification: new FormControl(),
resultList: new FormArray([])
});
onSave() {
let stud: Student = new Student();
stud.firstName = this.myform.get('firstName').value;
stud.lastName = this.myform.get('lastName').value;
stud.gender = this.myform.get('gender').value;
stud.dob = this.myform.get('dob').value;
stud.qualification = this.myform.get('qualification').value;
this.studList.push(stud);
this.myform.controls.resultList.patchValue(this.studList);
console.log(JSON.stringify(this.studList));
}
ngOnInit() {
}
}
Model:
export class Student {
public firstName: String;
public lastName: string;
public gender: string;
public dob: string;
public qualification: string;
}
HTML:
<div class="container">
<h3>Striped Rows</h3>
<table class="table table-striped" formArrayName="resultList">
<thead>
<tr>
<th>Firstname</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of myform.controls.resultList.controls; let i = index" [formGroupName]="i">
<td><p formControlName="firstName"></p></td>
</tr>
</tbody>
</table>
</div>
this.studList JSON:
[
{
"firstName":"santosh",
"lastName":"jadi",
"gender":"male",
"dob":"2018-03-31T18:30:00.000Z",
"qualification":"BE"
},
{
"firstName":"santosh",
"lastName":"jadi",
"gender":"male",
"dob":"2018-03-31T18:30:00.000Z",
"qualification":"BE"
}
]
Upvotes: 19
Views: 27538
Reputation: 4808
First create your form based on your retrieved data structure then use patchValue
;
the form needs to contain all form controls before applying patchValue
.
let's say your data has a property containing an array of string you do
pseudo-code:
for ( const item of data.myArray ) {
form.controls.myArray.push( new FormControl('') )
}
then
form.patchValue(data);
this example doesn't make mush sense as you could directly initialize the data inside the for
loop but for more complex forms this will make you avoid parsing through all keys an data types to initialize the values.
Upvotes: 0
Reputation: 498
I am using the formgroup
in formarray
as:
this.formGroup = new FormGroup({
clientCode: new FormControl('', []),
clientName: new FormControl('', [Validators.required, Validators.pattern(/^[a-zA-Z0-9 _-]{0,50}$/)]),
type: new FormControl('', [Validators.required]),
description: new FormControl('', []),
industry: new FormControl('', []),
website: new FormControl('', [Validators.required, Validators.pattern(this.settings.regex.website)]),
businessEmail: new FormControl('', [Validators.pattern(this.settings.regex.email)]),
clients: this._formBuilder.array([this._formBuilder.group({
contactPerson: new FormControl('', [Validators.required]),
contactTitle: new FormControl('', [Validators.required]),
phoneNumber: new FormControl('', [Validators.required, Validators.pattern(this.settings.regex.phone)]),
emailId: new FormControl('', [Validators.required, Validators.pattern(this.settings.regex.email)]),
timeZone: new FormControl('', [Validators.required, Validators.pattern(this.settings.zipCode), Validators.minLength(5), Validators.maxLength(12)])
})])
})
For patch value I am using below method as:
let control = _this.formGroup.get('clients') as FormArray
clients.forEach(ele => {
control.push(_this._formBuilder.group({
contactPerson: new FormControl(ele.client_name, [Validators.required]),
contactTitle: new FormControl(ele.contact_title, [Validators.required]),
phoneNumber: new FormControl(ele.phone_number, [Validators.required, Validators.pattern(_this.settings.regex.phone)]),
emailId: new FormControl(ele.email_id, [Validators.required, Validators.pattern(_this.settings.regex.email)]),
timeZone: new FormControl(ele.timezone, [Validators.required, Validators.pattern(_this.settings.zipCode), Validators.minLength(5), Validators.maxLength(12)])
}))
});
Using this method we can validate the nested field as well.
Hope this may help.
Upvotes: 0
Reputation: 2141
I'd prefer using FormBuilder
to create form.
export class ComponentName implements OnInit {
form: FormGroup;
constructor(private fb: FormBuilder){}
ngOnInit() {
this.buildForm();
}
buildForm() {
this.form = this.fb.group({
firstName: '',
lastName: '',
...
resultList: this.fb.array([])
});
}
}
I believe the studlist will be obtained through API call as an observable rather than an static array. Let's assume, we data as follow.
resultModel =
{
firstName: "John",
lastName: "Doe",
....
resultList: [
{
prop1: value1,
prop2: value2,
prop3: value3
},
{
prop1: value1,
prop2: value2,
prop3: value3
}
...
]
}
Once the data is available, we can patch the values as follow:
patchForm(): void {
this.form.patchValue({
firstName: this.model.firstName,
lastName: this.model.lastName,
...
});
// Provided the FormControlName and Object Property are same
// All the FormControls can be patched using JS spread operator as
this.form.patchValue({
...this.model
});
// The FormArray can be patched right here, I prefer to do in a separate method
this.patchResultList();
}
// this method patches FormArray
patchResultList() {
let control = this.form.get('resultList') as FormArray;
// Following is also correct
// let control = <FormArray>this.form.controls['resultList'];
this.resultModel.resultList.forEach(x=>{
control.push(this.fb.group({
prop1: x.prop1,
prop2: x.prop2,
prop3: x.prop3,
}));
});
}
Upvotes: 2
Reputation: 691
First try with this steps and make sure are you on correct way
Because in your scenario you are patching the object to formArray ,so you have to parse that object first & check once have you imported ReactiveFormsModule in your app.module.ts.
Upvotes: 6
Reputation: 4798
By your question you want to add new Student
to resultList
.
First of all, you need to know FormArray
is an array of AbstractControl.
You can add to array only type of AbstractControl not other.
To simplify task prefer to use FormBuilder
:
constructor(private fb: FormBuilder) {}
createForm() {
this.myform = this.fb.group({
firstName: ['', [Validators.required, Validators.minLength(4)]],
lastName: [],
gender: ['male'],
dob: [],
qualification: [],
resultList: new FormArray([])
});
}
As you can see before filling resultList FormArray
, it's mapped to FormGroup:
onSave() {
let stud: Student = new Student();
stud.firstName = 'Hello';
stud.lastName = 'World';
stud.qualification = 'SD';
this.studList.push(stud);
let studFg = this.fb.group({
firstName: [stud.firstName, [Validators.required, Validators.minLength(4)]],
lastName: [stud.lastName],
gender: [stud.gender],
dob: [stud.dob],
qualification: [stud.qualification],
})
let formArray = this.myform.controls['resultList'] as FormArray;
formArray.push(studFg);
console.log(formArray.value)
}
FormBuilder - Creates an AbstractControl from a user-specified configuration.
It is essentially syntactic sugar that shortens the new FormGroup(), new FormControl(), and new FormArray() boilerplate that can build up in larger forms.
Also, in html formControlName bound to <p>
element, It's not an input and you can't bind to not form elements like div/p/span...:
<tbody>
<tr *ngFor="let item of myform.controls.resultList.controls; let i = index" [formGroupName]="i">
<td><p formControlName="firstName"></p></td> <==== Wrong element
</tr>
</tbody>
So, I think you just want to show added students in table. Then iterate over studList and show it's value in table:
<tbody>
<tr *ngFor="let item of studList; let i = index" [formGroupName]=i>
<td>
<p> {{item.firstName}} </p>
</td>
</tr>
</tbody>
Take care when patching array. Because patchValue of FormArray patches values by the index:
patchValue(value: any[], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
value.forEach((newValue: any, index: number) => {
if (this.at(index)) {
this.at(index).patchValue(newValue, {onlySelf: true, emitEvent: options.emitEvent});
}
});
this.updateValueAndValidity(options);
}
So, code below it patches the element at index=0: First indexed value of this.myform.controls['resultList'] as FormArray
will be replaced with:
let stud1 = new Student();
stud1.firstName = 'FirstName';
stud1.lastName = 'LastName';
stud1.qualification = 'FFF';
formArray.patchValue([stud1]);
Your case doesn't work because patchValue requires some controls in array. In your case there is no controls in array. Look source code.
Upvotes: 26
Reputation: 176936
you have to co like this , code is taken from angular.io, you need to do setcontrol that will do or go though link there is code for the same it makes uses of Address array
this.setAddresses(this.hero.addresses);
setAddresses(addresses: Address[]) {
const addressFGs = addresses.map(address => this.fb.group(address));
const addressFormArray = this.fb.array(addressFGs);
this.heroForm.setControl('secretLairs', addressFormArray);
}
Upvotes: 4
Reputation: 10157
Array does not contain patchValue
method. You have to iterate over controls and patchValue
each of them separately.
Upvotes: 1