Search code examples
htmlangulartypescriptangular-forms

FormGroup in FormArray containing object displaying [object Object]


I'm working on a prototype for an editor sending data according to a model to an api. Here's the model :

title: string,
desc: string,
pairs: [
  {
    left: { label: string },
    right: { label: string }
  },
  ...
]

The problem here is that I need to work with angular forms and that I can't find a way to display/use left.label and right.label.

For now, my code (shown below) works in typescript, as it's initializing the form with given values (some mock data according to the model). If I log or print my formGroup after it's been given values, it shows exactly as it should.

But my main problem is in the html of my component. Even though my formGroup is formatted well, I can't seem to use label properties (and of course, I want to edit them).

I think it's because when I create a pair in my formArray, I set only left and right properties as formControl in the group.

Here is my component.ts:

export class LinksEditorComponent implements OnInit {
  @Input() exercise;
  exerciseForm: FormGroup;

  get pairs(): FormArray {
    return this.exerciseForm.get('pairs') as FormArray;
  }

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    if (!this.exercise.title || !this.exercise.desc || !this.exercise.pairs) {
      this.exercise = {
        title: '',
        desc: '',
        pairs: []
      };
    }
    this.initExerciseForm();
  }

  initExerciseForm() {
    this.exerciseForm = this.formBuilder.group({
      title: [this.exercise.title, Validators.required],
      desc: [this.exercise.desc, Validators.required],
      pairs: this.formBuilder.array(
        this.exercise.pairs.map(pair => this.createPair(pair)),
        Validators.required
      )
    });
  }

  createPair(pair: any): FormGroup {
    return this.formBuilder.group({
      left: [{ label: pair.left.label }, Validators.required],
      right: [{ label: pair.right.label }, Validators.required]
    });
  }

  onAddPair() {
    this.pairs.push(
      this.formBuilder.group({
        left: [{ label: '' }, Validators.required],
        right: [{ label: '' }, Validators.required]
      })
    );
  }
}

And here is my component.html:

<mat-card class="links-editor" *ngIf="exercise">
  <form
    fxLayout="column"
    fxLayoutAlign="space-between center"
    [formGroup]="exerciseForm"
    (ngSubmit)="onSubmit()"
    fxLayout="column"
  >
    <mat-form-field appearance="standard">
      <mat-label>Title</mat-label>
      <input matInput formControlName="title" />
    </mat-form-field>
    <mat-form-field appearance="standard">
      <mat-label>Description</mat-label>
      <input matInput formControlName="desc" />
    </mat-form-field>
    <mat-list formArrayName="pairs">
      <div *ngFor="let pairControl of pairs.controls; let i = index">
        <div [formGroupName]="i">
          <mat-form-field appearance="standard">
            <mat-label>Left chip</mat-label>
            <input matInput formControlName="left" />
          </mat-form-field>
          <mat-form-field appearance="standard">
            <mat-label>Right chip</mat-label>
            <input matInput formControlName="right" />
          </mat-form-field>
        </div>
      </div>
      <button type="button" mat-mini-fab color="accent" (click)="onAddPair()">
        <mat-icon>add</mat-icon>
      </button>
    </mat-list>
    <button type="submit" mat-fab color="accent" [disabled]="exerciseForm.invalid">
      <mat-icon>done</mat-icon>
    </button>
  </form>
</mat-card>

If I try in the HTML file to set <input formControlName="left.input">, I've got an error in the console, and the data isn't displaying. And of course, if I set <input formControlName="left">, I got... Well left's value : [Object object].


Solution

  • Okay I found the solution ! I didn't understood I could just create a FormGroup inside a FormGroup. So here's how my code looks like now, in case someone needed help :

    component.ts pair creation

     createPair(pair: any): FormGroup {
        return this.formBuilder.group({
          left: this.formBuilder.group({ label: [pair.left.label, Validators.required] }),
          right: this.formBuilder.group({ label: [pair.right.label, Validators.required] })
        });
      }
    
      onAddPair() {
        this.pairs.push(
          this.formBuilder.group({
            left: this.formBuilder.group({ label: ['', Validators.required] }),
            right: this.formBuilder.group({ label: ['', Validators.required] })
          })
        );
      }
    

    component.html display

    <mat-list formArrayName="pairs">
          <div *ngFor="let pairControl of pairs.controls; let i = index">
            <div [formGroupName]="i">
              <mat-form-field appearance="standard" formGroupName="left">
                <mat-label>Left chip</mat-label>
                <input matInput formControlName="label" />
              </mat-form-field>
              <mat-form-field appearance="standard" formGroupName="right">
                <mat-label>Right chip</mat-label>
                <input matInput formControlName="label" />
              </mat-form-field>
            </div>
          </div>
          <button type="button" mat-mini-fab color="accent" (click)="onAddPair()">
            <mat-icon>add</mat-icon>
          </button>
    </mat-list>