FormArray don't find my formControlName in an object

I don't know why but my placeholder work perfectly. So I can see the name of my music, but if I want to use my formControleName my error is : Cannot find control with path: musics -> name

This is my form :

this.musicsForm = new FormGroup({
   musics: new FormArray([
     new FormControl(new Music('Musique 1', null, null)),
     new FormControl(new Music('Musique 2', null, null)),
   ]),
 });

And my object is like that :

export class Music {
  _id: string;
  name: string;
  fileUrl: string;
  feat: string; // i don't use it right now
  index: number; // i don't use it right now

  constructor(name: string, fileUrl: string, feat: string, index?: number) {
    this.name = name;
    this.fileUrl = fileUrl;
    this.feat = feat;
    this.index = index;
  }
}

And this is my HTML :

 <form [formGroup]="musicsForm" cdkDropList class="col-md-11 px-0" (cdkDropListDropped)="drop($event)">
    <class="example-box" *ngFor="let m of musicsForm.get('musics').controls; let i = index" cdkDrag>
      <div class="item row my-4 w-100" [formGroupName]=i>
        ...
        <div class="col-md-4 mt-1">
          <input class="px-2 w-100" type="text" [placeholder]="m.value.name" formControlName="name" > // I think the bug is just here
        </div>
        ...
      </div>
    </div>
</form>

Upvotes: 2

Views: 4747

Answers (2)

nash11
nash11

Reputation: 8650

Your formArray is an array of type formGroup, so you need to add a formGroupName within your ngfor to bind each formGroup in your formArray. This should be placed on an HTML element which encapsulates the formControls within (i.e. properties in your formGroup: name, fileUrl, etc. You also need to pass a formGroup to your formGroupName. In order to pass the entire formGroup, you need to be looping through each element in musics.controls not musics.value.

Now, since we are no longer looping over musics.value, you will not be able to access the name value in your placeholder using m.name. You should use m.value.name instead.

Edit: You will also need to update your component as you creating a formControl when you should be creating a formGroup. You can use FormBuilder since it lets you generate formGroup using the normal object syntax. It would be better to create your entire form using FormBuilder rather than a mix of both FormBuilder and manual FormControl instances (creating form using new FormGroup, new FormControl, etc.)

this.musicsForm = this.fb.group({
  musics: this.fb.array([
    this.fb.group(new Music('Musique 1', null, null)),
    this.fb.group(new Music('Musique 2', null, null)),
  ])
});

Here's how your HTML will now look.

<div cdkDropList class="col-md-11 px-0" (cdkDropListDropped)="drop($event)" formArrayName="musics">
  <div class="example-box" *ngFor="let m of musicsForm.get('musics').controls; let i = index" cdkDrag>
    <div class="item row my-4 w-100" [formGroupName]="i">
      <!-- Define all formControlNames within your formGroup within this div -->
      ...
      <div class="col-md-4 mt-1">
        <input class="px-2 w-100" type="text" [placeholder]="m.value.name" formControlName="name">
      </div>
      ...
    </div>
  </div>
</div>

Check out this example on how to implement formArray correctly

Upvotes: 3

bryan60
bryan60

Reputation: 29305

add this in:

<div class="example-box" *ngFor="let m of musics.value;let i = index" [formGroupName]="i" cdkDrag>

your form array is an array of groups, and their group name is the index in that array. You need to tell angular that you want the 'name' control, and at which index you want it. right now you're just saying I want the 'name' control of the form array 'musics' which doesn't exist, it only exists at indexes.

Upvotes: 0

Related Questions