Custom field bindings

Custom bound fields simplify connecting UI controls to Appframe data objects. This guide builds a BoundNumberInput with useField, demonstrates validation, and shows how to commit changes.

Building a number input

Define a reusable NumberInput component that wraps MUI's TextField, handles step buttons, and exposes value and onChange props.

export type NumberInputProps = TextFieldProps & {
	step?: number;
	value: number | null;
	onChange: (value: number | null) => void;
};

Binding with useField

export function BoundNumberInput({ field, ...props }: { field: string } & Omit<NumberInputProps, "value" | "onChange">) {
	let { value, onChange, onKeyDown, error } = useField<number>(field);

	return (
		<NumberInput
		{...props}
		value={value}
		onChange={(v) => onChange({ target: { value: v } })}
		onKeyDown={onKeyDown}
		error={!!error}
		helperText={error}
		/>
	);
}

Validation

useField attempts to coerce the input to the field's type. Invalid values set the error string so the component can display feedback. Pressing Escape cancels edits via the returned onKeyDown handler.

Commit operations

The hook writes values into the bound data object. Call dataObject.endEdit() to persist changes to the server. In accounting-products, the product page commits pending changes before loading a new record:

Example usage

The timekeeping-timereg app uses the component in its timesheet form:

Last updated

Was this helpful?