I'm getting a problem with 'controls'enter image description here enter image description here "Object is possibly 'null'.ngtsc(2531)" and "Element implicitly has an 'any' type because type 'AbstractControl' has no index signature. Did you mean to call 'get'?ngtsc(7052)"
you can get an idea from this site https://csharp-video-tutorials.blogspot.com/2018/10/angular-dynamic-forms-tutorial.html what I want to do! I want to use formArray to check the validation. You can see below the picture whole hour input are displayed validation error. Because I didn't use formArray. when I implemented formArray It will get an error on this spot.
in the beginning I used this => *ngFor="let skill of employeeForm.get('skills').controls; let i = index" but the controls word is shown as an error!!!
.HTML
<form [formGroup]="employeeForm">
<div formArrayName="skills" *ngFor="let skill of employeeForm.get('skills')['controls']; let i = index;">
<div >
<div >
<button type="button" (click)="addSkillButtonClick()">ADD ROW</button>
</div>
</div>
<p-table [loading]="loading" styleClass="text-sm " [ngClass]="{'p-datatable-is-loading': loading}" [value]="dataSource"
[scrollable]="true" scrollHeight="calc(100% 2px)" scrollDirection="both">
<ng-template pTemplate="header">
<tr>
<th style="width: 250px;">Project</th>
<th style="width: 250px;">Process</th>
<th style="width: 200px;">Working Hours</th>
<th alignFrozen="right" pFrozenColumn style="width: 114px;"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-source let-index="rowIndex">
<tr *ngIf="dataSource[index].editing; else viewingMode;" formGroupName="0">
<td style="width: 250px; overflow: visible" >
<div>
<p-autoComplete appendTo="body" #project name="project" [(ngModel)]="dataSource[index].project_id" [suggestions]="projectSuggestions"
field="name" (onSelect)="handleSelectProject($event, index)"
formControlName="project"
(completeMethod)="handleSearchProject($event, index)" (onFocus)="project.show()" placeholder="Search project">
</p-autoComplete>
</div>
</td>
<td style="width: 250px;">
<div>
<p-dropdown formControlName="process" name="process" [ngStyle]="{ width: '100%' }" [(ngModel)]="dataSource[index].process" [options]="processOptions"
placeholder="Select process">
</p-dropdown>
</div>
</td>
<td style="width: 200px;" >
<div ngClass="{'has-error': formErrors.hours }">
<p-inputNumber formControlName="hours" [(ngModel)]="dataSource[index].hour" [id]="'working_hour' index" [name]="'working_hour' index" [min]="0" [max]="24"
placeholder="00" (blur)="logValidationErrors"></p-inputNumber>
<label for="hours" >h</label>
</div>
<div>
<p-inputNumber formControlName="minutes" [(ngModel)]="dataSource[index].minutes" [id]="'working_min' index" [name]="'working_min' index" [min]="0" [max]="59" placeholder="0" ></p-inputNumber>
</div>
<label for="hours" >m</label>
</td>
<td alignFrozen="right" pFrozenColumn style="width: 114px;">
<button [disabled]="dataSource[index].deleting" pButton type="button" (click)="handleDeleteRowClick(index)" icon="pi pi-trash"
></button>
<button pButton type="button" (click)="handleSaveRowClick(index)" icon="pi pi-save"
></button>
</td>
</tr>
</ng-template>
</p-table>
</div>
</form>
.TS
export class DailyReportRegularTableComponent extends DailyReportLog implements OnInit {
@Input() dataSource: UIDailyLogRegularTableInterface[] = [];
@Output() dataSourceChange = new EventEmitter<UIDailyLogRegularTableInterface[]>();
helper!: HelperService
_formHelper: FormService = new FormService;
employeeForm!: FormGroup;
formErrors: any;
validationMessages: any;
constructor(private _helper: HelperService,private formBuilder: FormBuilder,private fb: FormBuilder) {
super();
this.helper = this._helper;
}
ngOnInit(): void {
this.employeeForm = this.fb.group({
skills: this.fb.array([
this.addSkillFormGroup()
])
});
this.employeeForm.valueChanges.subscribe((data) => {
this.logValidationErrors(this.employeeForm)
});
}
addSkillButtonClick(): void {
(<FormArray>this.employeeForm.get('skills')).push(this.addSkillFormGroup());
}
logValidationErrors(group: FormGroup = this.employeeForm): void{
Object.keys(group.controls).forEach((key: string) =>{
const abstractControl = group.get(key)
if(abstractControl instanceof FormGroup){
this.logValidationErrors(abstractControl)
} else {
this.formErrors[key] = '';
if(abstractControl && !abstractControl.valid){
for (const errorKey in abstractControl.errors) {
if (errorKey) {
this.formErrors[key];
}
}
}
if (abstractControl instanceof FormGroup) {
this.logValidationErrors(abstractControl);
}
if (abstractControl instanceof FormArray) {
for (const control of abstractControl.controls){
if (control instanceof FormGroup) {
this.logValidationErrors(control)
}
}
}
}
})
}
addSkillFormGroup(): FormGroup{
return this.fb.group({
project: ['', Validators.required],
process: ['', Validators.required],
hours: ['', Validators.required],
minutes: [''],
})
}
onl oadDataClick(): void{
this.logValidationErrors(this.employeeForm)
}
}
CodePudding user response:
This is strict type checking and considers employeeForm.get('skills')
as any
, which is true as you have not given it a type. I would suggest making a getter of this in which your specify the type:
get skillArr() {
return this.employeeForm.get('skills') as FormArray
}
And use this in template:
let skill of skillArr.controls
Now Angular can tell it's a formArray. You COULD type it as any
as well in the template, but as you can type it as a FormArray, I would recommend that. This would be the way to type it as any
:
let skill of $any(employeeForm.get('skills')).controls
I only resort to this when I have a nested FormArray inside a FormArray, as using a getter there does not work.