igby
igby

Reputation: 43

Angular forms - How to bind formControlName for select in formArrayName to use objects

I'm trying to figure out how to set up a binding object complex binding for select inside formArrayName. I'm guessing that formControlName should have reference to the current item in the array, but I don't know how to access it.

<div formArrayName="users">
    <div *ngFor="let u of users.controls; let i=index" [formGroupName]='i'>
      user {{i}}: <select formControlName='id'[compareWith]="compareFn">
        <option *ngFor="let a of avaliableUsers" [ngValue]='a'>{{a.login}}</option>
      </select>
    </div>
  </div>

I've created demo with single select (which is working as I want) and array which is pushing value into "id". https://stackblitz.com/edit/angular-d2uaa1

Any help is greatly appreciated.

EDIT

Key points:

Solution (Based on @JT_82 comment)

   <div *ngFor="let u of users.controls; let i=index">
      <select [formControlName]='i' [compareWith]="compareFn">
        <option *ngFor="let a of avaliableUsers" [ngValue]='a'>{{a.login}}</option>
      </select>
    </div>
 ngOnInit(): void {
    this.owner.patchValue(this.group.owner);
    this.group.users.forEach(u => {
      this.users.push(this.fb.control(u))
    });
  }

 compareFn(a, b): boolean {
    return a.id === b.id;
  }

Upvotes: 3

Views: 2728

Answers (1)

AVJT82
AVJT82

Reputation: 73357

EDIT: Per OP's wishes, we would like to keep the object as value in the select, for that we can instead of using a formgroup inside the formarray, just push formcontrols, which then contains object values:

this.group.users.forEach(u => {
  this.users.push(this.fb.control(u)) // just push formcontrol!
});

and then mark in template:

<select [formControlName]='i' [compareWith]="compareFn">
  <option *ngFor="let a of avaliableUsers" [ngValue]='a'>{{a.login}}</option>
</select>

Since we are now using an object value as the formcontrol, when trying to match preset values, we need a compareWith function:

compareFn(a, b): boolean {
  return a.id === b.id;
}

StackBlitz


ORIGINAL ANSWER:

I would perhaps call a function when the select changes, and then find the user from availableUsers and set the form value for login with the found user. So template:

<select formControlName='id' (change)="findLogin(u)">
  <option *ngFor="let a of avaliableUsers" [ngValue]='a.id'>{{a.login}}</option>
</select>

So you can remove compareWith, since we are now using a number for the value. Then the findLogin function where we pass the current formgroup from the iteration:

findLogin(group: FormGroup) {
  const user = this.avaliableUsers.find(x => x.id === group.get('id').value)
  group.get('login').setValue(user.login);
}

Your forked StackBlitz

Upvotes: 3

Related Questions