Form Configuration
Complete guide to configuring forms with all features - context, services, validations, computed fields, and lifecycle hooks working together.
⚙️
What is Form Configuration?
Form configuration is the complete JSON structure that defines your form's behavior, data, validations, and dynamic features. It brings together all the form builder's capabilities into a single, cohesive definition.
Complete Form Configuration Structure
json
{
"id": "completeExampleForm",
"type": "form",
"metadata": {
"formName": "Complete Feature Example",
"version": "1.0",
"description": "Demonstrates all form features"
},
"initialPage": "personal",
"context": {
"user": {
"firstName": "",
"lastName": "",
"email": "",
"preferences": {
"newsletter": true,
"notifications": false
}
},
"order": {
"items": [],
"subtotal": 0,
"tax": 0,
"total": 0,
"discountCode": ""
},
"location": {
"zipCode": "",
"city": "",
"state": "",
"taxRate": 0
}
},
"services": {
"loadUserProfile": {
"type": "rest",
"url": "/api/users/{{context:user.email}}",
"method": "GET"
},
"lookupTaxRate": {
"type": "rest",
"url": "/api/tax-rate/{{context:location.zipCode}}",
"method": "GET"
},
"submitOrder": {
"type": "rest",
"url": "/api/orders",
"method": "POST",
"format": "json",
"body": {
"user": "{{context:user}}",
"order": "{{context:order}}"
}
},
"calculateDiscount": {
"type": "function",
"function": "applyDiscountCode"
}
},
"validations": {
"email": {
"rule": "pattern",
"value": "^[^\s@]+@[^\s@]+\.[^\s@]+$",
"message": "Please enter a valid email address"
}
},
"computedFields": {
"fullName": {
"targetPath": "user.fullName",
"expression": "context.user.firstName + ' ' + context.user.lastName",
"dependencies": ["user.firstName", "user.lastName"]
},
"orderTotal": {
"targetPath": "order.total",
"expression": "context.order.subtotal + context.order.tax",
"dependencies": ["order.subtotal", "order.tax"]
}
},
"lifecycle": {
"onFormLoad": [
{
"type": "callApi",
"service": "loadUserProfile",
"then": [
{
"type": "assignContext",
"assign": {
"user": {"{{event}}"}
}
}
]
}
],
"onContextChange": [
{
"watch": ["location.zipCode"],
"actions": [
{
"type": "callApi",
"service": "lookupTaxRate",
"then": [
{
"type": "assignContext",
"assign": {
"location.taxRate": {"{{event.rate}}"}
}
}
]
}
]
},
{
"watch": ["order.discountCode"],
"actions": [
{
"type": "callApi",
"service": "calculateDiscount",
"then": [
{
"type": "assignContext",
"assign": {
"order.discount": {"{{event.discount}}"}
}
}
]
}
]
}
],
"beforeSubmit": [
{
"type": "validate",
"targets": ["user.email", "user.firstName"]
}
]
},
"blocks": [...],
"pages": {...}
}Configuration Sections Breakdown
📋 Basic Configuration
- id: Unique form identifier
- type: Always "form"
- metadata: Form name, version, description
- initialPage: First page to show
🎯 Data & State
- context: Form data and state
- computedFields: Auto-calculated values
- services: API calls and functions
- validations: Field validation rules
⚡ Dynamic Behavior
- lifecycle: Event-driven actions
- onFormLoad: Initial data loading
- onContextChange: Reactive updates
- beforeSubmit: Final validation
🎨 User Interface
- blocks: Individual form components
- pages: Form page definitions
- className: CSS styling
- displayConditions: Conditional visibility
Feature Integration Examples
1. Auto-Populating Form with API Data
json
{
"context": {
"user": {
"firstName": "",
"lastName": "",
"email": ""
}
},
"services": {
"loadProfile": {
"type": "rest",
"url": "/api/user/{{context:user.email}}",
"method": "GET"
}
},
"lifecycle": {
"onFormLoad": [
{
"type": "callApi",
"service": "loadProfile",
"then": [
{
"type": "assignContext",
"assign": {
"user.firstName": {"{{event.firstName}}"},
"user.lastName": {"{{event.lastName}}"}
}
}
]
}
]
}
}2. Dependent Dropdowns with Validation
json
{
"context": {
"location": {
"country": "",
"state": "",
"city": ""
}
},
"services": {
"loadStates": {
"type": "rest",
"url": "/api/states/{{context:location.country}}",
"method": "GET"
},
"loadCities": {
"type": "rest",
"url": "/api/cities/{{context:location.state}}",
"method": "GET"
}
},
"lifecycle": {
"onContextChange": [
{
"watch": ["location.country"],
"actions": [
{
"type": "callApi",
"service": "loadStates",
"then": [
{
"type": "assignContext",
"assign": {
"location.states": {"{{event}}"}
}
}
]
}
]
},
{
"watch": ["location.state"],
"actions": [
{
"type": "callApi",
"service": "loadCities",
"then": [
{
"type": "assignContext",
"assign": {
"location.cities": {"{{event}}"}
}
}
]
}
]
}
]
},
"validations": {
"state": {
"rule": "exact",
"value": {"{{context:location.states}}"},
"message": "Please select a valid state"
}
}
}3. Shopping Cart with Real-time Calculations
json
{
"context": {
"cart": {
"items": [],
"subtotal": 0,
"taxRate": 0.08,
"tax": 0,
"discount": 0,
"total": 0
}
},
"services": {
"calculateTax": {
"type": "function",
"function": "calculateOrderTax"
}
},
"computedFields": {
"subtotal": {
"targetPath": "cart.subtotal",
"expression": "context.cart.items.reduce((sum, item) => sum + (item.price * item.quantity), 0)",
"dependencies": ["cart.items"]
},
"tax": {
"targetPath": "cart.tax",
"expression": "Math.round(context.cart.subtotal * context.cart.taxRate * 100) / 100",
"dependencies": ["cart.subtotal", "cart.taxRate"]
},
"total": {
"targetPath": "cart.total",
"expression": "context.cart.subtotal + context.cart.tax - context.cart.discount",
"dependencies": ["cart.subtotal", "cart.tax", "cart.discount"]
}
},
"lifecycle": {
"onContextChange": [
{
"watch": ["cart.items", "cart.taxRate"],
"actions": [
{
"type": "callApi",
"service": "calculateTax",
"then": [
{
"type": "assignContext",
"assign": {
"cart.calculatedTax": {"{{event.tax}}"}
}
}
]
}
]
}
]
}
}Advanced Patterns
Multi-Step Workflow with Progress Tracking
json
{
"id": "multiStepWorkflow",
"initialPage": "step1",
"context": {
"progress": {
"currentStep": 1,
"totalSteps": 4,
"completedSteps": []
},
"formData": {
"step1": {},
"step2": {},
"step3": {},
"step4": {}
}
},
"services": {
"saveProgress": {
"type": "rest",
"url": "/api/save-progress",
"method": "POST",
"body": {
"step": {"{{context:progress.currentStep}}"},
"data": {"{{context:formData}}"}
}
}
},
"lifecycle": {
"onPageChange": [
{
"type": "assignContext",
"assign": {
"progress.currentStep": {"{{event:newPage}}"}
}
},
{
"type": "callApi",
"service": "saveProgress"
}
]
},
"computedFields": {
"progressPercentage": {
"targetPath": "progress.percentage",
"expression": "Math.round((context.progress.completedSteps.length / context.progress.totalSteps) * 100)",
"dependencies": ["progress.completedSteps", "progress.totalSteps"]
}
}
}Conditional Form Sections
json
{
"context": {
"user": {
"accountType": "personal",
"businessInfo": {
"companyName": "",
"taxId": ""
}
}
},
"blocks": [
{
"id": "account-type-selector",
"blockType": "SelectBlock",
"metadata": {
"label": "Account Type",
"contextPath": "user.accountType",
"options": [
{ "label": "Personal", "value": "personal" },
{ "label": "Business", "value": "business" }
]
}
},
{
"id": "business-section",
"blockType": "CardBlock",
"displayConditions": [
{
"matchType": "exact",
"field": "user.accountType",
"value": "business"
}
],
"blocks": [
{
"blockType": "InputBlock",
"metadata": {
"label": "Company Name",
"contextPath": "user.businessInfo.companyName",
"required": true
}
}
]
}
]
}Best Practices
✅ Organization Tips
- • Group related context properties together
- • Use descriptive service names
- • Keep lifecycle hooks focused on single responsibilities
- • Use computed fields for simple calculations
⚠️ Performance Considerations
- • Always use
watcharrays in lifecycle hooks - • Avoid deep nesting in context paths
- • Cache expensive API calls when possible
- • Use computed fields instead of repeated calculations
🔧 Debugging Strategies
- • Test services independently before integration
- • Use console logging in custom functions
- • Validate context paths exist before using them
- • Handle API errors gracefully with fallbacks
Complete Working Example
Here's a complete form configuration that demonstrates all features working together:
🎯 View Complete Example
Check out the comprehensive example that shows all features in action:
View Complete Form Example