Skip to content

createFormView

const view = createFormView(options);

Import createFormView from mlform/view. It composes runtime state, layout, navigation, and presenter descriptors without mounting DOM. Primitive renderer and design system options stay on mountForm().

Required:

  • schema
  • transport

Common optional inputs:

  • layout
  • plugins
  • registry
  • initialValues
  • validators
  • hooks
  • inactiveFieldPolicy
  • listenerErrorPolicy
  • onListenerError

Use plugins for custom field and report kinds. A plugin carries its schema definition, presenter, and optional runtime behaviors through one interface; direct registry options remain available for low-level integrations.

interface FormViewController {
form: FormController;
reportFetchMode: ReportFetchMode;
engineRegistry: Registry;
descriptorRegistry: PrimitiveDescriptorRegistry;
navigation: FormViewNavigationController;
state: FormViewState;
getSnapshot(): FormViewSnapshot;
getNodeById(id: string): ResolvedFormLayoutNode | undefined;
getField(id: string): FormViewFieldItem | undefined;
getReport(id: string): FormViewReportItem | undefined;
getVisibleFields(): FormViewFieldItem[];
getVisibleReports(): FormViewReportItem[];
validate(): Promise<FormValidationResult>;
submit(options?: SubmitOptions): Promise<SubmitResult>;
submitPipeline(options?: SubmitOptions): Promise<PipelineResult>;
reset(): void;
dispose(): void;
subscribe(listener): () => void;
}

getSnapshot() returns:

  • form
  • layout
  • fields
  • reports
  • wizard
  • tabs
  • disclosure

wizard is null unless layout.kind === "wizard". tabs is null unless layout.kind === "tabs". disclosure is null when the active layout contains no disclosure sections.

Each field item contains:

  • id
  • kind
  • config
  • controller
  • state
  • descriptor
  • stepId
  • tabId
  • sectionId (the innermost section, if any)
  • sectionIds (all enclosing sections, outermost first)
  • visibleInLayout

The report collection follows the same pattern.

  • in a wizard, validates the current step before advancing
  • in tabs, advances without validation
  • returns false when validation fails or movement is not available
  • a valid final wizard step returns true without moving past the end
  • never validates
  • moves back one wizard step or tab
  • returns whether navigation changed
  • activates a tab directly
  • allows free backward wizard navigation
  • validates wizard steps incrementally when moving forward
  • returns false for stacked and split layouts
  • throws when the current wizard or tabs layout does not contain id
  • returns the currently active layout nodes after wizard, tab, and disclosure state is applied
  • navigation.disclosure.toggle(sectionId) opens or closes one section
  • open(sectionId) and close(sectionId) are explicit variants
  • openAll() and closeAll() manage every section in the layout
  • section controls work inside stacked, split, wizard, and tabs layouts
  • an unknown section id throws

Use subscribe() for host rendering:

const unsubscribe = view.subscribe((snapshot) => {
render(snapshot);
});

Typical host pattern:

  1. create the view once
  2. render initial snapshot
  3. subscribe
  4. on teardown, unsubscribe and call view.dispose()

createFormView() does not resolve primitive renderers, attach stylesheets, or mutate DOM. Use:

  • mountForm() for built-in one-page DOM
  • mountForm() for built-in wizard DOM
  • mountForm() for built-in tabs DOM
  • mountForm() for built-in disclosure DOM
  • attachDesignSystem() yourself when your custom host needs it
  • createPrimitiveAdapter() from mlform/kit when your host owns the layout but uses built-in controls