Dynamic Form Generation in Angular
This challenge focuses on creating a flexible and reusable component in Angular that can dynamically generate forms based on a configuration object. This is a common requirement in applications where form structures can change based on user roles, application settings, or data fetched from an API. You will build a component that renders form fields according to a predefined schema, allowing for easy customization and scalability.
Problem Description
Your task is to develop an Angular component, let's call it DynamicFormComponent, that accepts a configuration object and renders an HTML form accordingly. The configuration object will define the structure of the form, including different types of input fields, their labels, validation rules, and initial values.
Key Requirements:
- Component Structure: Create a reusable Angular component.
- Configuration Object: Define an interface for the form configuration. This interface should describe an array of field configurations, where each field configuration includes:
type: The type of input element (e.g., 'text', 'email', 'password', 'textarea', 'select', 'checkbox', 'radio').label: The label text for the input field.name: The name attribute for the input field, which will be used for form submission and binding.value: The initial value of the input field.placeholder: (Optional) Placeholder text for the input field.options: (For 'select', 'radio' types) An array of objects, each with alabelandvalue.validators: (Optional) An array of strings representing validation rule names (e.g., 'required', 'email', 'minLength').
- Dynamic Rendering: The
DynamicFormComponentshould iterate over the field configurations and render the appropriate HTML input elements. Use Angular'sFormBuilderandFormGroupto manage form state and validation. - Validation Handling: Implement basic validation based on the
validatorsspecified in the configuration. Display error messages when validation fails. - Event Handling: The component should emit an event when the form is submitted, passing the form's current value.
Expected Behavior:
- The
DynamicFormComponentwill receive a form configuration as an@Input(). - Based on the configuration, it will render input fields, labels, and select options.
- User input will be tracked and validated.
- Error messages will be displayed for invalid fields.
- Upon submission, the component will emit an
EventEmitterwith the form's data.
Edge Cases to Consider:
- Handling different input types gracefully.
- Displaying appropriate error messages for various validation rules.
- Handling forms with no fields.
- Ensuring accessibility with labels associated with inputs.
Examples
Example 1: Simple Text Input and Email Input
// Assuming this configuration is passed to DynamicFormComponent
const formConfig = {
fields: [
{
type: 'text',
label: 'Full Name',
name: 'fullName',
value: '',
placeholder: 'Enter your full name',
validators: ['required', 'minLength:3'] // Example: custom validator syntax
},
{
type: 'email',
label: 'Email Address',
name: 'email',
value: '',
placeholder: 'Enter your email',
validators: ['required', 'email']
}
]
};
Input: The formConfig object as described above.
Output: A rendered form with two input fields: "Full Name" and "Email Address". The "Full Name" field will have a minimum length validation of 3 characters and a "required" validation. The "Email Address" field will have "required" and "email" validations. If a user tries to submit an empty form or an invalid email, error messages will be displayed. Upon valid submission, an event will be emitted with an object like:
{
"fullName": "John Doe",
"email": "john.doe@example.com"
}
Example 2: Select Dropdown and Radio Buttons
// Assuming this configuration is passed to DynamicFormComponent
const formConfig = {
fields: [
{
type: 'select',
label: 'Country',
name: 'country',
value: 'USA',
options: [
{ label: 'United States', value: 'USA' },
{ label: 'Canada', value: 'CAN' },
{ label: 'Mexico', value: 'MEX' }
],
validators: ['required']
},
{
type: 'radio',
label: 'Preferred Contact Method',
name: 'contactMethod',
value: 'email',
options: [
{ label: 'Email', value: 'email' },
{ label: 'Phone', value: 'phone' }
],
validators: ['required']
}
]
};
Input: The formConfig object as described above.
Output: A rendered form with a "Country" dropdown and "Preferred Contact Method" radio buttons. The "Country" field will be pre-selected with "USA". Both fields will be required. Upon valid submission, an event will be emitted with an object like:
{
"country": "CAN",
"contactMethod": "phone"
}
Example 3: Textarea and Checkbox
// Assuming this configuration is passed to DynamicFormComponent
const formConfig = {
fields: [
{
type: 'textarea',
label: 'Comments',
name: 'comments',
value: '',
placeholder: 'Enter your comments here',
validators: ['maxLength:200'] // Example: custom validator syntax
},
{
type: 'checkbox',
label: 'Agree to Terms and Conditions',
name: 'termsAccepted',
value: false,
validators: ['required'] // A checkbox being required means it must be checked
}
]
};
Input: The formConfig object as described above.
Output: A rendered form with a "Comments" textarea and a "Agree to Terms and Conditions" checkbox. The textarea will have a maximum length of 200 characters. The checkbox must be checked for the form to be valid. Upon valid submission, an event will be emitted with an object like:
{
"comments": "This is a sample comment.",
"termsAccepted": true
}
Constraints
- The number of fields in a single form configuration will not exceed 20.
- The
typeproperty of a field configuration will only be one of the following: 'text', 'email', 'password', 'textarea', 'select', 'checkbox', 'radio'. - The
validatorsproperty will be an array of strings, where supported validators are 'required', 'email', 'minLength', 'maxLength', 'pattern'. Custom validators (like 'minLength:3') need to be handled. - The application should be built using Angular version 10 or higher.
- Performance is important; the form should render efficiently even with the maximum number of fields.
Notes
- You will need to import
FormBuilder,FormGroup, andValidatorsfrom@angular/forms. - Consider how to map the string validator names (e.g., 'required') to Angular's built-in
Validatorsor custom validation functions. - For custom validator strings like
minLength:3, you will need to parse the value and apply the appropriate validator. - Think about how to handle asynchronous validation if that becomes a requirement in a more advanced scenario (though not strictly required for this challenge).
- The use of
*ngForand*ngIfwill be crucial in rendering the dynamic elements. - Ensure proper error message display for each field that fails validation.