@uplink-protocol/form-controller
Version:
Reactive multi-step form controller with dynamic validation and state management
182 lines (154 loc) • 5.25 kB
Markdown
# Using Form Controller with TypeScript
This guide demonstrates how to use the form-controller package with TypeScript, leveraging type definitions for better developer experience.
## Basic Setup
```typescript
import { FormController, FormConfig, Field } from '@uplink-protocol/form-controller';
// Define your form configuration with proper types
const formConfig: FormConfig = {
steps: [
{
id: 'contact',
title: 'Contact Information',
fields: {
name: {
id: 'name',
type: 'text',
label: 'Full Name',
required: true
} as Field,
email: {
id: 'email',
type: 'email',
label: 'Email Address',
required: true,
validation: {
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
}
} as Field
}
}
]
};
// Initialize the form controller
const form = FormController(formConfig);
```
## TypeScript Benefits
Using TypeScript with form-controller provides several advantages:
1. **Type Checking**: Catch configuration errors at compile time
2. **IntelliSense**: Get autocompletion and hints as you type
3. **Documentation**: Access JSDoc comments directly in your IDE
4. **Refactoring**: Reliable refactoring with type safety
## Typed Event Handlers
```typescript
// Type-safe event handlers
const handleFieldChange = (stepId: string, fieldId: string, value: any) => {
form.methods.updateField(stepId, fieldId, value);
};
// Type-safe form submission
const handleSubmit = () => {
const result = form.methods.submitForm();
if (result.success) {
// TypeScript knows result.data is available
console.log('Form data:', result.data);
} else {
// TypeScript knows result.errors is available
console.error('Validation errors:', result.errors);
}
};
```
## Enhanced Validation with TypeScript
```typescript
// Define a field with enhanced validation features
const passwordField: Field = {
id: 'password',
type: 'password',
label: 'Password',
validation: {
required: true,
minLength: 8,
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/,
// Type-safe per-validation error messages
errorMessages: {
required: 'Password is required',
minLength: 'Password must be at least 8 characters long',
pattern: 'Password must include uppercase, lowercase, and numbers'
},
// Multiple dynamic validators with typed parameters
dynamicValidators: [
{
name: 'passwordStrength',
params: {
minStrength: 3,
checkCommonPasswords: true
},
errorMessage: 'Your password is too weak'
},
{
name: 'notPreviouslyUsed',
params: {
checkHistory: true
},
errorMessage: 'You cannot reuse a previous password'
}
],
collectAllErrors: true
}
};
// Register the validators with proper TypeScript types
form.methods.registerValidator(
'passwordStrength',
(value: string, context: ValidatorContext) => {
// Implementation details...
const { minStrength } = context.field.validation?.dynamicValidators?.[0].params || {};
return true;
}
);
// Validate and access all errors
const validatePasswordField = (value: string) => {
const result = form.methods.validateField('account', 'password', value);
if (!result) {
// Get errors from the fieldErrors binding
const errors = form.bindings.fieldErrors.current.account?.password;
// With collectAllErrors, this might contain multiple errors separated by ' | '
const errorsList = errors?.split(' | ');
console.error('Password validation errors:', errorsList);
}
};
```
// Register the validator
form.methods.registerValidator('passwordStrength', passwordStrengthValidator);
```
## Type-Safe Subscriptions
```typescript
// Type-safe reactive subscriptions
form.bindings.formData.subscribe((data) => {
// TypeScript knows the structure of data
const nameValue = data.contact?.name;
console.log('Name updated:', nameValue);
});
form.bindings.fieldErrors.subscribe((errors) => {
// TypeScript knows the structure of errors
const emailError = errors.contact?.email;
if (emailError) {
showError('email', emailError);
}
});
```
## Advanced Type Usage
```typescript
// Extract types from the form controller for reuse
type FormData = ReturnType<typeof form.methods.getAllData>;
type FlatFormData = ReturnType<typeof form.methods.getFlatData>;
type FormErrors = typeof form.bindings.fieldErrors.current;
// Create strongly-typed helper functions
function processFormData(data: FormData) {
// Process the form data with type safety
}
function handleFormErrors(errors: FormErrors) {
// Process errors with type safety
}
// Example usage
form.bindings.formData.subscribe(processFormData);
form.bindings.fieldErrors.subscribe(handleFormErrors);
```
This guide demonstrates how to leverage TypeScript's type system with the form-controller package for a better developer experience with increased reliability and productivity.