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?
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.