Home > Mobile >  Access dynamic nested reactive form controls for validation
Access dynamic nested reactive form controls for validation

Time:12-06

stackblitz demo

I have a nested form controls that builds up the page structure, within about 3 *ngFor loops. The below e.g is final loop:

<ng-container *ngFor="let data of tabsAndPills[tab][pill]; let i = index">
...

<input [required]="data['isMandatory']" type="text" [formControlName]="data['name']" />

These loops build up my form controls, therefore I can't hard code any validation msg. I want to add a bit of text under each control to say "required" - when the control is empty(if you remove the surname field value in the stackblitz demo the entire form becomes invalid which is correct, but I want to hook into that specific control to enable the below text). e.g.

<div *ngIf="data['name'].invalid">{{ data['displayName'] }} is required</div>

I need to look for the dynamic control of [formControlName]="data['name']" in my form, therefore seeing if its invalid or not to display the text. I imagine I need to do something like this template side...

*ngIf="form.controls.fields['controls'].tab['controls'].pill['controls'].data['name'].invalid"

But can't get this right, the looped let of tab and pill are not recognised in the above format. Or, is there a way I can use a getter to make this easier?

stackblitz demo

CodePudding user response:

To access a nested form control within multiple ngFor loops, you can use the get() method provided by the FormGroup class. This method allows you to retrieve a form control by its path within the form group, which is defined by the sequence of keys that you pass to the get() method.

Here's an example of how you can use the get() method to access a nested form control within multiple ngFor loops:

<ng-container *ngFor="let tab of tabsAndPills; let tabIndex = index">
  <ng-container *ngFor="let pill of tab['controls']; let pillIndex = index">
    <ng-container *ngFor="let data of pill['controls']; let dataIndex = index">
      <input
        [required]="data['isMandatory']"
        type="text"
        [formControlName]="data['name']"
      />
      <div *ngIf="form.get(['controls', tabIndex, 'controls', pillIndex, 'controls', dataIndex, 'name']).invalid">
        {{ data['displayName'] }} is required
      </div>
    </ng-container>
  </ng-container>
</ng-container>

In this code, we use the get() method to retrieve the form control that corresponds to the current data item within the nested ngFor loops. We use the current tabIndex, pillIndex, and dataIndex values to construct the path to the form control within the form group, and we pass this path to the get() method. This allows us to access the form control and determine whether it is invalid or not, so that we can show the corresponding error message.

You can also use the get() method to simplify the ngIf condition, by storing the result of the get() call in a local template variable. Here's an example:

<ng-container *ngFor="let tab of tabsAndPills; let tabIndex = index">
  <ng-container *ngFor="let pill of tab['controls']; let pillIndex = index">
    <ng-container *ngFor="let data of pill['controls']; let dataIndex = index">
      <input
        [required]="data['isMandatory']"
        type="text"
        [formControlName]="data['name']"
      />
      <ng-container *ngIf="(control = form.get(['controls', tabIndex, 'controls', pillIndex, 'controls', dataIndex, 'name']))">
        <div *ngIf="control.invalid">
          {{ data['displayName'] }} is required
        </div>
      </ng-container>
    </ng-container>
  </ng-container>
</ng-container>

In this code, we use the get() method to retrieve the form control that corresponds to the current data item, and we store the result in a local template variable named control. This allows us to simplify the ngIf condition, and we can now simply check whether control is invalid or not, without needing to use the full path to the form control. This can make the code easier to read and understand.

  • Related