i am facing an issue, i am building a from with list of students ( formarray ). when clicked on edit of a single form array control, i want to open material modal to edit those fields & complete the student information. The issue i am facing is that as modal fields are not part of formgroup, its giving an error. How can i use 1 single control in that modal.
<div [formGroup]="form" >
<div formArrayName="students">
<button type="button" (click)="addStudent()"> Add </button>
<div *ngFor="let control of students.controls; let i = index">
<!-- All other fields -->
<button type="button" aria-label="Close" (click)="openModal(studentModal, i)">
close </button>
</div>
</div>
</div>
<ng-template #studentModal let-modal>
<div >
<button type="button" aria-label="Close" (click)="modal.dismiss('Cross click')">
<span aria-hidden="true"><i >{{'close' | translate}}</i></span>
</button>
</div>
<div >
<div >
<div >
<div >
<!-- Display single formarray control -->
</div>
</div>
</div>
</div>
</ng-template>
Meanwhile component class is
this.form = this.fb.group({
students: this.fb.array([])
});
get students(): FormArray {
return this.form.get('students') as FormArray;
}
addStudent(): void {
this.sensors.push(this.fb.group({
studentUniqueID: new FormControl(),
name: new FormControl(''),
grade: new FormControl('')
}),
]));
}
openModal(content, id): void {
this.toEditStudentControl = this.students.controls[id];
this.editIndex = id;
this.modalService.open(content, { size: 'sm' });
}
CodePudding user response:
you can use the "data" to pass the formGroup
If you has a .html like
<form [formGroup]="form">
<div formArrayName="students">
<div
*ngFor="let group of students.controls;let i=index"
[formGroupName]="i"
>
<input formControlName="name" />
<!--see you pass to openDialog, the template and the index-->
<button mat-button (click)="openDialog(template,i)">Open dialog</button>
</div>
</div>
</form>
<!--see the "let-data"-->
<ng-template #template let-data>
<form [formGroup]="data.group">
<input formControlName="name">
</form>
<!--see that in close we return the data.group.value-->
<button mat-button [mat-dialog-close]="data.group.value" cdkFocusInitial>Ok</button>
</ng-template>
This allow you use some like
/**WRONG APROACH**/
openDialog(template:any,index:number) {
const dialogRef=this.dialog.open(template, {
data: {
group: this.students.at(index),
},
});
dialogRef.afterClosed().subscribe(result => {
(this.students.at(index) as FormGroup).patchValue(result)
});
}
See that the formArray has already change, but it's necesary use a patchValue to show the data in the input.
Well, really it's not a good idea pass the formGroup else a copy. So, if we cancel our FormArray not change
So, we are changing a bit your function addStudent and split in addStudent and createStudent
addStudent()
{
this.students.push(this.createStudent());
}
createStudent(data: any = null): FormGroup {
data = data || { studentUniqueID: 0, name: '', grade: '' };
return new FormGroup({
studentUniqueID: new FormControl(data.studentUniqueID),
name: new FormControl(data.name),
grade: new FormControl(data.grade),
});
}
Now, our openDialog becomes like
/*GOOD APPROACH**/
openDialog(template: any, index: number) {
const dialogRef = this.dialog.open(template, {
data: {
group: this.createStudent(this.students.at(index).value),
},
});
dialogRef.afterClosed().subscribe((result) => {
(this.students.at(index) as FormGroup).patchValue(result);
});
}
See stackblitz
IMPORTANT NOTE: Really If we only want to change the elements in the dialog, really don't need a FormArray. We can use a simple array of objects
<div *ngFor="let st of studentList;let i=index">
name:{{st.name}}<button mat-button (click)="openDialog2(template,i)">Open dialog</button>
</div>
openDialog2(template: any, index: number) {
const dialogRef = this.dialog.open(template, {
data: {
group: this.createStudent(this.students.at(index).value),
},
});
dialogRef.afterClosed().subscribe((result) => {
this.studentList[index]=result;
});
}
CodePudding user response:
You can use the "data" to pass the control or create a new control with the data (see the reason in the another response)
<ng-template #template let-data>
<input [formControl]="data.control" />
<button mat-button [mat-dialog-close]="data.control.value" cdkFocusInitial>
Ok
</button>
</ng-template>
<ng-template #template let-data>
<input [formControl]="data.control" />
<button mat-button [mat-dialog-close]="data.control.value" cdkFocusInitial>
Ok
</button>
</ng-template>
The .ts:
openDialog(template: any, index: number) {
const dialogRef = this.dialog.open(template, {
data: {
control: new FormControl(this.students.at(index).get('address')?.value),
},
});
dialogRef.afterClosed().subscribe((result) => {
const control=this.students.at(index).get('address');
control && control.patchValue(result);
});
}
the stackblitz
NOTE: Also you can pass only a variable and use ngModel
<ng-template #template2 let-data>
<input [(ngModel)]="data.value" />
<button mat-button [mat-dialog-close]="data.value" cdkFocusInitial>
Ok
</button>
</ng-template>
openDialog2(template: any, index: number) {
const dialogRef = this.dialog.open(template, {
data: {
value: this.students.at(index).get('address')?.value,
},
});
dialogRef.afterClosed().subscribe((result) => {
const control=this.students.at(index).get('address');
control && control.patchValue(result);
});
}