feat: implement dynamic form and popup components
- Added DynamicField interface to define form field structure. - Created DynamicFormConfig interface for form configuration. - Developed DynamicFormComponent to handle dynamic form rendering and validation. - Implemented DynamicPopupComponent for displaying forms in a modal dialog. - Added HTML and SCSS for dynamic form and popup styling. - Integrated Material Design components for form inputs and buttons. - Implemented form submission logic with API integration. - Added tests for DynamicForm and DynamicPopup components. - Updated global styles for Material components in themed popups. - Included Material Icons in index.html for better UI representation.
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
<form [formGroup]="form">
|
||||
|
||||
@for (f of config.fields; track f) {
|
||||
|
||||
<ng-container *ngIf="f.type !== 'array' && f.type !== 'hidden'">
|
||||
@if (f.type === 'text') {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ f.label }}</mat-label>
|
||||
<input matInput [id]="f.name" [formControlName]="f.name" [placeholder]="f.placeholder || ''" />
|
||||
<mat-hint *ngIf="f.placeholder">{{ f.placeholder }}</mat-hint>
|
||||
<mat-error *ngIf="form.get(f.name)?.invalid && (form.get(f.name)?.touched || form.get(f.name)?.dirty)">
|
||||
This field is required.
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (f.type === 'textarea') {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ f.label }}</mat-label>
|
||||
<textarea matInput [id]="f.name" cdkTextareaAutosize cdkAutosizeMinRows="3" cdkAutosizeMaxRows="6" [formControlName]="f.name" [placeholder]="f.placeholder || ''"></textarea>
|
||||
<mat-hint *ngIf="f.placeholder">{{ f.placeholder }}</mat-hint>
|
||||
<mat-error *ngIf="form.get(f.name)?.invalid && (form.get(f.name)?.touched || form.get(f.name)?.dirty)">
|
||||
This field is required.
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (f.type === 'number') {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ f.label }}</mat-label>
|
||||
<input matInput [id]="f.name" type="number" [formControlName]="f.name" />
|
||||
<mat-error *ngIf="form.get(f.name)?.invalid && (form.get(f.name)?.touched || form.get(f.name)?.dirty)">
|
||||
Please enter a valid number.
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (f.type === 'select') {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ f.label }}</mat-label>
|
||||
<mat-select [id]="f.name" [formControlName]="f.name">
|
||||
@for (opt of f.options; track opt) {
|
||||
<mat-option [value]="opt.value">{{ opt.label }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error *ngIf="form.get(f.name)?.invalid && (form.get(f.name)?.touched || form.get(f.name)?.dirty)">
|
||||
Please select a value.
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (f.type === 'year') {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ f.label }}</mat-label>
|
||||
<mat-select [id]="f.name" [formControlName]="f.name" [compareWith]="compareYearValues">
|
||||
@if (f.yearRange?.allowPresent) {
|
||||
<mat-option value="Present">Present</mat-option>
|
||||
}
|
||||
@for (yr of getYearOptions(f); track yr) {
|
||||
<mat-option [value]="yr">{{ yr }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error *ngIf="form.get(f.name)?.invalid && (form.get(f.name)?.touched || form.get(f.name)?.dirty)">
|
||||
Please select a year.
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (f.type === 'date') {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ f.label }}</mat-label>
|
||||
<input matInput [matDatepicker]="picker" [id]="f.name" [formControlName]="f.name" [placeholder]="f.placeholder || ''" />
|
||||
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
<mat-error *ngIf="form.get(f.name)?.invalid && (form.get(f.name)?.touched || form.get(f.name)?.dirty)">
|
||||
Please select a date.
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (f.type === 'file') {
|
||||
<div>
|
||||
<label [for]="f.name">{{ f.label }}</label>
|
||||
<input [id]="f.name" type="file" (change)="onFileChange($event, f.name)" />
|
||||
</div>
|
||||
}
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="f.type === 'array'">
|
||||
<div>
|
||||
<h3>{{ f.label }}</h3>
|
||||
<div [formArrayName]="f.name">
|
||||
@for (item of getArrayControls(f.name); track item; let i = $index) {
|
||||
<div [formGroupName]="i" class="array-item">
|
||||
<div class="array-item-fields">
|
||||
@for (sub of f.itemConfig; track sub) {
|
||||
@if (sub.type !== 'hidden') {
|
||||
@if (sub.type === 'date') {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ sub.label }}</mat-label>
|
||||
<input matInput [matDatepicker]="subPicker" [id]="sub.name + '_' + i" [formControlName]="sub.name" [placeholder]="sub.placeholder || ''" />
|
||||
<mat-datepicker-toggle matIconSuffix [for]="subPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #subPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
} @else if (sub.type === 'year') {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ sub.label }}</mat-label>
|
||||
<mat-select [id]="sub.name + '_' + i" [formControlName]="sub.name" [compareWith]="compareYearValues">
|
||||
@if (sub.yearRange?.allowPresent) {
|
||||
<mat-option value="Present">Present</mat-option>
|
||||
}
|
||||
@for (yr of getYearOptions(sub); track yr) {
|
||||
<mat-option [value]="yr">{{ yr }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
} @else {
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ sub.label }}</mat-label>
|
||||
<input matInput [id]="sub.name + '_' + i" [formControlName]="sub.name" [placeholder]="sub.placeholder || ''" />
|
||||
</mat-form-field>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="array-item-actions">
|
||||
<button type="button" class="remove-link" (click)="removeArrayItem(f, i)">
|
||||
<i class="fa-solid fa-trash-can"></i> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<button type="button" class="add-btn" (click)="addArrayItem(f)">
|
||||
<i class="fa-solid fa-plus"></i> Add Item
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
</form>
|
||||
Reference in New Issue
Block a user