Skip to content

Commit 4357ce7

Browse files
committed
refactoring
1 parent e3260f5 commit 4357ce7

File tree

1 file changed

+269
-6
lines changed
  • docs/en/Community-Articles/2025-09-30-Building-Dynamic-Forms-in-Angular-for-Enterprise-Applications

1 file changed

+269
-6
lines changed

docs/en/Community-Articles/2025-09-30-Building-Dynamic-Forms-in-Angular-for-Enterprise-Applications/post.md

Lines changed: 269 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Dynamic forms are useful for enterprise applications where form structures need
1616

1717
### 1. Form Configuration Model
1818

19-
We define a model to represent the form configuration. This model includes field types, labels, validation rules, and other metadata.
19+
Define a model to represent the form configuration. This model includes field types, labels, validation rules, and other metadata.
2020

2121
```typescript
2222
export interface FormFieldConfig {
@@ -27,13 +27,14 @@ export interface FormFieldConfig {
2727
placeholder?: string;
2828
required?: boolean;
2929
disabled?: boolean;
30-
options?: { key: string; value: any }[];
31-
validators?: ValidatorConfig[];
32-
conditionalLogic?: ConditionalRule[];
33-
order?: number;
30+
options?: { key: string; value: any }[];
31+
validators?: ValidatorConfig[]; // Custom validators
32+
conditionalLogic?: ConditionalRule[]; // For showing/hiding fields based on other field values
33+
order?: number; // For ordering fields in the form
3434
gridSize?: number; // For layout purposes, e.g., Bootstrap grid size (1-12)
3535
}
3636

37+
// Validator configuration for form fields
3738
export interface ValidatorConfig {
3839
type: 'required' | 'email' | 'minLength' | 'maxLength' | 'pattern' | 'custom';
3940
value?: any;
@@ -278,4 +279,266 @@ export class DynamicFormComponent implements OnInit {
278279
});
279280
}
280281
}
281-
```
282+
```
283+
284+
### 4. Dynamic Form Field Component
285+
286+
```typescript
287+
@Component({
288+
selector: 'app-dynamic-form-field',
289+
template: `
290+
@if (isVisible) {
291+
<div class="field-container" [formGroup]="form">
292+
293+
@if (field.type === 'text') {
294+
<!-- Text Input -->
295+
<div class="form-group">
296+
<label [for]="field.key">{{ field.label }}</label>
297+
<input
298+
[id]="field.key"
299+
[formControlName]="field.key"
300+
[placeholder]="field.placeholder || ''"
301+
class="form-control"
302+
[class.is-invalid]="isFieldInvalid()">
303+
@if (isFieldInvalid()) {
304+
<div class="invalid-feedback">
305+
{{ getErrorMessage() }}
306+
</div>
307+
}
308+
</div>
309+
} @else if (field.type === 'select') {
310+
<!-- Select Dropdown -->
311+
<div class="form-group">
312+
<label [for]="field.key">{{ field.label }}</label>
313+
<select
314+
[id]="field.key"
315+
[formControlName]="field.key"
316+
class="form-control"
317+
[class.is-invalid]="isFieldInvalid()">
318+
<option value="">Please select...</option>
319+
@for (option of field.options; track option.key) {
320+
<option
321+
[value]="option.key">
322+
{{ option.value }}
323+
</option>
324+
}
325+
</select>
326+
@if (isFieldInvalid()) {
327+
<div class="invalid-feedback">
328+
{{ getErrorMessage() }}
329+
</div>
330+
}
331+
</div>
332+
} @else if (field.type === 'checkbox') {
333+
<!-- Checkbox -->
334+
<div class="form-group form-check">
335+
<input
336+
type="checkbox"
337+
[id]="field.key"
338+
[formControlName]="field.key"
339+
class="form-check-input"
340+
[class.is-invalid]="isFieldInvalid()">
341+
<label class="form-check-label" [for]="field.key">
342+
{{ field.label }}
343+
</label>
344+
@if (isFieldInvalid()) {
345+
<div class="invalid-feedback">
346+
{{ getErrorMessage() }}
347+
</div>
348+
}
349+
</div>
350+
} @else if (field.type === 'email') {
351+
<!-- Email Input -->
352+
<div class="form-group">
353+
<label [for]="field.key">{{ field.label }}</label>
354+
<input
355+
type="email"
356+
[id]="field.key"
357+
[formControlName]="field.key"
358+
[placeholder]="field.placeholder || ''"
359+
class="form-control"
360+
[class.is-invalid]="isFieldInvalid()">
361+
@if (isFieldInvalid()) {
362+
<div class="invalid-feedback">
363+
{{ getErrorMessage() }}
364+
</div>
365+
}
366+
</div>
367+
} @else if (field.type === 'textarea') {
368+
<!-- Textarea -->
369+
<div class="form-group">
370+
<label [for]="field.key">{{ field.label }}</label>
371+
<textarea
372+
[id]="field.key"
373+
[formControlName]="field.key"
374+
[placeholder]="field.placeholder || ''"
375+
rows="4"
376+
class="form-control"
377+
[class.is-invalid]="isFieldInvalid()">
378+
</textarea>
379+
@if (isFieldInvalid()) {
380+
<div class="invalid-feedback">
381+
{{ getErrorMessage() }}
382+
</div>
383+
}
384+
</div>
385+
}
386+
</div>
387+
<!-- Add more field types as needed-->
388+
}
389+
`,
390+
imports: [ReactiveFormsModule],
391+
})
392+
export class DynamicFormFieldComponent implements OnInit {
393+
@Input() field!: FormFieldConfig;
394+
@Input() form!: FormGroup;
395+
@Input() isVisible: boolean = true;
396+
@Output() fieldChange = new EventEmitter<{ fieldKey: string; value: any }>();
397+
398+
ngOnInit() {
399+
const control = this.form.get(this.field.key);
400+
if (control) {
401+
control.valueChanges.subscribe(value => {
402+
this.fieldChange.emit({ fieldKey: this.field.key, value });
403+
});
404+
}
405+
}
406+
407+
isFieldInvalid(): boolean {
408+
const control = this.form.get(this.field.key);
409+
return !!(control && control.invalid && (control.dirty || control.touched));
410+
}
411+
412+
getErrorMessage(): string {
413+
const control = this.form.get(this.field.key);
414+
if (!control || !control.errors) return '';
415+
416+
const validators = this.field.validators || [];
417+
418+
for (const validator of validators) {
419+
if (control.errors[validator.type]) {
420+
return validator.message;
421+
}
422+
}
423+
424+
// Fallback error messages
425+
if (control.errors['required']) return `${this.field.label} is required`;
426+
if (control.errors['email']) return 'Please enter a valid email address';
427+
if (control.errors['minlength']) return `Minimum length is ${control.errors['minlength'].requiredLength}`;
428+
if (control.errors['maxlength']) return `Maximum length is ${control.errors['maxlength'].requiredLength}`;
429+
430+
return 'Invalid input';
431+
}
432+
}
433+
434+
```
435+
436+
### 5. Usage Example
437+
438+
```typescript
439+
440+
@Component({
441+
selector: 'app-home',
442+
template: `
443+
<div class="row">
444+
<div class="col-4 offset-4">
445+
<app-dynamic-form
446+
[fields]="formFields"
447+
submitButtonText="Save User"
448+
(formSubmit)="onSubmit($event)"
449+
(formCancel)="onCancel()">
450+
</app-dynamic-form>
451+
</div>
452+
</div>
453+
`,
454+
imports: [DynamicFormComponent]
455+
})
456+
export class HomeComponent {
457+
@Input() title: string = 'Home Component';
458+
formFields: FormFieldConfig[] = [
459+
{
460+
key: 'firstName',
461+
type: 'text',
462+
label: 'First Name',
463+
placeholder: 'Enter first name',
464+
required: true,
465+
validators: [
466+
{ type: 'required', message: 'First name is required' },
467+
{ type: 'minLength', value: 2, message: 'Minimum 2 characters required' }
468+
],
469+
gridSize: 12,
470+
order: 1
471+
},
472+
{
473+
key: 'lastName',
474+
type: 'text',
475+
label: 'Last Name',
476+
placeholder: 'Enter last name',
477+
required: true,
478+
validators: [
479+
{ type: 'required', message: 'Last name is required' }
480+
],
481+
gridSize: 12,
482+
order: 2
483+
},
484+
{
485+
key: 'email',
486+
type: 'email',
487+
label: 'Email Address',
488+
placeholder: 'Enter email',
489+
required: true,
490+
validators: [
491+
{ type: 'required', message: 'Email is required' },
492+
{ type: 'email', message: 'Please enter a valid email' }
493+
],
494+
order: 3
495+
},
496+
{
497+
key: 'userType',
498+
type: 'select',
499+
label: 'User Type',
500+
required: true,
501+
options: [
502+
{ key: 'admin', value: 'Administrator' },
503+
{ key: 'user', value: 'Regular User' },
504+
{ key: 'guest', value: 'Guest User' }
505+
],
506+
validators: [
507+
{ type: 'required', message: 'Please select user type' }
508+
],
509+
order: 4
510+
},
511+
{
512+
key: 'adminNotes',
513+
type: 'textarea',
514+
label: 'Admin Notes',
515+
placeholder: 'Enter admin-specific notes',
516+
conditionalLogic: [
517+
{
518+
dependsOn: 'userType',
519+
condition: 'equals',
520+
value: 'admin',
521+
action: 'show'
522+
}
523+
],
524+
order: 5
525+
}
526+
];
527+
528+
onSubmit(formData: any) {
529+
console.log('Form submitted:', formData);
530+
// Handle form submission
531+
}
532+
533+
onCancel() {
534+
console.log('Form cancelled');
535+
// Handle form cancellation
536+
}
537+
}
538+
539+
540+
```
541+
542+
## Conclusion
543+
544+
Dynamic forms provide a powerful foundation for enterprise applications that need flexible, maintainable form solutions. By separating form configuration from implementation, teams can create scalable systems that adapt to changing business requirements without extensive code modifications. The key to success is building a robust architecture that handles validation, conditional logic, and user experience considerations while maintaining performance and accessibility standards.

0 commit comments

Comments
 (0)