I am trying to create a dynamic angular form the data that I get from an ngRx service.
Here is the sample code. My html template looks like
<div class="padding-top-50" *ngIf="filteredParam$ | async; else loader">
<form class="row" [formGroup]="paramForm" *ngIf="paramForm">
<div class="col-sm-4 padding-top-30" *ngFor="let reportParam of formData">
<label>{{ reportParam.name | textTrimmer }}</label>
<ng-container [ngTemplateOutlet]="reportParam.type === 'Date'? dateField: textField"
[ngTemplateOutletContext]="{reportParam: reportParam}">
</ng-container>
</div>
</form>
</div>
<ng-template #textField let-reportParam="reportParam">
<div class="input-group">
<input #inputField type="text" [name]="reportParam.name" class="form-control"
placeholder="{{ 'vrvReportTranslation.item' | cxTranslate }}" (change)="inputChanged(reportParam)" />
</div>
</ng-template>
<ng-template #dateField let-reportParam="reportParam">
<div class="input-group">
<input #inputField type="text" [name]="reportParam.name" class="form-control ng-datepicker" placeholder="MM-DD-YYYY"
ngbDatepicker #d1="ngbDatepicker" (dateSelect)="d1.toggle();" [positionTarget]="buttonFrom"
[formControlName]="reportParam.name" (change)="inputChanged(reportParam)" [firstDayOfWeek]="7" />
<button class="btn btn-outline-secondary bi-calendar3 margin-0" #buttonFrom (click)="d1.toggle();" type="button"
[ngClass]="'active-btn'">
<span class="calendar-icon"></span>
</button>
</div>
</ng-template>
And my component file looks like this:
formData: ReportsParam[] = [];
paramForm: FormGroup | undefined;
filteredParam$ = this.reportService.getReportParams();
ngOnInit(): void {
this.filteredParam$.subscribe(data => {
// update the array in case subscription triggers again
this.formData.length = 0;
let formObj: { [key: string]: FormControl } = {};
const previousMonthDate = this.getLastMonthDate();
console.log(this.paramForm);
if (data)
data.forEach(element => {
formObj[element.name] = this.formBuilder.control('');
this.formData.push(element);
})
this.paramForm = this.formBuilder.group(formObj);
console.log(this.paramForm);
});
}
Below is the error I am getting
I understand the error is happening because paramForm is getting its value after the view has been created thus throwing this error, but I am not able to solve this. I don't know:
Found the issue. If you are using ng-container for formGroup you need to tell the templates which formgroup you want to use. So the template should look like this :
<div class="padding-top-50" *ngIf="filteredParam$ | async; else loader">
<form class="row" [formGroup]="paramForm">
<div class="col-sm-4 padding-top-30" *ngFor="let reportParam of formData">
<label>{{ reportParam.name | textTrimmer }}</label>
<ng-container [ngTemplateOutlet]="reportParam.type === 'Date'? dateField: textField"
[ngTemplateOutletContext]="{reportParam: reportParam}">
</ng-container>
</div>
</form>
</div>
<ng-template #textField let-reportParam="reportParam">
<div class="input-group" [formGroup]="paramForm">
<input #inputField type="text" [name]="reportParam.name" class="form-control"
placeholder="{{ 'vrvReportTranslation.item' | cxTranslate }}" (change)="inputChanged(reportParam)" />
</div>
</ng-template>
<ng-template #dateField let-reportParam="reportParam">
<div class="input-group" [formGroup]="paramForm">
<input #inputField type="text" [name]="reportParam.name" class="form-control ng-datepicker" placeholder="MM-DD-YYYY"
ngbDatepicker #d1="ngbDatepicker" (dateSelect)="d1.toggle();" [positionTarget]="buttonFrom"
[formControlName]="reportParam.name" (change)="inputChanged(reportParam)" [firstDayOfWeek]="7" />
<button class="btn btn-outline-secondary bi-calendar3 margin-0" #buttonFrom (click)="d1.toggle();" type="button"
[ngClass]="'active-btn'">
<span class="calendar-icon"></span>
</button>
</div>
</ng-template>
Rest all the things will be same. Also one *ngIf was not required. Not marking this as answer because it doesn't answer the second part, i.e. Is it even the best/correct approach to create a dynamic form for an API.