Search code examples
angularangular2-formsngforangular-reactive-forms

Angular 2+: Accessing Reactive Forms independently with Validation in ngFor loop


I am working with a Reactive form. I have 2 sets of data. They are:

  1. List of Items - Contains Item ID and Item Name
  2. List of Groups - Contains Group ID and Group Name

Here is what I am trying to do:

  1. In the UI, I am looping through the Items and showing the Item ID and Item Name. Works fine.
  2. I am looping through the Groups and showing the Group ID and Group Name in a Select box. Works fine too.
  3. I place the Select box from point 2 above, in the ngFor loop of the Items in point 1 above, so that each Item can be assigned to a Group. Let's say that I click on the first Select box with the intention of assigning a Group from the list, for the First Item. Then instead of clicking on any of the options shown, I click away somewhere else on the page. This action shows error message for all the other form controls/select boxes on the page as well. If I select a value for any of the other select boxes in rest of the page, then the error message for even the first dropdown is cleared. This is the issue and this should not happen. The error should show up only for the Select box that is paired with the First Item and not for the other select boxes in the page that are meant to be paired with other Items. Each of those errors should show up independently for each of those forms.

SUMMARY OF THE ISSUE:

I am trying to assign a Group to each of those Items. So I am trying to fix the (above) connected behavior of the forms, so that each form should work independently of the other in the loop and after selecting a Group, when the "Assign Group" button is clicked, I am trying to access the corresponding Item ID and Group ID of only the form that was submitted and not the other forms. How can this be achieved with proper Validation/Errors showing up for the submitted forms, as and when they are accessed?

LIVE DEMO OF THE ISSUE:

I have already setup a StackBlitz demo here replicating the issue.

LIVE DEMO EDITOR CODE:

Here is the Live Demo Editor code


Solution

  • actually with your code angular understand that there's one Form (formControlName) called: partnerGroupsId so all of them are linked with same ID and name

    LIVE SOLUTION : https://angular-2-accessing-reactive-forms-independently.stackblitz.io

    LIVE EDIT CODE : https://stackblitz.com/edit/angular-2-accessing-reactive-forms-independently

    SOLUTION :

    You need to loop through your data (this.allData) to build form Builder groups :

    const groups = {};
    
    for(let i = 0; i< this.allData.length; i++) {
      groups['partnerGroupsId' + i] = ['', [
                Validators.required,
      ]];
    }
    
    this.applicationForm = this.formBuilder.group(
            groups
    );
    

    and use the below HTML :

    <ul class="pb-0">
    <ng-container *ngFor="let item of allData; let i = index">
        <li>
            <label class="label">
                {{item.item_name}} /
                 <span class="label-heading">Item ID # {{item.item_id}}</span>
            </label>
        <form [formGroup]="applicationForm" (ngSubmit)="assignGroup()"
              (keyup.enter)="assignGroup()"
              class="full-width">
              <label position="floating">Select Group</label>
              <select formControlName="partnerGroupsId{{ i }}">
                <option value="">Select a Group from this list</option>
                  <option *ngFor="let partnerGroups of allDataGroups;"
                          value="{{partnerGroups.group_id}}" selected>
                      {{partnerGroups.display_name}}
                  </option>                  
              </select>
          <div class="validaterrors">
              <ng-container *ngFor="let validation of validationMessages.partnerGroupsId;"   >
                  <div class="error-message"
                        *ngIf="applicationForm.get('partnerGroupsId' + i).hasError(validation.type) && (applicationForm.get('partnerGroupsId' + i).dirty || applicationForm.get('partnerGroupsId' + i).touched)">
                      {{ validation.message }}
                  </div>
              </ng-container>
          </div>
    
            <button color="primary" expand="block" type="submit" class="">
                Assign Group
            </button>
        </form>
        </li>
    </ng-container>