Skip to main content

FormApi

State and operations for a form, including typed children and submission.

Import

import type { FormApi } from '@ngblocks/form-nodes';

When to use it

Use for form API operations such as submission without exposing direct child-name collisions. The form's callable $api is described by CallableNodeApi<FormApi<...>>.

Declaration

type FormApi<TNodes extends Nodes, TParent extends AnyNode = AnyNode> = {
nodeType(): 'form';
onValueChange(callback: (value: FormValue<TNodes>, node: FormNode<TNodes, TParent>) => void, options?: {
injector?: Injector;
debounce?: number;
}): () => void;
readonly children: FormChildren<TNodes, TParent> & Readonly<Record<string, DynamicNode>>;
forEachChild(callback: (child: keyof TNodes extends never ? DynamicNode : FormChildren<TNodes, TParent>[keyof TNodes], key: string) => void, options?: {
includeDynamic?: false;
}): void;
forEachChild(callback: (child: DynamicNode, key: string) => void, options: {
includeDynamic?: boolean;
}): void;
get(key: string): DynamicNode | undefined;
add<TKey extends string, TDefinition>(key: TKey extends keyof TNodes | '$api' ? never : TKey, definition: ObjectNodeDefinitionInput<TDefinition>): AddedNode<TDefinition, FormNode<TNodes, TParent>>;
add<TDefinitions extends ObjectNodeDefinitions>(definitions: TDefinitions & ObjectNodeDefinitionInputs<TDefinitions> & Partial<Record<keyof TNodes | '$api', never>>): {
readonly [TKey in keyof TDefinitions]: AddedNode<TDefinitions[TKey], FormNode<TNodes, TParent>>;
};
remove(key: string): DynamicNode | undefined;
form: Signal<FormNode<TNodes, TParent>>;
root: Signal<FormRoot<TNodes, TParent>>;
parent: Signal<TParent | null>;
path: Signal<readonly string[]>;
keyInParent: Signal<NodeKeyInParent<TParent>>;
value: NodeValueSignal<{
[K in keyof TNodes]: NodeValue<TNodes[K]>;
}, FormSet<TNodes>>;
asReadonly(): Signal<FormValue<TNodes>>;
set(value: FormSet<TNodes>): void;
update(updater: (value: FormValue<TNodes>) => FormSet<TNodes>): void;
patch(value: FormPatch<TNodes>): void;
reset(...args: [
] | [
value: FormSet<TNodes>
]): void;
resetToInitial(): void;
validators: Signal<Validators<FormValue<TNodes>>> & {
(options: {
resolve?: boolean;
}): Validators<FormValue<TNodes>>;
};
setValidators(validators: ValidatorSource<FormValue<TNodes>, FormNode<TNodes, TParent>>): void;
errors: NodeErrorsSignal<FormNode<TNodes, TParent>>;
allErrors: Signal<readonly ValidationErrorWithTargetNode<AnyNode>[]>;
valid: Signal<boolean>;
invalid: Signal<boolean>;
getError<TKind extends keyof ValidationErrorMap>(kind: TKind): (ValidationErrorWithTargetNode<FormNode<TNodes, TParent>> & ValidationErrorMap[TKind]) | undefined;
getError<TKind extends keyof ValidationErrorMap | (string & {})>(kind: TKind): (ValidationErrorWithTargetNode<FormNode<TNodes, TParent>> & CustomValidationError<TKind>) | undefined;
hasError(kind: keyof ValidationErrorMap | (string & {})): boolean;
hasValidator(validator: (context: any) => unknown, options?: {
resolve?: boolean;
}): boolean;
required: Signal<boolean>;
pending: Signal<boolean>;
submitted: Signal<boolean>;
submitting: Signal<boolean>;
submit(): Promise<boolean>;
debouncing: Signal<boolean>;
flush(): void;
focus(options?: FocusOptions): void;
validationStatus: Signal<ValidationStatus>;
touched: Signal<boolean>;
untouched: Signal<boolean>;
markAsTouched(options?: {
skipDescendants?: boolean;
}): void;
markAsUntouched(): void;
dirty: Signal<boolean>;
pristine: Signal<boolean>;
markAsDirty(): void;
markAsPristine(): void;
disabled: Signal<boolean>;
disabledReasons: Signal<readonly DisabledReason[]>;
enabled: Signal<boolean>;
disable(message?: string): void;
enable(): void;
readonly: Signal<boolean>;
writable: Signal<boolean>;
markAsReadonly(): void;
markAsWritable(): void;
hidden: Signal<boolean>;
visible: Signal<boolean>;
hide(): void;
show(): void;
};

Type parameters

ParameterConstraintDefault
TNodesNodesRequired
TParentAnyNodeAnyNode

Declared members

The declaration above also includes inherited contracts and overloads where applicable.

