mirror of https://github.com/abpframework/abp.git
1 changed files with 816 additions and 0 deletions
@ -0,0 +1,816 @@ |
|||||
|
```json |
||||
|
//[doc-seo] |
||||
|
{ |
||||
|
"Description": "Learn how to use the ABP Dynamic Form Module to create dynamic, configurable forms with validation, conditional logic, and custom components in Angular applications." |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
# Dynamic Form Module |
||||
|
|
||||
|
The ABP Dynamic Form Module is a powerful component that allows you to create dynamic, configurable forms without writing extensive HTML templates. It provides a declarative way to define form fields with validation, conditional logic, grid layout, and custom components. |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
The Dynamic Form Module is part of the `@abp/ng.components` package. If you haven't installed it yet, install it via npm: |
||||
|
|
||||
|
```bash |
||||
|
npm install @abp/ng.components |
||||
|
``` |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
Import the `DynamicFormComponent` in your component: |
||||
|
|
||||
|
```ts |
||||
|
import { DynamicFormComponent } from '@abp/ng.components/dynamic-form'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-my-component', |
||||
|
imports: [DynamicFormComponent], |
||||
|
templateUrl: './my-component.component.html', |
||||
|
}) |
||||
|
export class MyComponent {} |
||||
|
``` |
||||
|
|
||||
|
## Basic Example |
||||
|
|
||||
|
Here's a simple example of how to use the dynamic form: |
||||
|
|
||||
|
```ts |
||||
|
import { Component } from '@angular/core'; |
||||
|
import { DynamicFormComponent } from '@abp/ng.components/dynamic-form'; |
||||
|
import { FormFieldConfig } from '@abp/ng.components/dynamic-form'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-user-form', |
||||
|
imports: [DynamicFormComponent], |
||||
|
template: ` |
||||
|
<abp-dynamic-form |
||||
|
[fields]="formFields" |
||||
|
[submitButtonText]="'Submit'" |
||||
|
[showCancelButton]="true" |
||||
|
(onSubmit)="handleSubmit($event)" |
||||
|
(formCancel)="handleCancel()"> |
||||
|
</abp-dynamic-form> |
||||
|
`, |
||||
|
}) |
||||
|
export class UserFormComponent { |
||||
|
formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'firstName', |
||||
|
type: 'text', |
||||
|
label: 'First Name', |
||||
|
placeholder: 'Enter your first name', |
||||
|
required: true, |
||||
|
order: 1, |
||||
|
}, |
||||
|
{ |
||||
|
key: 'lastName', |
||||
|
type: 'text', |
||||
|
label: 'Last Name', |
||||
|
placeholder: 'Enter your last name', |
||||
|
required: true, |
||||
|
order: 2, |
||||
|
}, |
||||
|
{ |
||||
|
key: 'email', |
||||
|
type: 'email', |
||||
|
label: 'Email', |
||||
|
placeholder: 'Enter your email', |
||||
|
required: true, |
||||
|
order: 3, |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
handleSubmit(formValue: any) { |
||||
|
console.log('Form submitted:', formValue); |
||||
|
// Handle form submission |
||||
|
} |
||||
|
|
||||
|
handleCancel() { |
||||
|
console.log('Form cancelled'); |
||||
|
// Handle form cancellation |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Component Inputs |
||||
|
|
||||
|
The `DynamicFormComponent` accepts the following inputs: |
||||
|
|
||||
|
| Input | Type | Default | Description | |
||||
|
|-------|------|---------|-------------| |
||||
|
| `fields` | `FormFieldConfig[]` | `[]` | Array of field configurations | |
||||
|
| `values` | `Record<string, any>` | `undefined` | Initial values for the form | |
||||
|
| `submitButtonText` | `string` | `'Submit'` | Text for the submit button | |
||||
|
| `submitInProgress` | `boolean` | `false` | Whether form submission is in progress | |
||||
|
| `showCancelButton` | `boolean` | `false` | Whether to show the cancel button | |
||||
|
|
||||
|
## Component Outputs |
||||
|
|
||||
|
| Output | Type | Description | |
||||
|
|--------|------|-------------| |
||||
|
| `onSubmit` | `EventEmitter<any>` | Emitted when the form is submitted with valid data | |
||||
|
| `formCancel` | `EventEmitter<void>` | Emitted when the cancel button is clicked | |
||||
|
|
||||
|
## FormFieldConfig Properties |
||||
|
|
||||
|
The `FormFieldConfig` interface defines the structure of each field in the form: |
||||
|
|
||||
|
```ts |
||||
|
interface FormFieldConfig { |
||||
|
key: string; // Unique identifier for the field |
||||
|
type: FieldType; // Type of the field |
||||
|
label: string; // Label text for the field |
||||
|
value?: any; // Initial value |
||||
|
placeholder?: string; // Placeholder text |
||||
|
required?: boolean; // Whether the field is required |
||||
|
disabled?: boolean; // Whether the field is disabled |
||||
|
options?: OptionProps; // Options for select fields |
||||
|
validators?: ValidatorConfig[]; // Array of validator configurations |
||||
|
conditionalLogic?: ConditionalRule[]; // Array of conditional rules |
||||
|
order?: number; // Display order (ascending) |
||||
|
gridSize?: number; // Bootstrap grid size (1-12) |
||||
|
component?: Type<ControlValueAccessor>; // Custom component |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Field Types |
||||
|
|
||||
|
The following field types are supported: |
||||
|
|
||||
|
- `text` - Text input |
||||
|
- `email` - Email input |
||||
|
- `number` - Number input |
||||
|
- `select` - Dropdown select |
||||
|
- `checkbox` - Checkbox |
||||
|
- `date` - Date picker |
||||
|
- `textarea` - Textarea |
||||
|
|
||||
|
## Validators |
||||
|
|
||||
|
You can add validators to your form fields using the `validators` property: |
||||
|
|
||||
|
```ts |
||||
|
const formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'username', |
||||
|
type: 'text', |
||||
|
label: 'Username', |
||||
|
validators: [ |
||||
|
{ |
||||
|
type: 'required', |
||||
|
message: 'Username is required', |
||||
|
}, |
||||
|
{ |
||||
|
type: 'minLength', |
||||
|
value: 3, |
||||
|
message: 'Username must be at least 3 characters', |
||||
|
}, |
||||
|
{ |
||||
|
type: 'maxLength', |
||||
|
value: 20, |
||||
|
message: 'Username must not exceed 20 characters', |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
key: 'age', |
||||
|
type: 'number', |
||||
|
label: 'Age', |
||||
|
validators: [ |
||||
|
{ |
||||
|
type: 'min', |
||||
|
value: 18, |
||||
|
message: 'You must be at least 18 years old', |
||||
|
}, |
||||
|
{ |
||||
|
type: 'max', |
||||
|
value: 100, |
||||
|
message: 'Age must not exceed 100', |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
### Available Validator Types |
||||
|
|
||||
|
| Type | Description | Requires Value | |
||||
|
|------|-------------|----------------| |
||||
|
| `required` | Field is required | No | |
||||
|
| `email` | Must be a valid email | No | |
||||
|
| `minLength` | Minimum string length | Yes | |
||||
|
| `maxLength` | Maximum string length | Yes | |
||||
|
| `min` | Minimum numeric value | Yes | |
||||
|
| `max` | Maximum numeric value | Yes | |
||||
|
| `pattern` | Regular expression pattern | Yes | |
||||
|
| `requiredTrue` | Must be true (for checkboxes) | No | |
||||
|
|
||||
|
## Select Fields with Options |
||||
|
|
||||
|
You can create dropdown select fields with static or dynamic options: |
||||
|
|
||||
|
### Static Options |
||||
|
|
||||
|
```ts |
||||
|
const formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'country', |
||||
|
type: 'select', |
||||
|
label: 'Country', |
||||
|
options: { |
||||
|
defaultValues: [ |
||||
|
{ key: 'us', value: 'United States' }, |
||||
|
{ key: 'uk', value: 'United Kingdom' }, |
||||
|
{ key: 'ca', value: 'Canada' }, |
||||
|
], |
||||
|
valueProp: 'key', |
||||
|
labelProp: 'value', |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
### Dynamic Options from API |
||||
|
|
||||
|
```ts |
||||
|
const formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'department', |
||||
|
type: 'select', |
||||
|
label: 'Department', |
||||
|
options: { |
||||
|
url: '/api/departments', |
||||
|
apiName: 'MyApi', |
||||
|
valueProp: 'id', |
||||
|
labelProp: 'name', |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
### OptionProps Interface |
||||
|
|
||||
|
```ts |
||||
|
interface OptionProps<T = any> { |
||||
|
defaultValues?: T[]; // Static array of options |
||||
|
url?: string; // API endpoint URL |
||||
|
disabled?: (option: T) => boolean; // Function to disable specific options |
||||
|
labelProp?: string; // Property name for label |
||||
|
valueProp?: string; // Property name for value |
||||
|
apiName?: string; // API name for RestService |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Conditional Logic |
||||
|
|
||||
|
The Dynamic Form Module supports conditional logic to show/hide or enable/disable fields based on other field values: |
||||
|
|
||||
|
```ts |
||||
|
const formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'hasLicense', |
||||
|
type: 'checkbox', |
||||
|
label: 'Do you have a driver\'s license?', |
||||
|
order: 1, |
||||
|
}, |
||||
|
{ |
||||
|
key: 'licenseNumber', |
||||
|
type: 'text', |
||||
|
label: 'License Number', |
||||
|
placeholder: 'Enter your license number', |
||||
|
order: 2, |
||||
|
conditionalLogic: [ |
||||
|
{ |
||||
|
dependsOn: 'hasLicense', |
||||
|
condition: 'equals', |
||||
|
value: true, |
||||
|
action: 'show', |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
key: 'age', |
||||
|
type: 'number', |
||||
|
label: 'Age', |
||||
|
order: 3, |
||||
|
}, |
||||
|
{ |
||||
|
key: 'parentConsent', |
||||
|
type: 'checkbox', |
||||
|
label: 'Parent Consent Required', |
||||
|
order: 4, |
||||
|
conditionalLogic: [ |
||||
|
{ |
||||
|
dependsOn: 'age', |
||||
|
condition: 'lessThan', |
||||
|
value: 18, |
||||
|
action: 'show', |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
### Conditional Rule Interface |
||||
|
|
||||
|
```ts |
||||
|
interface ConditionalRule { |
||||
|
dependsOn: string; // Key of the field to watch |
||||
|
condition: string; // Condition type |
||||
|
value: any; // Value to compare against |
||||
|
action: string; // Action to perform |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Available Conditions |
||||
|
|
||||
|
- `equals` - Field value equals the specified value |
||||
|
- `notEquals` - Field value does not equal the specified value |
||||
|
- `contains` - Field value contains the specified value (for strings/arrays) |
||||
|
- `greaterThan` - Field value is greater than the specified value (for numbers) |
||||
|
- `lessThan` - Field value is less than the specified value (for numbers) |
||||
|
|
||||
|
### Available Actions |
||||
|
|
||||
|
- `show` - Show the field when condition is met |
||||
|
- `hide` - Hide the field when condition is met |
||||
|
- `enable` - Enable the field when condition is met |
||||
|
- `disable` - Disable the field when condition is met |
||||
|
|
||||
|
## Grid Layout |
||||
|
|
||||
|
You can use the `gridSize` property to control the Bootstrap grid layout: |
||||
|
|
||||
|
```ts |
||||
|
const formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'firstName', |
||||
|
type: 'text', |
||||
|
label: 'First Name', |
||||
|
gridSize: 6, // Half width |
||||
|
order: 1, |
||||
|
}, |
||||
|
{ |
||||
|
key: 'lastName', |
||||
|
type: 'text', |
||||
|
label: 'Last Name', |
||||
|
gridSize: 6, // Half width |
||||
|
order: 2, |
||||
|
}, |
||||
|
{ |
||||
|
key: 'address', |
||||
|
type: 'textarea', |
||||
|
label: 'Address', |
||||
|
gridSize: 12, // Full width |
||||
|
order: 3, |
||||
|
}, |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
The `gridSize` property uses Bootstrap's 12-column grid system. If not specified, it defaults to 12 (full width). |
||||
|
|
||||
|
## Custom Components |
||||
|
|
||||
|
You can use custom components for specific fields by providing a component that implements `ControlValueAccessor`: |
||||
|
|
||||
|
```ts |
||||
|
// custom-rating.component.ts |
||||
|
import { Component, forwardRef } from '@angular/core'; |
||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-custom-rating', |
||||
|
template: ` |
||||
|
<div class="rating"> |
||||
|
@for (star of [1,2,3,4,5]; track star) { |
||||
|
<span |
||||
|
class="star" |
||||
|
[class.filled]="star <= value" |
||||
|
(click)="setValue(star)"> |
||||
|
★ |
||||
|
</span> |
||||
|
} |
||||
|
</div> |
||||
|
`, |
||||
|
styles: [` |
||||
|
.star { cursor: pointer; font-size: 24px; color: #ccc; } |
||||
|
.star.filled { color: #ffc107; } |
||||
|
`], |
||||
|
providers: [{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => CustomRatingComponent), |
||||
|
multi: true |
||||
|
}] |
||||
|
}) |
||||
|
export class CustomRatingComponent implements ControlValueAccessor { |
||||
|
value = 0; |
||||
|
onChange: any = () => {}; |
||||
|
onTouched: any = () => {}; |
||||
|
|
||||
|
setValue(rating: number) { |
||||
|
this.value = rating; |
||||
|
this.onChange(rating); |
||||
|
this.onTouched(); |
||||
|
} |
||||
|
|
||||
|
writeValue(value: any): void { |
||||
|
this.value = value || 0; |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.onChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
this.onTouched = fn; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Then use it in your form configuration: |
||||
|
|
||||
|
```ts |
||||
|
import { CustomRatingComponent } from './custom-rating.component'; |
||||
|
|
||||
|
const formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'rating', |
||||
|
type: 'text', // Type is ignored when using custom component |
||||
|
label: 'Rating', |
||||
|
component: CustomRatingComponent, |
||||
|
value: 3, |
||||
|
}, |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
## Setting Initial Values |
||||
|
|
||||
|
You can set initial values for the form fields in two ways: |
||||
|
|
||||
|
### 1. Using the `value` property in FormFieldConfig |
||||
|
|
||||
|
```ts |
||||
|
const formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'firstName', |
||||
|
type: 'text', |
||||
|
label: 'First Name', |
||||
|
value: 'John', |
||||
|
}, |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
### 2. Using the `values` input |
||||
|
|
||||
|
```ts |
||||
|
@Component({ |
||||
|
template: ` |
||||
|
<abp-dynamic-form |
||||
|
[fields]="formFields" |
||||
|
[values]="initialValues" |
||||
|
(onSubmit)="handleSubmit($event)"> |
||||
|
</abp-dynamic-form> |
||||
|
`, |
||||
|
}) |
||||
|
export class MyComponent { |
||||
|
formFields: FormFieldConfig[] = [ |
||||
|
{ |
||||
|
key: 'firstName', |
||||
|
type: 'text', |
||||
|
label: 'First Name', |
||||
|
}, |
||||
|
{ |
||||
|
key: 'lastName', |
||||
|
type: 'text', |
||||
|
label: 'Last Name', |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
initialValues = { |
||||
|
firstName: 'John', |
||||
|
lastName: 'Doe', |
||||
|
}; |
||||
|
|
||||
|
handleSubmit(formValue: any) { |
||||
|
console.log(formValue); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Programmatic Form Control |
||||
|
|
||||
|
You can access the form instance using the `exportAs` property and template reference variable: |
||||
|
|
||||
|
```ts |
||||
|
@Component({ |
||||
|
template: ` |
||||
|
<abp-dynamic-form |
||||
|
#myForm="abpDynamicForm" |
||||
|
[fields]="formFields" |
||||
|
(onSubmit)="handleSubmit($event)"> |
||||
|
</abp-dynamic-form> |
||||
|
|
||||
|
<button (click)="myForm.resetForm()">Reset Form</button> |
||||
|
`, |
||||
|
}) |
||||
|
export class MyComponent { |
||||
|
formFields: FormFieldConfig[] = [ |
||||
|
// ... field configurations |
||||
|
]; |
||||
|
|
||||
|
handleSubmit(formValue: any) { |
||||
|
console.log(formValue); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Available Methods |
||||
|
|
||||
|
- `resetForm()` - Resets the form to its initial state |
||||
|
- `submit()` - Programmatically submit the form |
||||
|
|
||||
|
## Custom Action Buttons |
||||
|
|
||||
|
You can customize the action buttons by projecting your own content: |
||||
|
|
||||
|
```ts |
||||
|
@Component({ |
||||
|
template: ` |
||||
|
<abp-dynamic-form |
||||
|
[fields]="formFields" |
||||
|
(onSubmit)="handleSubmit($event)"> |
||||
|
|
||||
|
<div actions class="form-actions"> |
||||
|
<button type="button" class="btn btn-secondary" (click)="handleCancel()"> |
||||
|
Cancel |
||||
|
</button> |
||||
|
<button type="submit" class="btn btn-success"> |
||||
|
Save Changes |
||||
|
</button> |
||||
|
<button type="button" class="btn btn-info" (click)="handleDraft()"> |
||||
|
Save as Draft |
||||
|
</button> |
||||
|
</div> |
||||
|
</abp-dynamic-form> |
||||
|
`, |
||||
|
}) |
||||
|
export class MyComponent { |
||||
|
formFields: FormFieldConfig[] = [ |
||||
|
// ... field configurations |
||||
|
]; |
||||
|
|
||||
|
handleSubmit(formValue: any) { |
||||
|
console.log('Form submitted:', formValue); |
||||
|
} |
||||
|
|
||||
|
handleCancel() { |
||||
|
console.log('Cancelled'); |
||||
|
} |
||||
|
|
||||
|
handleDraft() { |
||||
|
console.log('Saved as draft'); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Complete Example |
||||
|
|
||||
|
Here's a complete example demonstrating various features: |
||||
|
|
||||
|
```ts |
||||
|
import { Component } from '@angular/core'; |
||||
|
import { DynamicFormComponent } from '@abp/ng.components/dynamic-form'; |
||||
|
import { FormFieldConfig } from '@abp/ng.components/dynamic-form'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-employee-form', |
||||
|
imports: [DynamicFormComponent], |
||||
|
template: ` |
||||
|
<div class="container"> |
||||
|
<h2>Employee Registration</h2> |
||||
|
<abp-dynamic-form |
||||
|
#employeeForm="abpDynamicForm" |
||||
|
[fields]="formFields" |
||||
|
[submitButtonText]="'Register Employee'" |
||||
|
[showCancelButton]="true" |
||||
|
[submitInProgress]="isSubmitting" |
||||
|
(onSubmit)="handleSubmit($event)" |
||||
|
(formCancel)="handleCancel()"> |
||||
|
</abp-dynamic-form> |
||||
|
</div> |
||||
|
`, |
||||
|
}) |
||||
|
export class EmployeeFormComponent { |
||||
|
isSubmitting = false; |
||||
|
|
||||
|
formFields: FormFieldConfig[] = [ |
||||
|
// Personal Information |
||||
|
{ |
||||
|
key: 'firstName', |
||||
|
type: 'text', |
||||
|
label: 'First Name', |
||||
|
placeholder: 'Enter first name', |
||||
|
required: true, |
||||
|
gridSize: 6, |
||||
|
order: 1, |
||||
|
validators: [ |
||||
|
{ type: 'required', message: 'First name is required' }, |
||||
|
{ type: 'minLength', value: 2, message: 'First name must be at least 2 characters' }, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
key: 'lastName', |
||||
|
type: 'text', |
||||
|
label: 'Last Name', |
||||
|
placeholder: 'Enter last name', |
||||
|
required: true, |
||||
|
gridSize: 6, |
||||
|
order: 2, |
||||
|
validators: [ |
||||
|
{ type: 'required', message: 'Last name is required' }, |
||||
|
{ type: 'minLength', value: 2, message: 'Last name must be at least 2 characters' }, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
key: 'email', |
||||
|
type: 'email', |
||||
|
label: 'Email', |
||||
|
placeholder: 'Enter email address', |
||||
|
required: true, |
||||
|
gridSize: 6, |
||||
|
order: 3, |
||||
|
validators: [ |
||||
|
{ type: 'required', message: 'Email is required' }, |
||||
|
{ type: 'email', message: 'Please enter a valid email address' }, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
key: 'phoneNumber', |
||||
|
type: 'text', |
||||
|
label: 'Phone Number', |
||||
|
placeholder: 'Enter phone number', |
||||
|
gridSize: 6, |
||||
|
order: 4, |
||||
|
}, |
||||
|
|
||||
|
// Employment Details |
||||
|
{ |
||||
|
key: 'department', |
||||
|
type: 'select', |
||||
|
label: 'Department', |
||||
|
required: true, |
||||
|
gridSize: 6, |
||||
|
order: 5, |
||||
|
options: { |
||||
|
defaultValues: [ |
||||
|
{ id: 1, name: 'Engineering' }, |
||||
|
{ id: 2, name: 'Marketing' }, |
||||
|
{ id: 3, name: 'Sales' }, |
||||
|
{ id: 4, name: 'Human Resources' }, |
||||
|
], |
||||
|
valueProp: 'id', |
||||
|
labelProp: 'name', |
||||
|
}, |
||||
|
validators: [ |
||||
|
{ type: 'required', message: 'Department is required' }, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
key: 'position', |
||||
|
type: 'text', |
||||
|
label: 'Position', |
||||
|
placeholder: 'Enter position', |
||||
|
required: true, |
||||
|
gridSize: 6, |
||||
|
order: 6, |
||||
|
validators: [ |
||||
|
{ type: 'required', message: 'Position is required' }, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
key: 'startDate', |
||||
|
type: 'date', |
||||
|
label: 'Start Date', |
||||
|
required: true, |
||||
|
gridSize: 6, |
||||
|
order: 7, |
||||
|
validators: [ |
||||
|
{ type: 'required', message: 'Start date is required' }, |
||||
|
], |
||||
|
}, |
||||
|
|
||||
|
// Conditional Fields |
||||
|
{ |
||||
|
key: 'isManager', |
||||
|
type: 'checkbox', |
||||
|
label: 'Is this person a manager?', |
||||
|
gridSize: 12, |
||||
|
order: 8, |
||||
|
}, |
||||
|
{ |
||||
|
key: 'teamSize', |
||||
|
type: 'number', |
||||
|
label: 'Team Size', |
||||
|
placeholder: 'Number of team members', |
||||
|
gridSize: 6, |
||||
|
order: 9, |
||||
|
conditionalLogic: [ |
||||
|
{ |
||||
|
dependsOn: 'isManager', |
||||
|
condition: 'equals', |
||||
|
value: true, |
||||
|
action: 'show', |
||||
|
}, |
||||
|
], |
||||
|
validators: [ |
||||
|
{ type: 'min', value: 1, message: 'Team size must be at least 1' }, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
key: 'managementExperience', |
||||
|
type: 'textarea', |
||||
|
label: 'Management Experience', |
||||
|
placeholder: 'Describe your management experience', |
||||
|
gridSize: 12, |
||||
|
order: 10, |
||||
|
conditionalLogic: [ |
||||
|
{ |
||||
|
dependsOn: 'isManager', |
||||
|
condition: 'equals', |
||||
|
value: true, |
||||
|
action: 'show', |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
|
||||
|
// Additional Information |
||||
|
{ |
||||
|
key: 'notes', |
||||
|
type: 'textarea', |
||||
|
label: 'Additional Notes', |
||||
|
placeholder: 'Any additional information', |
||||
|
gridSize: 12, |
||||
|
order: 11, |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
handleSubmit(formValue: any) { |
||||
|
this.isSubmitting = true; |
||||
|
|
||||
|
console.log('Employee Data:', formValue); |
||||
|
|
||||
|
// Simulate API call |
||||
|
setTimeout(() => { |
||||
|
this.isSubmitting = false; |
||||
|
alert('Employee registered successfully!'); |
||||
|
}, 2000); |
||||
|
} |
||||
|
|
||||
|
handleCancel() { |
||||
|
if (confirm('Are you sure you want to cancel?')) { |
||||
|
// Navigate back or reset form |
||||
|
console.log('Form cancelled'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## API Reference |
||||
|
|
||||
|
### DynamicFormComponent |
||||
|
|
||||
|
#### Properties |
||||
|
|
||||
|
| Property | Type | Description | |
||||
|
|----------|------|-------------| |
||||
|
| `dynamicForm` | `FormGroup` | The underlying Angular FormGroup instance | |
||||
|
| `fieldVisibility` | `{ [key: string]: boolean }` | Object tracking field visibility state | |
||||
|
|
||||
|
#### Methods |
||||
|
|
||||
|
| Method | Parameters | Returns | Description | |
||||
|
|--------|-----------|---------|-------------| |
||||
|
| `submit()` | - | `void` | Submits the form if valid | |
||||
|
| `onCancel()` | - | `void` | Emits the formCancel event | |
||||
|
| `resetForm()` | - | `void` | Resets the form to initial values | |
||||
|
| `isFieldVisible(field)` | `FormFieldConfig` | `boolean` | Checks if a field is currently visible | |
||||
|
|
||||
|
### DynamicFormService |
||||
|
|
||||
|
The `DynamicFormService` provides utility methods for form management: |
||||
|
|
||||
|
#### Methods |
||||
|
|
||||
|
| Method | Parameters | Returns | Description | |
||||
|
|--------|-----------|---------|-------------| |
||||
|
| `createFormGroup(fields)` | `FormFieldConfig[]` | `FormGroup` | Creates a FormGroup from field configurations | |
||||
|
| `getInitialValues(fields)` | `FormFieldConfig[]` | `any` | Extracts initial values from field configurations | |
||||
|
| `getOptions(url, apiName?)` | `string, string?` | `Observable<any[]>` | Fetches options from an API endpoint | |
||||
|
|
||||
|
## See Also |
||||
|
|
||||
|
- [Form Validation](./form-validation.md) |
||||
|
- [Form Input Component](./form-input-component.md) |
||||
|
- [Dynamic Form Extensions](./dynamic-form-extensions.md) |
||||
Loading…
Reference in new issue