Preface:
I am having the hardest time trying to figure out what sounds like an easy process for nested angular forms. I am dealing with a few components here and some of the formGroups
and formArrays
are being dynamically created and its throwing me off.
Apologies for the large code dump, but its the minimal example I was able to come up with to try and explain my problem.
The parent component is very straight forward as it only has two formControls
. I then pass the form to the tasks
component to have access to it.
this.intakeForm = this.fb.group({
requestor: ['', Validators.required],
requestJustification: ['', Validators.required]
});
HTML:
<form [formGroup]=“intakeForm”>
<app-tasks
[uiOptions]="uiOptions"
[intakeForm]="intakeForm">
</app-tasks>
</form>
Some method in here will trigger generateTask
which creates the new form group.
ngOnInit() {
this.intakeForm.addControl('tasks', new FormArray([]));
}
// Push a new form group to our tasks array
generateTask(user, tool) {
const control = <FormArray>this.intakeForm.controls['tasks'];
control.push(this.newTaskControl(user, tool))
}
// Return a form group
newTaskControl(user, tool) {
return this.fb.group({
User: user,
Tool: tool,
Roles: this.fb.array([])
})
}
HTML:
<table class="table table-condensed smallText" *ngIf="intakeForm.controls['tasks'].length">
<thead>
<tr>
<th>Role(s)</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let t of intakeForm.get('tasks').controls let i = index; trackBy:trackByIndex" [taskTR]="t" [ui]="uiOptions" [intakeForm]="intakeForm" [taskIndex]="i">
</tr>
</tbody>
</table>
Some method in here will trigger the addRole
method which will add the form group.
@Input('taskTR') row;
@Input('ui') ui;
@Input('intakeForm') intakeForm: FormGroup;
// Add a new role
addRole($event, task) {
let t = task.get('Roles').controls as FormArray;
t.push(this.newRoleControl($event))
}
// Return a form group
newRoleControl(role) {
return this.fb.group({
Role: [role, Validators.required],
Action: [null, Validators.required]
})
}
HTML
<td class="col-md-9">
<ng-select [items]="ui.adminRoles.options"
bindLabel="RoleName"
bindValue="Role"
placeholder="Select one or more roles"
[multiple]="true"
[clearable]="false"
(add)="addRole($event, row)"
(remove)="removeRole($event, row)">
</td>
I need to add formControlName
to my TR Component
, specifically on the ng-select
. However, when I try and add a formControlName
, it tells me that it needs to be within a formGroup
.
From what I can tell, the formGroup
is in the tasksComponent
and is wrapping the whole table so its technically within a formGroup
?
My end goal is to be able to add the formControlName
to this input but I am having a hard time trying to figure out the path to get there.
Here is an image of the full form object.
The last expanded section, Role
, is what this input should be called via formControlName
so that I can perform validation and what not on the control.
Edit 1 - Changes for @Harry Ninh
Tasks Component HTML
<tbody>
<tr *ngFor="let t of intakeForm.get('tasks').controls let i = index; trackBy:trackByIndex" [taskTR]="t" [ui]="uiOptions" [intakeForm]="intakeForm" [taskIndex]="i" [formGroup]="intakeForm"></tr>
</tbody>
TR Component HTML
<td class="col-md-9">
<ng-select [items]="ui.adminRoles.options"
bindLabel="RoleName"
bindValue="Role"
placeholder="Select one or more roles"
[multiple]="true"
[clearable]="false"
formControlName="Roles"
(add)="addRole($event, row)"
(remove)="removeRole($event, row)">
</td>
Result: ERROR Error: formControlName must be used with a parent formGroup directive.
Angular tells you that it's better for you to use the ways it gives you to disable/enable form controls. You can enable/disable a form control by using the following ways: Instantiate a new FormControl with the disabled property set to true. FormControl({value: '', disabled: true}) .
What are form controls in Angular? In Angular, form controls are classes that can hold both the data values and the validation information of any form element. Every form input you have in a reactive form should be bound by a form control. These are the basic units that make up reactive forms.
patchValue()linkPatches the value of a control.
You are expected to declare [formGroup]="intakeForm"
in the root tag of every component that wraps all formControlName
, formGroupName
and formArrayName
properties. Angular won't try to go up the hierarchy to find that when compiling the code.
In TR Component template, the root element (ie. the <td>
) should have [formGroup]="intakeForm"
, in order to tell Angular the formControlName
who is related to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With