UNPKG

@uplink-protocol/form-controller

Version:

Reactive multi-step form controller with dynamic validation and state management

256 lines (185 loc) 7.64 kB
# Form Controller Extensibility Guide This document provides a guide on how to extend the FormControllerClass to create custom form controllers with specialized behavior. ## Overview Thanks to the class-based architecture refactoring, the `FormControllerClass` has been designed to be highly extensible. You can create custom form controllers by extending this class and overriding specific methods to customize behavior without duplicating code. ## Extensibility Features The `FormControllerClass` provides several extension points: - **Protected properties** for access in subclasses - **Protected methods** that can be overridden - **Lifecycle hooks** at various stages of initialization - **Modular service architecture** for targeted customization ## Creating a Custom Form Controller To create a custom form controller, extend the `FormControllerClass`: ```typescript import { FormControllerClass, FormConfig } from '@uplink-protocol/form-controller'; export class CustomFormController extends FormControllerClass { constructor(config: FormConfig) { super(config); // Additional initialization if needed } // Override methods as needed } ``` ## Extension Points ### Protected Properties All service instances and state objects are accessible in subclasses: ```typescript class CustomFormController extends FormControllerClass { customMethod() { // Access services directly const fieldData = this.fieldService.getFieldData(stepId, fieldId); // Access manager services this.formManagerService.customOperation(); // Access state const data = this.initialFormData; } } ``` ### Initialization Hooks You can customize the initialization process by overriding these methods: #### 1. `setupInitialFormData` Override to customize how initial form data is set up: ```typescript protected setupInitialFormData(config: FormConfig): void { // Call the parent implementation first super.setupInitialFormData(config); // Add custom logic config.steps.forEach(step => { // Add default values for custom fields this.initialFormData[step.id]['customField'] = 'default value'; }); } ``` #### 2. `initializeServices` Override to customize service initialization: ```typescript protected initializeServices(config: FormConfig): void { // Call the parent implementation super.initializeServices(config); // Replace a service with a custom implementation this.validatorService = new EnhancedValidatorService(); // Add additional services this.myCustomService = new MyCustomService(config); } ``` #### 3. `initializeManagerServices` Override to customize manager service initialization: ```typescript protected initializeManagerServices(): void { // Call the parent implementation super.initializeManagerServices(); // Replace a manager with a custom implementation this.navigationManager = new CustomNavigationManager( this.stepperService, this.configService, this.fieldService, this.interactionService ); } ``` #### 4. `initializeMethods` Override to customize or extend available controller methods: ```typescript protected initializeMethods(): void { // Call the parent implementation to get all standard methods super.initializeMethods(); // Add custom methods this.methods.customSubmit = () => { // Custom submission logic this.methods.submitForm(); // Additional processing after submission this.sendDataToServer(); }; // Override existing methods const originalSubmit = this.methods.submitForm; this.methods.submitForm = () => { // Pre-processing this.beforeSubmit(); // Call original method const result = originalSubmit(); // Post-processing this.afterSubmit(); return result; }; } ``` #### 5. `initializeStepValidation` Override to customize the initial step validation behavior: ```typescript protected initializeStepValidation(): void { // Call the parent implementation super.initializeStepValidation(); // Add custom validation logic this.validateCustomRules(); } ``` ## Examples ### Example 1: Creating a Form Controller with Analytics ```typescript import { FormControllerClass, FormConfig } from '@uplink-protocol/form-controller'; export class AnalyticsFormController extends FormControllerClass { private analyticsService: AnalyticsService; constructor(config: FormConfig) { super(config); this.analyticsService = new AnalyticsService(); } protected initializeMethods(): void { super.initializeMethods(); // Wrap navigation methods with analytics const originalNextStep = this.methods.nextStep; this.methods.nextStep = () => { const previousStep = this.bindings.currentStep.current.id; const result = originalNextStep(); const currentStep = this.bindings.currentStep.current.id; this.analyticsService.trackNavigation(previousStep, currentStep); return result; }; // Wrap submission with analytics const originalSubmit = this.methods.submitForm; this.methods.submitForm = () => { const result = originalSubmit(); if (result.success) { this.analyticsService.trackCompletion(result.data); } return result; }; } } ``` ### Example 2: Custom Validation Form Controller ```typescript import { FormControllerClass, FormConfig } from '@uplink-protocol/form-controller'; export class EnhancedValidationController extends FormControllerClass { constructor(config: FormConfig) { super(config); } protected initializeServices(config: FormConfig): void { super.initializeServices(config); // Replace the validator service with an enhanced one this.validatorService = new EnhancedValidatorService(); } protected initializeMethods(): void { super.initializeMethods(); // Add advanced validation methods this.methods.validateWithRules = (rules) => { return this.validatorService.validateWithCustomRules( this.formService.getAllData(), rules ); }; } protected initializeStepValidation(): void { super.initializeStepValidation(); // Add cross-field validation this.methods.validateCrossFieldRules(); } } ``` ## Best Practices 1. **Call super methods first**: When overriding methods, call the parent implementation first unless you want to completely replace the behavior. 2. **Avoid modifying base services directly**: Instead of modifying the base services, create enhanced services and replace the references in your subclass. 3. **Maintain the services architecture**: If adding new services, follow the existing pattern of keeping core services separate from manager services. 4. **Use composition for complex extensions**: For very complex extensions, consider composing multiple controllers rather than deeply nesting inheritance. 5. **Document your extensions**: Make sure to document your custom controller's features and how they differ from the base implementation. ## Conclusion The refactored class-based architecture of the FormControllerClass makes it highly extensible, allowing for customization at various levels. By extending the base class and overriding specific methods, you can create specialized form controllers tailored to specific needs without duplicating code.