MultiForm Custom API
Control your form like a boss — programmatic API to read/write data, manage steps, submit forms, and trigger actions from your JavaScript.
Multistep Form provides a public API for programmatic form control from external JavaScript code. This allows you to integrate the form with custom logic and dynamically modify form data and state.
API Access
The API is available through the global object window.FpsForm2_API[formId], where formId is the short component ID from form settings (data.params.comp_ID).
// Get form API by ID
const formAPI = window.FpsForm2_API['myFormId'];Mounting and Timing Issues
Important: The form API is registered during component mount. If your script runs before the form is mounted, the API will be undefined.
Recommended Solution: waitForFormAPI Helper
waitForFormAPI HelperUse this helper function to wait for the API to become available:
function waitForFormAPI(formId, callback, timeout = 5000) {
const start = Date.now();
const check = () => {
if (window.FpsForm2_API && window.FpsForm2_API[formId]) {
callback(window.FpsForm2_API[formId]);
} else if (Date.now() - start < timeout) {
setTimeout(check, 100);
} else {
console.error('Form API not available after ' + timeout + 'ms. Check form ID.');
}
};
check();
}
// Usage
waitForFormAPI('myFormId', (formAPI) => {
console.log('✅ API ready!');
// Your code here
formAPI.editModel('firstName', 'John');
});Alternative: Use setTimeout with 500-1000ms delay if you know the form will mount quickly.
API Methods
Getting Data
getModel()
Returns the current form model (flat object with fields).
const model = formAPI.getModel();
console.log(model.firstName); // get field valuegetExtendedModel()
Returns the extended form model (with nested objects for links/arrayLinks).
const extModel = formAPI.getExtendedModel();
console.log(extModel.company); // may be an object with structuregetState()
Returns the current form state (step, popup, custom fields).
const state = formAPI.getState();
console.log(state.step); // current step
console.log(state.popup); // opened popupgetOriginalModel()
Returns the original model (before user changes).
const original = formAPI.getOriginalModel();Modifying Model
editModel(field, value)
Changes a single field in the model. Supports nested paths with dot notation.
// Simple field
formAPI.editModel('firstName', 'John');
// Nested field
formAPI.editModel('address.city', 'Moscow');Important: Automatically updates both model and extendedModel.
setModel(newModelData)
Merges the provided object with the current model (merge, not replace).
formAPI.setModel({
firstName: 'John',
lastName: 'Doe',
age: 30
});replaceModel(newModel)
Completely replaces the model (replace, not merge).
formAPI.replaceModel({
firstName: 'Jane',
lastName: 'Smith'
});Modifying State
editState(field, value)
Changes a single field in state. Supports nested paths.
// Change step
formAPI.editState('step', 'step2');
// Open popup
formAPI.editState('popup', 'confirmPopup');
// Close popup
formAPI.editState('popup', '');
// Custom field
formAPI.editState('myCustomFlag', true);setState(newStateData)
Merges the provided object with the current state.
formAPI.setState({
step: 'step3',
customData: { foo: 'bar' }
});replaceState(newState)
Completely replaces the state (use with caution, may break form logic).
formAPI.replaceState({
step: 'newStep',
popup: ''
});Programmatic Submit
submit(options)
Programmatically triggers form submission.
// Simple submit
formAPI.submit();
// With options
formAPI.submit({
submitKeepModel: true, // don't reset model after submit
targetStep: 'step3', // go to step3 after success
resetModel: false, // don't reset model
finish: (result) => { // callback after completion
console.log('Submit completed', result);
}
});Options:
finish— callback after completionsubmitKeepModel— keep model after submit (default:true)targetStep— step to navigate to after successautoSubmit— autosubmit flag (default:false)submitMapping— field mapping for submitnewData— additional data{ model: {...}, state: {...} }resetModel— reset model after submit
Calling Actions
callAction(actionIdOrName, callback)
Programmatically triggers a form action by ID or name.
// Call action by ID
formAPI.callAction('action_id_123', (success) => {
if (success) {
console.log('Action completed!');
}
});
// Call action by name
formAPI.callAction('Submit Order', (success) => {
console.log('Done:', success);
});Supported action types:
state— updates state/model via stateMappingendpoint— calls API endpoint with mapping
Limitations:
Does not support
link,modal, or complex action typesFor those, use direct API methods (
editState,submit, etc.)
Note: For simpler cases, consider using the data-action mechanism in HTML elements. It's easier to implement and doesn't require JavaScript. See the data-action documentation for details.
Utilities
refreshOptions()
Refreshes dynamic options in form fields (for select/autocomplete with endpoints).
formAPI.refreshOptions();Usage Examples
1. Validation and Data Modification
waitForFormAPI('registrationForm', (formAPI) => {
// Get current data
const model = formAPI.getModel();
// Validation
if (!model.email.includes('@')) {
alert('Invalid email');
formAPI.editModel('email', '');
}
// Auto-fill
if (model.country === 'Russia') {
formAPI.editModel('currency', 'RUB');
}
});2. Step Management
waitForFormAPI('wizardForm', (formAPI) => {
// Next step
function nextStep() {
const state = formAPI.getState();
const steps = ['step1', 'step2', 'step3', 'final'];
const currentIndex = steps.indexOf(state.step);
if (currentIndex < steps.length - 1) {
formAPI.editState('step', steps[currentIndex + 1]);
}
}
// Previous step
function prevStep() {
const state = formAPI.getState();
const steps = ['step1', 'step2', 'step3', 'final'];
const currentIndex = steps.indexOf(state.step);
if (currentIndex > 0) {
formAPI.editState('step', steps[currentIndex - 1]);
}
}
// Attach to buttons
document.getElementById('nextBtn').addEventListener('click', nextStep);
document.getElementById('prevBtn').addEventListener('click', prevStep);
});3. Syncing with External Data
waitForFormAPI('profileForm', (formAPI) => {
// Load data from API
async function loadUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
// Update form
formAPI.setModel({
firstName: userData.first_name,
lastName: userData.last_name,
email: userData.email
});
}
loadUserData('123');
// Subscribe to changes (polling)
setInterval(() => {
const model = formAPI.getModel();
console.log('Current data:', model);
}, 5000);
});4. Custom Triggers and Calculations
waitForFormAPI('orderForm', (formAPI) => {
// Recalculate total when quantity changes
function recalculateTotal() {
const model = formAPI.getModel();
const quantity = parseInt(model.quantity) || 0;
const price = parseFloat(model.price) || 0;
const total = quantity * price;
formAPI.editModel('total', total);
}
// Monitor changes (you can also use input events)
document.getElementById('quantityInput').addEventListener('change', recalculateTotal);
document.getElementById('priceInput').addEventListener('change', recalculateTotal);
});5. Programmatically Opening Popups
waitForFormAPI('mainForm', (formAPI) => {
// Open confirmation popup
function showConfirmation() {
formAPI.editState('popup', 'confirmationPopup');
}
// Close all popups
function closePopups() {
formAPI.editState('popup', '');
}
document.getElementById('confirmBtn').addEventListener('click', showConfirmation);
document.getElementById('closeBtn').addEventListener('click', closePopups);
});6. Calling Actions Programmatically
waitForFormAPI('checkoutForm', (formAPI) => {
// Trigger an action
document.getElementById('processBtn').addEventListener('click', () => {
formAPI.callAction('Process Payment', (success) => {
if (success) {
alert('Payment processed!');
} else {
alert('Payment failed');
}
});
});
});Important Notes
Form ID is required — API is only available if the FpsForm2 component has a short component ID in
data.params.comp_ID.Timing is critical — Always use
waitForFormAPIhelper orsetTimeoutto ensure the form is mounted before accessing the API.Race conditions — Use
setModel()for updating multiple fields at once, NOT multipleeditModel()calls. Form inputs can override changes between sequential updates, causing data loss. Add 100-150mssetTimeoutdelay if inputs still override your changes.Model vs ExtendedModel — When modifying through the API, both objects are automatically synchronized.
Autosubmit — If autosubmit on model change is enabled, programmatic changes via API will also trigger it.
State persistence — If state saving to field (saveStateTo) is enabled, programmatic state changes will also be saved on submit.
Refs — API uses refs to get current values, so it always returns fresh data.
Cleanup — API is automatically removed from
window.FpsForm2_APIwhen component unmounts.Actions vs data-action — For simple button actions, consider using the
data-actionHTML attribute mechanism instead of programmaticcallAction. It's simpler and requires no JavaScript code.
Complete Working Example
<!-- Simple control with two inputs and submit button -->
<div style="padding: 20px; border: 1px solid #ccc; border-radius: 8px; max-width: 400px;">
<!-- Model field -->
<div style="margin-bottom: 15px;">
<label style="display: block; margin-bottom: 5px; font-weight: bold;">
Model Field:
</label>
<input
type="text"
id="modelField"
placeholder="Enter value for model"
style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"
/>
</div>
<!-- State field -->
<div style="margin-bottom: 15px;">
<label style="display: block; margin-bottom: 5px; font-weight: bold;">
State Field:
</label>
<input
type="text"
id="stateField"
placeholder="Enter value for state"
style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"
/>
</div>
<!-- Submit button -->
<button
id="submitBtn"
style="width: 100%; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px;"
>
Submit Form
</button>
</div>
<script>
// ============= SETTINGS - CHANGE THESE =============
const FORM_ID = 'myFormId'; // Form component ID
const MODEL_FIELD_NAME = 'firstName'; // Model field name
const STATE_FIELD_NAME = 'customState'; // State field name
// ===================================================
// Helper to wait for API
function waitForFormAPI(formId, callback, timeout = 5000) {
const start = Date.now();
const check = () => {
if (window.FpsForm2_API && window.FpsForm2_API[formId]) {
callback(window.FpsForm2_API[formId]);
} else if (Date.now() - start < timeout) {
setTimeout(check, 100);
} else {
console.error('Form API not available after ' + timeout + 'ms');
}
};
check();
}
// Wait for API to be ready
waitForFormAPI(FORM_ID, (formAPI) => {
console.log('✅ Form API ready!', formAPI);
// Model field - update model on input
document.getElementById('modelField').addEventListener('input', (e) => {
const value = e.target.value;
formAPI.editModel(MODEL_FIELD_NAME, value);
console.log(`Model updated: ${MODEL_FIELD_NAME} = ${value}`);
});
// State field - update state on input
document.getElementById('stateField').addEventListener('input', (e) => {
const value = e.target.value;
formAPI.editState(STATE_FIELD_NAME, value);
console.log(`State updated: ${STATE_FIELD_NAME} = ${value}`);
});
// Submit button
document.getElementById('submitBtn').addEventListener('click', () => {
console.log('Submitting form...');
formAPI.submit({
finish: (result) => {
console.log('Submit completed!', result);
alert('Form submitted successfully!');
}
});
});
});
</script>AI Prompt Template
Use this prompt template with any AI assistant (ChatGPT, Claude, etc.) to generate custom code for your form:
I'm working with Multistep Form component that has a public JavaScript API available at window.FpsForm2_API[formId].
Available API methods:
- getModel() - returns current form model (flat object)
- getExtendedModel() - returns extended model (with nested objects)
- getState() - returns current state (step, popup, custom fields)
- getOriginalModel() - returns original model before changes
- editModel(field, value) - change single field in model
- setModel(newModelData) - merge object with current model (PREFERRED for multiple fields)
- replaceModel(newModel) - completely replace model
- editState(field, value) - change single field in state
- setState(newStateData) - merge object with current state
- replaceState(newState) - completely replace state
- submit(options) - programmatically submit form
- callAction(actionIdOrName, callback) - trigger form action
- refreshOptions() - refresh dynamic field options
Important: The form API becomes available after component mount. Always use this helper:
function waitForFormAPI(formId, callback, timeout = 5000) {
const start = Date.now();
const check = () => {
if (window.FpsForm2_API && window.FpsForm2_API[formId]) {
callback(window.FpsForm2_API[formId]);
} else if (Date.now() - start < timeout) {
setTimeout(check, 100);
} else {
console.error('Form API not available after ' + timeout + 'ms');
}
};
check();
}
CRITICAL: When updating multiple fields at once, ALWAYS use setModel() instead of multiple editModel() calls to avoid race conditions with form inputs:
// ❌ BAD - Can lose data due to race conditions:
formAPI.editModel('firstName', 'John');
formAPI.editModel('lastName', 'Doe');
formAPI.editModel('email', '[email protected]');
// ✅ GOOD - Atomic update, no race conditions:
formAPI.setModel({
firstName: 'John',
lastName: 'Doe',
email: '[email protected]'
});
// Optional: Add 100-150ms delay if inputs still override your changes:
setTimeout(() => {
formAPI.setModel({ /* your data */ });
}, 150);
**TASK**: [DESCRIBE YOUR TASK HERE]
Example tasks:
- Calculate total price when quantity or price fields change
- Validate email format and show error
- Auto-fill city based on selected country
- Navigate to next step when button is clicked
- Open popup when certain condition is met
- Load data from external API and populate form
- Clear specific fields when user clicks a button
- Call a form action programmatically
- Parse address from geocoding API and populate multiple address fields
Please generate JavaScript code that:
1. Uses waitForFormAPI helper to wait for the API
2. Gets the form API using the form ID
3. Uses setModel() for updating multiple fields (NOT multiple editModel calls)
4. Includes error handling
5. Has clear comments explaining what the code does
6. Can be easily integrated into HTML via <script> tagPrompt Usage Examples:
Example 1: Calculate total
**TASK**: I have a form with fields 'quantity', 'price', and 'total'. When quantity or price changes, automatically calculate and update the total field.Form ID is 'orderForm'.Example 2: Conditional field population
**TASK**: When user selects country='USA' in the dropdown, automatically set currency='USD' and timezone='America/New_York'.When country='UK', set currency='GBP' and timezone='Europe/London'.Form ID is 'registrationForm'.Example 3: Custom validation
**TASK**: Before allowing user to proceed to 'step2', check that:
- email contains '@' symbol
- phone number is exactly 10 digits
- age is between 18 and 100
If validation fails, show alert and prevent step change.
Form ID is 'wizardForm'.Example 4: External data integration
**TASK**: When form loads, fetch user data from '/api/users/123' and populate firstName, lastName, and email fields.Show loading indicator while fetching.Form ID is 'profileForm'.Example 5: Action trigger
**TASK**: Add a button that triggers the form action named 'Send Email'when clicked, and shows an alert when the action completes.Form ID is 'contactForm'.Pro tip: Just copy the prompt template, replace [DESCRIBE YOUR TASK HERE] with your specific requirement, and paste it into any AI chat. You'll get ready-to-use code! 🚀
Last updated
Was this helpful?