MemberMeaning
nodeTypeReturns the concrete primitive represented by this node.
onValueChangeSubscribes to future exposed value changes and returns an idempotent cancellation function. Runs untracked, respects equality and control debounce, and skips initial values. Multiple listeners coexist with the construction callback; subscriptions are not cloned. The explicit injector, otherwise the registration context, owns the listener. The node's current injector also ends the subscription on destruction and acts as the fallback owner. Binding and ancestor ownership follow the node when it is rebound or detached. Without an injector, observation still works and can be canceled manually. A positive debounce delays only this callback until that many milliseconds without another change. Omitted or zero stays synchronous. Cancellation drops pending delivery. Node values, validation, and interaction state are unaffected by the subscription delay.
childrenReadonly runtime child map. Declared properties retain exact node types; arbitrary keys use DynamicNode.
forEachChildDynamically added nodes are excluded by default. Pass { includeDynamic: true } to visit them.
getReturns a child by runtime key, or undefined when no current child has that key.
addAdds one child node at runtime and returns that live node with its exact inferred type.
removeDetaches and returns a dynamically added child, or undefined when the key is absent. Initially declared children are fixed and cannot be removed.
formThis explicit form workflow. Descendants resolve this form until another nested form begins. Unlike root(), this signal deliberately does not cross the form's workflow boundary.
rootComplete structural root containing this form. A root or detached form returns itself. A nested form therefore returns itself from form() and its outermost ancestor from root().
parentImmediate structural parent of this form, or null when it is a root or has been detached.
pathProperty and array-index segments from the complete root to this form. Root forms use [].
keyInParentProperty or array index under which this form is stored, or null when it is a root form.
valueExposed aggregate of public child values. The equal option may retain a previous snapshot.
asReadonlyReturns a stable, live readonly signal of the exposed value, with no node operations. Preserves configured equality and committed-value reads; pending control input remains pending. This does not mark the node readonly or prevent deep mutation of object values. The node and its $api return the same signal, and the method is safe to extract.
setAssigns a complete form value immediately without marking the form or its descendants dirty.
updateComputes and sets the complete form value from its current value without marking nodes dirty.
patchAssigns supplied child branches immediately; arrays reconcile complete values like set(). Omitted branches remain unchanged and unknown runtime keys are ignored.
resetRecursively clears touched and dirty state and cancels pending control input. Passing a complete value also assigns it; omitting the value preserves all current committed values.
resetToInitialRestores the initial values of the current form/group subtree and resets interaction state.
validatorsCurrent normalized validators assigned directly to this form, in declaration order.
setValidatorsReplaces validators owned by this form and immediately validates its current aggregate value.
errorsA signal containing the validation errors of this form node itself, excluding its descendants.
allErrorsA signal containing the validation errors of this form node and its descendants.
validWhether this form and every descendant have completed validation without errors.
invalidWhether this form or any descendant currently contributes a validation error.
getErrorReturns the first validation error belonging directly to this form and matching kind. Suggests registered error kinds while accepting any custom string.
hasErrorWhether this node's own errors contain the given kind. Does not search descendants. Suggests registered error kinds while accepting any custom string.
hasValidatorWhether the same validator function is directly registered on this node, including async validators. By default, does not run validators. Set resolve to true to inspect resolved leaf references.
requiredWhether active validation metadata marks this form itself as required.
pendingWhether asynchronous validation is active on this form or any descendant.
submittedWhether submit() has been called on this form since its last reset.
submittingWhether this form or an ancestor form is currently running its submission action.
submitMarks and flushes the subtree, then runs the configured submission action when validation allows it. Clears previous subtree submission errors before checking local validation. Resolves to false for returned errors, blocked/concurrent attempts, or a missing action. Errors target this form or its captured descendants; edits/reset/detachment discard stale errors. Thrown or rejected action failures propagate without becoming validation errors.
debouncingWhether any descendant field currently has a pending control-value debounce.
flushImmediately commits every pending control value in this form's subtree.
focusFocuses the first bound UI control in this form's subtree, in DOM order.
validationStatusAggregated validation phase for this form subtree: 'valid', 'invalid', or 'unknown'.
touchedWhether this form or any descendant has been marked touched.
untouchedLogical inverse of touched().
markAsTouchedMarks this form and, by default, every interactive descendant as touched and commits their pending control values for every debounce strategy.
markAsUntouchedClears this node's own touched marker without changing descendant markers or values. An interactive touched descendant can keep an aggregate touched() true. Use reset() to clear interaction state throughout the subtree.
dirtyWhether this form currently reports user-modified state.
pristineLogical inverse of dirty().
markAsDirtyMarks this form's own state dirty, making dirty() true and pristine() false while it is interactive.
markAsPristineClears this form's own dirty state. pristine() becomes true and dirty() false only when no contributing descendant remains dirty.
disabledWhether this form is effectively disabled by its own state or an ancestor reason.
disabledReasonsActive inherited and local causes of this form's disabled state.
enabledLogical inverse of disabled().
disableDisables this form subtree, optionally recording a user-facing reason. Sets disabled() to true and enabled() to false on this form and its descendants.
enableClears local disabled state, including a static initial disabled option. Continuing reactive conditions and inherited reasons remain effective, so enabled() may stay false.
readonlyWhether this form is effectively readonly through its own state or an ancestor.
writableLogical inverse of readonly().
markAsReadonlyMarks this form subtree readonly, making readonly() true and writable() false throughout it.
markAsWritableClears local readonly state, including a static initial readonly option. Reactive conditions and ancestor readonly state can still prevent the node from becoming writable.
hiddenWhether this form is effectively hidden through its own state or an ancestor.
visibleLogical inverse of hidden().
hideHides this form subtree, making hidden() true and visible() false throughout it.
showClears local hidden state, including a static initial hidden option. Reactive conditions and ancestor hidden state can still keep the node hidden.