```json
//[doc-seo]
{
"Description": "Learn how to use the ABP Dynamic Form Module to create dynamic, configurable forms with validation, conditional logic, nested groups and arrays, many input types, 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: `
`,
})
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` | `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` | Emitted when the form is submitted with valid data |
| `formCancel` | `EventEmitter` | 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/radio (static or API)
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; // Custom component
// Type-specific attributes
min?: number | string; // number, date, time, range
max?: number | string; // number, date, time, range
step?: number | string; // number, time, range
minLength?: number; // text, password
maxLength?: number; // text, password
pattern?: string; // tel, text (regex)
accept?: string; // file (e.g. "image/*")
multiple?: boolean; // file
// Nested forms (group / array)
children?: FormFieldConfig[]; // Child fields for group/array
minItems?: number; // array: minimum items (default 0)
maxItems?: number; // array: maximum items
}
```
### Field Types
The following field types are supported:
| Type | Description |
|------|-------------|
| `text` | Text input |
| `email` | Email input |
| `number` | Number input (supports `min`, `max`, `step`) |
| `select` | Dropdown select (static or API-driven options) |
| `checkbox` | Checkbox |
| `date` | Date picker (supports `min`, `max`) |
| `datetime-local` | Date and time picker |
| `time` | Time picker (supports `min`, `max`, `step`) |
| `textarea` | Multi-line text |
| `password` | Password input (`minLength`, `maxLength`) |
| `tel` | Telephone input (`pattern`) |
| `url` | URL input |
| `radio` | Radio group (uses `options`) |
| `file` | File upload (`accept`, `multiple`) |
| `range` | Range slider (`min`, `max`, `step`) |
| `color` | Color picker |
| `group` | Nested group of fields (uses `children`) |
| `array` | Dynamic list with add/remove (uses `children`, `minItems`, `maxItems`) |
**Notes:**
- `file`: form value is `File` or `File[]` when `multiple` is true. Use `accept` (e.g. `"image/*"`) to limit types.
- `range`: defaults `min` 0, `max` 100, `step` 1 if omitted.
- `radio`: requires `options` (static `defaultValues` or `url`).
## 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 and Radio Fields with Options
You can create `select` dropdowns or `radio` groups with static or dynamic options. Both use the `options` property (`OptionProps`).
### 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
Used for `select` and `radio` fields. Provide either static `defaultValues` or `url` for API-driven options:
```ts
interface OptionProps {
defaultValues?: T[]; // Static array of options
url?: string; // API endpoint URL (fetched via RestService)
disabled?: (option: T) => boolean; // Function to disable specific options
labelProp?: string; // Property name for label (default 'value')
valueProp?: string; // Property name for value (default 'key')
apiName?: string; // API name for RestService when using url
}
```
When using `url`, the response array is mapped with `valueProp` / `labelProp` to build options. Localization is applied to labels via `abpLocalization` where applicable.
## 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).
## Nested Forms
The Dynamic Form supports **nested structures** via two field types:
### Group Type
Use `type: 'group'` to group related fields (e.g. address, contact info). Define child fields in `children`:
```ts
{
key: 'address',
type: 'group',
label: 'Address Information',
gridSize: 12,
children: [
{ key: 'street', type: 'text', label: 'Street', gridSize: 8 },
{ key: 'city', type: 'text', label: 'City', gridSize: 4 },
{ key: 'zipCode', type: 'text', label: 'ZIP Code', gridSize: 6 },
],
}
```
**Output:** `{ "address": { "street": "...", "city": "...", "zipCode": "..." } }`
Groups use `