Home > other >  open single form array control in material modal using Reactive form
open single form array control in material modal using Reactive form

Time:09-22

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);
    });
  }
  • Related