Form Generator
FormGenerator is a client-side orchestrator for multi-step and multi-section forms.
It renders questions from a declarative configuration (usually returned by a backend API), manages
step navigation, validation, dependent-question visibility, and answer collection.
Source: src/client/components/FormGenerator/index.js
How it works
- Instantiate โ Pass a DOM container and a config object to
new FormGenerator(container, config). The constructor extendsFormComponentand immediately renders markup into the container. - Render steps โ One wrapper (
.lta-form-generator__step) is created per step. Questions for each step are placed in.lta-form-generator__question-containerplaceholders. - Mount question components โ Each question's
questionTypemaps to a dedicated component (e.g.RadioComponent,SchoolFinder,DateComponent). The component instance is stored onquestion.ref. - Collect answers โ When a question changes,
updateAnswers()merges the answer intoformGenerator.answerskeyed by[step][questionId]. - Navigate โ The Next button validates the current step via
stepValidator(), then advancescurrentStep. On the final step, Next becomes Submit and callssubmitForm(). - Dependent questions โ Radio/checkbox options can declare
dependentQuestion: [ids]. When the selection changes,beforeDepChangeHandler()hides previous dependents, clears their answers, and shows new ones.
Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Page / Modal (container element) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ FormGenerator โ โ
โ โ โโโ TopBackButton / ExitButton โ โ
โ โ โโโ Stepper (optional progress bar) โ โ
โ โ โโโ Step 1..N (.lta-form-generator__step) โ โ
โ โ โ โโโ Question containers โ โ
โ โ โ โโโ RadioComponent, Select, โฆ โ โ
โ โ โโโ Next / Submit button โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโForm types
| Type | Constant | Behaviour |
|---|---|---|
| Step form | FormTypes.StepForm | One step visible at a time. Next advances; only the final step shows the submit label. |
| Section form | FormTypes.SectionForm | All steps rendered on one page with section titles. Submit button hidden until the last
section unless submitButton.alwaysVisible is set. |
Config options
| Option | Description |
|---|---|
questions | Array of question objects (required) |
totalSteps | Number of steps (default: 3) |
formType | stepForm or sectionForm |
themeColor | CSS variable name for stepper accent (e.g. color-lta-blue) |
isHideProgress | Hide the stepper when true (default: hidden) |
isBackButtonEnabled | Show top back button |
showBottomBackButton | Show bottom back button below Next |
isExitButtonEnabled | Show exit button with exitButtonHandler |
backButtonHandlerFirstStep | Custom handler when back is pressed on step 1 (default: history.back()) |
hasSeparator | Add separators between questions / sections |
stepVisibility | Boolean array controlling stepper visibility per step |
conditionalStepsVisiblity | Hide stepper when a step has no visible questions |
mainPageElement | Element whose background class updates per step |
submitButton | { title, dataTarget, alwaysVisible } |
saveAddress | Passed to VenueAddressSearch questions (default: true) |
radioAnswerField | Field on radio options used as the answer value (default: label) |
titlePattern | Heading layout pattern passed to question components |
wrapperClasses | Per-step CSS classes array |
Public API
| Method / property | Description |
|---|---|
onChange(callback) | Fires when any answer changes |
onStepChange(callback) | Fires after navigating to a new step |
onSubmit(callback) | Fires on final-step submit with (answers, value) |
formValidation() | Async โ validates all steps; sets value on success |
stepValidator(step) | Async โ validates a single step |
enableSubmitButton() | Re-enables submit after async work completes |
reRenderForm(updatedStepData) | Re-render a question after server-side validation errors |
getAnswerByQuestionType(type) | Read an answer value by questionType |
unload() | Remove all question components and clear the container |
answers | Current answers object (by step โ question id) |
currentStep | 1-based current step index |
questions | Filtered question array (questions without id are removed) |
Answer structure
// Answers are grouped by step, then by question id:
{
1: {
1: { fieldName: 'addVenue', value: 'Yes', dependentQuestion: [2], ... }
},
2: {
2: { fieldName: 'venue', value: { venueName: '...' }, ... }
}
}
Question types
Each questionType maps to a component in renderQuestion(). Search-type
questions merge extra API config from src/client/components/FormGenerator/data.js.
| questionType | Component |
|---|---|
radio | RadioComponent |
checkbox | CheckboxComponent |
select | SelectComponent |
textArea / textInput | InputField |
dateComponent | DateComponent |
duetDateComponent | DuetDateComponent |
fileUpload | FileUploadComponent |
info | KeyPoints (read-only) |
schoolSearch | SchoolFinder |
universitySearch / nurserySearch / collegeSearch | SchoolFinder (variant config) |
coachSearch | CoachFinder |
parkFinder / coachVenueSearch / activatorVenueSearch | ParkFinder |
venueAddressSearch | VenueAddressSearch |
addressFinder | AddressFinder |
tournamentSearch | TournamentFinder |
checkout | CheckoutComponent |
playerList / sessionPlayerList | PlayerList / SessionPlayersList |
contactNumber / childContactNumber | ContactNumber |
formTable | FormTable |
householdAccountOnboarding / householdMemberList | HouseHoldAccountOnboarding / HouseHoldMemberList |
Question schema
{
id: 1, // required โ unique question id
step: 1, // which step this question belongs to
fieldName: 'addVenue', // key used in API payloads
question: 'Your venues', // main heading
stepperTitle: 'Your venues', // stepper label for this step
instructionText: 'Are you affiliated with a venue?',
errorMessage: 'Please select an option',
questionType: 'radio', // see QuestionTypes
visible: true, // false for dependent questions shown later
isOptional: false,
options: [
{
id: 'yes',
label: 'Yes',
dependentQuestion: [2] // show question id 2 when selected
},
{
id: 'no',
label: 'No',
dependentQuestion: []
}
]
}
Basic usage
import FormGenerator, { FormTypes } from '../../components/FormGenerator';
const container = document.querySelector('.js-my-form');
const formGenerator = new FormGenerator(container, {
questions: stepsData, // array of question objects (often from an API)
totalSteps: 3,
formType: FormTypes.StepForm,
themeColor: 'color-lta-blue',
isBackButtonEnabled: true,
submitButton: {
title: 'Submit',
alwaysVisible: false
}
});
formGenerator.onChange((answers) => {
console.log('Answers updated:', answers);
});
formGenerator.onStepChange((step) => {
console.log('Current step:', step);
});
formGenerator.onSubmit((answers, value) => {
console.log('Form submitted:', answers, value);
});
Dependent questions
Dependent questions start with visible: false. When a parent radio/checkbox option is
selected, its dependentQuestion array lists question id values to show.
Switching options hides previous dependents, recursively clears nested dependents, and removes their
answers from the current step.
Steps with no visible questions are skipped automatically during Next/Back navigation whenconditionalStepsVisiblity is enabled.
Validation
stepValidator(step) iterates visible, non-optional questions on that step and calls each
component's validate(true). Questions with shouldRender() returningfalse are skipped. Optional questions and answers with value: 'N/A' are
also skipped.
The Next button visibility is controlled by nextValidator(), which hides Next while any
visible required question's shouldShowNext() returns true (used by
components that need inline confirmation before advancing).
Live demo
Step form with radio dependent questions and a select field. Open the browser console to seeonChange and onSubmit output.
Event: none
Used in
Coaches onboarding, school onboarding, checkout, accreditation renewal, officials licence renewal,
activators portal, competition eligibility, tennis leader resource form, and other multi-step flows
that fetch Steps from an API and mount FormGenerator into a page or modal container.
For modal-based flows, useForm Modaland mount FormGenerator into contentEl.