Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reactive form for each table row

I'm using Angular 2 and I want to validate controls in each row separately. But I'm not getting any way to do that. I want it to be done using reactive forms only and not using template-driven approach. I want [formGroup] on each <tr>. Any help will be appreciated. Below is the structure of my code:

<tbody *ngFor="let single of allTeamDetails"                
       [ngClass]="{'alternate-row-color': $even}">
  <tr>
    <td class="td-data first-column">
      <input type="text" class="input-text form-control" 
             [value]="single.first_name">
    </td>
    <td class="td-data second-column">
      <input type="text" class="input-text form-control" 
             [value]="single.last_name">
    </td>
    <td class="td-data third-column">
      <input type="email" class="input-text form-control" 
             [value]="single.email">
    </td>
    <td class="td-data fourth-column">
      <select class="selection-dropdown width-80-percent" 
              [value]="single.user_role">
        <option *ngFor="let singleRole of allUserRole"      
                value="{{singleRole.name}}"> 
                {{setUserRoleAndType(singleRole.name)}}</option>
      </select>
    </td>
    <td class="td-data fifth-column" >
      <input type="password" class="input-text form-control">
    </td>
    <td class="td-data sixth-column" >
      <input type="password" class="input-text form-control">
    </td>
    <td class="td-data save-send-tm-data">
      <button class="btn save-user-details save-sub-account-details"                                                      
              type="button" data-toggle="tooltip" title="Save">
        <i class="fa fa-floppy-o" aria-hidden="true"></i>
      </button>
    </td>
    <td class="td-data save-send-tm-data">
      <button type="button"                                      
              class="btn save-user-details save-sub-account-details"  
              data-toggle="tooltip" title="Send Message"                       
              (click)="openSendMessageModal(single.email)">
        <i class="fa fa-envelope" aria-hidden="true"></i> 
      </button>
    </td>
  <tr>
</tbody>
like image 526
Gourav Pokharkar Avatar asked May 24 '17 12:05

Gourav Pokharkar


1 Answers

Use formArray. What you will do is you create a formGroup (master form) that contains multiple, smaller formGroup. Each smaller fromGroups will be what is repeated in your *ngFor.

Your form should look something like this:

<!--This is your master form-->
<form [formGroup]="teamForm">
  <!--Use formArray to create multiple, smaller forms'-->
  <div formArrayName="memberDetails">
    <div *ngFor="let single of allTeamDetails; let $index=index">
      <div [formGroupName]="$index">
        <div>
          <!--your field properties of every repeated items-->
          <input placeholder="First Name" type="text" formControlName="firstName" />
        </div>
        <div>
          <input placeholder="Last Name" type="text" formControlName="lastName" />
        </div>
      </div>
    </div>
  </div>
</form>

In your component, you can use angular's formBuilder to help to build a the form.

In your constructor:

  constructor(private formBuilder: FormBuilder) {
    this.teamForm = this.formBuilder.group({
      memberDetails: this.formBuilder.array([])
    });
  }

Now you can initialize every property of your repeated models. You can then customise each validators of each field. Note the properties in your typescript file that corresponds to the ones in html. I do all these in ngOnInit so that the properties can binded to the html before they are rendered.

  ngOnInit() {   
    this.teamForm = this.formBuilder.group({
      memberDetails: this.formBuilder.array(
        this.allTeamDetails.map(x => this.formBuilder.group({
          firstName: [x.first_name, [Validators.required, Validators.minLength(2)]],
          lastName: [x.last_name, [Validators.required, Validators.minLength(2)]]
        }))
      )
    })
  }

After all these, adding validation messages are very trivial. The benefits of doing it this way?

  1. because each instance is now a single formGroup by itself, you can customize your validation logic down to a very granular level.

  2. As the point above, you can subscribe to each valueChange of each smaller forms, down to each single field. For example, if you want to subscribe to first team member's first name's field change, you can do this:

      this.teamForm
       .controls.memberDetails
       .controls[0] //get the first instance!
       .controls.firstName  //get the firstName formControlName
       .valueChange 
       .subscribe(x=>console.log('value changed!))
    
  3. In the event if you want to validate the master form, you can do so as well.

Have created a plnkr, just for you :)

like image 182
CozyAzure Avatar answered Sep 29 '22 04:09

CozyAzure